From b0378c3d99fcedcf873a0cd006866d280822753a Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Tue, 18 Feb 2020 09:11:31 +0100 Subject: [PATCH 01/13] initial AHT10 support test --- tasmota/my_user_config.h | 1 + tasmota/support_features.ino | 4 +- tasmota/xsns_64_aht10.ino | 184 +++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 tasmota/xsns_64_aht10.ino diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 67ab1cf1c..0652f6ebb 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -486,6 +486,7 @@ // #define USE_HIH6 // [I2cDriver36] Enable Honeywell HIH Humidity and Temperature sensor (I2C address 0x27) (+0k6) // #define USE_DHT12 // [I2cDriver41] Enable DHT12 humidity and temperature sensor (I2C address 0x5C) (+0k7 code) // #define USE_DS1624 // [I2cDriver42] Enable DS1624, DS1621 temperature sensor (I2C addresses 0x48 - 0x4F) (+1k2 code) +// #define USE_AHT10 // [I2cDriver43] Enable AHT10 humidity and temperature sensor (I2C address 0x58) (+0k8 code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 2dc25b8e5..4fa9dcae8 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -513,7 +513,9 @@ void GetFeatures(void) #ifdef USE_LE01MR feature5 |= 0x08000000; // xnrg_13_fif_le01mr.ino #endif - +#ifdef USE_AHT10 + feature5 |= 0x10000000; // xsns_64_dht12.ino +#endif // feature5 |= 0x10000000; // feature5 |= 0x20000000; // feature5 |= 0x40000000; diff --git a/tasmota/xsns_64_aht10.ino b/tasmota/xsns_64_aht10.ino new file mode 100644 index 000000000..15dbbb5c1 --- /dev/null +++ b/tasmota/xsns_64_aht10.ino @@ -0,0 +1,184 @@ +/* + xsns_64_AHT10.ino - AHT10 I2C temperature and humidity sensor support for Tasmota + + Copyright (C) 2020 M. Wagner + + 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_I2C +#ifdef USE_AHT10 +/*********************************************************************************************\ + * AHT10 - Temperature and Humidity + * + * I2C Address: 0x38 +\*********************************************************************************************/ + +#define XSNS_64 64 +#define XI2C_43 43 // See I2CDEVICES.md + +#define AHT10_ADDR 0x38 + +unsigned char eSensorCalibrateCmd[3] = {0xE1, 0x08, 0x00}; +unsigned char eSensorNormalCmd[3] = {0xA8, 0x00, 0x00}; +unsigned char eSensorMeasureCmd[3] = {0xAC, 0x33, 0x00}; +unsigned char eSensorResetCmd = 0xBA; + +struct AHT10 { + float humidity = NAN; + float temperature = NAN; + uint8_t valid = 0; + uint8_t count = 0; + char name[6] = "AHT10"; +} AHT10; + +bool begin() +{ + Wire.begin(AHT10_ADDR); + Wire.beginTransmission(AHT10_ADDR); + Wire.write(eSensorCalibrateCmd, 3); + Wire.endTransmission(); + delay(500); + + if((readStatus() & 0x68) == 0x08) + return true; + else + { + return false; + } +} + +bool AHT10Read(void) +{ + unsigned long result, temp[6]; + + if (AHT10.valid) { AHT10.valid--; } + + Wire.beginTransmission(AHT10_ADDR); + Wire.write(eSensorMeasureCmd, 3); + Wire.endTransmission(); + delay(100); + + Wire.requestFrom(AHT10_ADDR, 6); + for(unsigned char i = 0; Wire.available() > 0; i++) + { + temp[i] = Wire.read(); + } + + AHT10.humidity = (((temp[1] << 16) | (temp[2] << 8) | temp[3]) >> 4)* 100 / 1048576; + AHT10.temperature = ((200 * (((temp[3] & 0x0F) << 16) | (temp[4] << 8) | temp[5])) / 1048576) - 50; + + if (isnan(AHT10.temperature) || isnan(AHT10.humidity)) { return false; } + + AHT10.valid = SENSOR_MAX_MISS; + return true; +} + +/********************************************************************************************/ +unsigned char readStatus(void) +{ + unsigned char result = 0; + + Wire.requestFrom(AHT10_ADDR, 1); + result = Wire.read(); + return result; +} + +void AHT10Detect(void) +{ + if (I2cActive(AHT10_ADDR)) + { + return; + } + + if (begin()) + { + I2cSetActiveFound(AHT10_ADDR, AHT10.name); + AHT10.count = 1; + } +} + +void AHT10EverySecond(void) +{ + if (uptime &1) { + // AHT10: 55mS + if (!AHT10Read()) { + AddLogMissed(AHT10.name, AHT10.valid); + } + } +} + +void AHT10Show(bool json) +{ + if (AHT10.valid) { + char temperature[33]; + dtostrfd(AHT10.temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(AHT10.humidity, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, AHT10.name, temperature, humidity); +#ifdef USE_DOMOTICZ + if ((0 == tele_period)) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif // USE_DOMOTICZ +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, AHT10.temperature); + KnxSensor(KNX_HUMIDITY, AHT10.humidity); + } +#endif // USE_KNX +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, AHT10.name, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, AHT10.name, humidity); +#endif // USE_WEBSERVER + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns64(uint8_t function) +{ + if (!I2cEnabled(XI2C_43)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + AHT10Detect(); + } + else if (AHT10.count) { + switch (function) { + case FUNC_EVERY_SECOND: + AHT10EverySecond(); + break; + case FUNC_JSON_APPEND: + AHT10Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + AHT10Show(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_AHT10 +#endif // USE_I2C From 583f08672e077e6e434dfc86d42eaef0cfd03ebb Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Tue, 18 Feb 2020 09:14:40 +0100 Subject: [PATCH 02/13] Update I2CDEVICES.md --- I2CDEVICES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/I2CDEVICES.md b/I2CDEVICES.md index c7b42108f..be92d03dc 100644 --- a/I2CDEVICES.md +++ b/I2CDEVICES.md @@ -64,4 +64,4 @@ Index | Define | Driver | Device | Address(es) | Description 41 | USE_DHT12 | xsns_58 | DHT12 | 0x5C | Temperature and humidity sensor 42 | USE_DS1624 | xsns_59 | DS1621 | 0x48 - 0x4F | Temperature sensor 42 | USE_DS1624 | xsns_59 | DS1624 | 0x48 - 0x4F | Temperature sensor - + 43 | USE_AHT10 | xsns_64 | AHT10 | 0x38 | Temperature and humidity sensor From fcc59df10f54ccc30d71d389866d6bf565a13388 Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Tue, 18 Feb 2020 09:18:45 +0100 Subject: [PATCH 03/13] Update support_features.ino --- tasmota/support_features.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 4fa9dcae8..933db0960 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -514,7 +514,7 @@ void GetFeatures(void) feature5 |= 0x08000000; // xnrg_13_fif_le01mr.ino #endif #ifdef USE_AHT10 - feature5 |= 0x10000000; // xsns_64_dht12.ino + feature5 |= 0x10000000; // xsns_64_aht10.ino #endif // feature5 |= 0x10000000; // feature5 |= 0x20000000; From 01fbe69824d549e59422e66e6de5de2c74d8643b Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Tue, 18 Feb 2020 09:22:39 +0100 Subject: [PATCH 04/13] test1 test1 --- tasmota/my_user_config.h | 2 +- tasmota/tasmota.ino.cpp | 77284 +++++++++++++++++++++++++++++++++++++ 2 files changed, 77285 insertions(+), 1 deletion(-) create mode 100644 tasmota/tasmota.ino.cpp diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 0652f6ebb..04395065a 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -486,7 +486,7 @@ // #define USE_HIH6 // [I2cDriver36] Enable Honeywell HIH Humidity and Temperature sensor (I2C address 0x27) (+0k6) // #define USE_DHT12 // [I2cDriver41] Enable DHT12 humidity and temperature sensor (I2C address 0x5C) (+0k7 code) // #define USE_DS1624 // [I2cDriver42] Enable DS1624, DS1621 temperature sensor (I2C addresses 0x48 - 0x4F) (+1k2 code) -// #define USE_AHT10 // [I2cDriver43] Enable AHT10 humidity and temperature sensor (I2C address 0x58) (+0k8 code) + #define USE_AHT10 // [I2cDriver43] Enable AHT10 humidity and temperature sensor (I2C address 0x38) (+0k8 code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 diff --git a/tasmota/tasmota.ino.cpp b/tasmota/tasmota.ino.cpp new file mode 100644 index 000000000..ddd9b7b19 --- /dev/null +++ b/tasmota/tasmota.ino.cpp @@ -0,0 +1,77284 @@ +# 1 "C:\\Users\\martin\\AppData\\Local\\Temp\\tmpeqetuoko" +#include +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota.ino" +# 29 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota.ino" +#include +#include "tasmota_version.h" +#include "tasmota.h" +#include "my_user_config.h" +#ifdef USE_MQTT_TLS + #include +#endif +#include "tasmota_post.h" +#include "i18n.h" +#include "tasmota_template.h" + +#ifdef ARDUINO_ESP8266_RELEASE_2_4_0 +#include "lwip/init.h" +#if LWIP_VERSION_MAJOR != 1 + #error Please use stable lwIP v1.4 +#endif +#endif + + +#include +#include +#include +#include +#ifdef USE_ARDUINO_OTA + #include + #ifndef USE_DISCOVERY + #define USE_DISCOVERY + #endif +#endif +#ifdef USE_DISCOVERY + #include +#endif +#ifdef USE_I2C + #include +#endif +#ifdef USE_SPI + #include +#endif + + +#include "settings.h" + + + + + +WiFiUDP PortUdp; + +unsigned long feature_drv1; +unsigned long feature_drv2; +unsigned long feature_sns1; +unsigned long feature_sns2; +unsigned long feature5; +unsigned long feature6; +unsigned long serial_polling_window = 0; +unsigned long state_second = 0; +unsigned long state_50msecond = 0; +unsigned long state_100msecond = 0; +unsigned long state_250msecond = 0; +unsigned long pulse_timer[MAX_PULSETIMERS] = { 0 }; +unsigned long blink_timer = 0; +unsigned long backlog_delay = 0; +power_t power = 0; +power_t last_power = 0; +power_t blink_power; +power_t blink_mask = 0; +power_t blink_powersave; +power_t latching_power = 0; +power_t rel_inverted = 0; +int serial_in_byte_counter = 0; +int ota_state_flag = 0; +int ota_result = 0; +int restart_flag = 0; +int wifi_state_flag = WIFI_RESTART; +int blinks = 201; +uint32_t uptime = 0; +uint32_t loop_load_avg = 0; +uint32_t global_update = 0; +uint32_t web_log_index = 1; +float global_temperature = 9999; +float global_humidity = 0; +float global_pressure = 0; +uint16_t tele_period = 9999; +uint16_t blink_counter = 0; +uint16_t seriallog_timer = 0; +uint16_t syslog_timer = 0; +int16_t save_data_counter; +RulesBitfield rules_flag; +uint8_t mqtt_cmnd_blocked = 0; +uint8_t mqtt_cmnd_blocked_reset = 0; +uint8_t state_250mS = 0; +uint8_t latching_relay_pulse = 0; +uint8_t sleep; +uint8_t blinkspeed = 1; +uint8_t pin[GPIO_MAX]; +uint8_t active_device = 1; +uint8_t leds_present = 0; +uint8_t led_inverted = 0; +uint8_t led_power = 0; +uint8_t ledlnk_inverted = 0; +uint8_t pwm_inverted = 0; +uint8_t energy_flg = 0; +uint8_t light_flg = 0; +uint8_t light_type = 0; +uint8_t serial_in_byte; +uint8_t ota_retry_counter = OTA_ATTEMPTS; +uint8_t devices_present = 0; +uint8_t seriallog_level; +uint8_t syslog_level; +uint8_t my_module_type; +uint8_t my_adc0; +uint8_t last_source = 0; +uint8_t shutters_present = 0; +uint8_t prepped_loglevel = 0; + +bool serial_local = false; +bool fallback_topic_flag = false; +bool backlog_mutex = false; +bool interlock_mutex = false; +bool stop_flash_rotate = false; +bool blinkstate = false; + +bool pwm_present = false; +bool i2c_flg = false; +bool spi_flg = false; +bool soft_spi_flg = false; +bool ntp_force_sync = false; +bool is_8285 = false; +myio my_module; +gpio_flag my_module_flag; +StateBitfield global_state; +char my_version[33]; +char my_image[33]; +char my_hostname[33]; +char mqtt_client[TOPSZ]; +char mqtt_topic[TOPSZ]; +char serial_in_buffer[INPUT_BUFFER_SIZE]; +char mqtt_data[MESSZ]; +char log_data[LOGSZ]; +char web_log[WEB_LOG_SIZE] = {'\0'}; +#ifdef SUPPORT_IF_STATEMENT + #include + LinkedList backlog; + #define BACKLOG_EMPTY (backlog.size() == 0) +#else + uint8_t backlog_index = 0; + uint8_t backlog_pointer = 0; + String backlog[MAX_BACKLOG]; + #define BACKLOG_EMPTY (backlog_pointer == backlog_index) +#endif +void setup(void); +void BacklogLoop(void); +void loop(void); +uint16_t SendMail(char *buffer); +uint32_t GetRtcSettingsCrc(void); +void RtcSettingsSave(void); +void RtcSettingsLoad(void); +bool RtcSettingsValid(void); +uint32_t GetRtcRebootCrc(void); +void RtcRebootSave(void); +void RtcRebootReset(void); +void RtcRebootLoad(void); +bool RtcRebootValid(void); +void SetFlashModeDout(void); +bool VersionCompatible(void); +void SettingsBufferFree(void); +bool SettingsBufferAlloc(void); +uint16_t GetCfgCrc16(uint8_t *bytes, uint32_t size); +uint16_t GetSettingsCrc(void); +uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size); +uint32_t GetSettingsCrc32(void); +void SettingsSaveAll(void); +void UpdateQuickPowerCycle(bool update); +uint32_t GetSettingsTextLen(void); +bool SettingsUpdateText(uint32_t index, const char* replace_me); +char* SettingsText(uint32_t index); +uint32_t GetSettingsAddress(void); +void SettingsSave(uint8_t rotate); +void SettingsLoad(void); +void EspErase(uint32_t start_sector, uint32_t end_sector); +void SettingsErase(uint8_t type); +void SettingsSdkErase(void); +void SettingsDefault(void); +void SettingsDefaultSet1(void); +void SettingsDefaultSet2(void); +void SettingsResetStd(void); +void SettingsResetDst(void); +void SettingsDefaultWebColor(void); +void SettingsEnableAllI2cDrivers(void); +void SettingsDelta(void); +void OsWatchTicker(void); +void OsWatchInit(void); +void OsWatchLoop(void); +bool OsWatchBlockedLoop(void); +uint32_t ResetReason(void); +String GetResetReason(void); +size_t strchrspn(const char *str1, int character); +char* subStr(char* dest, char* str, const char *delim, int index); +float CharToFloat(const char *str); +int TextToInt(char *str); +char* ulltoa(unsigned long long value, char *str, int radix); +char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween); +char* Uint64toHex(uint64_t value, char *str, uint16_t bits); +char* dtostrfd(double number, unsigned char prec, char *s); +char* Unescape(char* buffer, uint32_t* size); +char* RemoveSpace(char* p); +char* ReplaceCommaWithDot(char* p); +char* LowerCase(char* dest, const char* source); +char* UpperCase(char* dest, const char* source); +char* UpperCase_P(char* dest, const char* source); +char* Trim(char* p); +char* RemoveAllSpaces(char* p); +char* NoAlNumToUnderscore(char* dest, const char* source); +char IndexSeparator(void); +void SetShortcutDefault(void); +uint8_t Shortcut(void); +bool ValidIpAddress(const char* str); +bool ParseIp(uint32_t* addr, const char* str); +uint32_t ParseParameters(uint32_t count, uint32_t *params); +bool NewerVersion(char* version_str); +char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option); +char* GetPowerDevice(char* dest, uint32_t idx, size_t size); +void GetEspHardwareType(void); +String GetDeviceHardware(void); +float ConvertTemp(float c); +float ConvertTempToCelsius(float c); +char TempUnit(void); +float ConvertHumidity(float h); +float ConvertPressure(float p); +String PressureUnit(void); +void ResetGlobalValues(void); +uint32_t SqrtInt(uint32_t num); +uint32_t RoundSqrtInt(uint32_t num); +char* GetTextIndexed(char* destination, size_t destination_size, uint32_t index, const char* haystack); +int GetCommandCode(char* destination, size_t destination_size, const char* needle, const char* haystack); +int GetStateNumber(char *state_text); +String GetSerialConfig(void); +void SetSerialBegin(); +void SetSerialConfig(uint32_t serial_config); +void SetSerialBaudrate(uint32_t baudrate); +void SetSerial(uint32_t baudrate, uint32_t serial_config); +void ClaimSerial(void); +void SerialSendRaw(char *codes); +uint32_t GetHash(const char *buffer, size_t size); +void ShowSource(uint32_t source); +void WebHexCode(uint32_t i, const char* code); +uint32_t WebColor(uint32_t i); +char* ResponseGetTime(uint32_t format, char* time_str); +int Response_P(const char* format, ...); +int ResponseTime_P(const char* format, ...); +int ResponseAppend_P(const char* format, ...); +int ResponseAppendTimeFormat(uint32_t format); +int ResponseAppendTime(void); +int ResponseJsonEnd(void); +int ResponseJsonEndEnd(void); +void DigitalWrite(uint32_t gpio_pin, uint32_t state); +uint8_t ModuleNr(void); +bool ValidTemplateModule(uint32_t index); +bool ValidModule(uint32_t index); +String AnyModuleName(uint32_t index); +String ModuleName(void); +void ModuleGpios(myio *gp); +gpio_flag ModuleFlag(void); +void ModuleDefault(uint32_t module); +void SetModuleType(void); +bool FlashPin(uint32_t pin); +uint8_t ValidPin(uint32_t pin, uint32_t gpio); +bool ValidGPIO(uint32_t pin, uint32_t gpio); +bool ValidAdc(void); +bool GetUsedInModule(uint32_t val, uint8_t *arr); +bool JsonTemplate(const char* dataBuf); +void TemplateJson(void); +inline int32_t TimeDifference(uint32_t prev, uint32_t next); +int32_t TimePassedSince(uint32_t timestamp); +bool TimeReached(uint32_t timer); +void SetNextTimeInterval(unsigned long& timer, const unsigned long step); +int32_t TimePassedSinceUsec(uint32_t timestamp); +bool TimeReachedUsec(uint32_t timer); +bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size); +bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg); +bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg); +bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg); +bool I2cValidRead16LE(uint16_t *data, uint8_t addr, uint8_t reg); +bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg); +bool I2cValidRead24(int32_t *data, uint8_t addr, uint8_t reg); +uint8_t I2cRead8(uint8_t addr, uint8_t reg); +uint16_t I2cRead16(uint8_t addr, uint8_t reg); +int16_t I2cReadS16(uint8_t addr, uint8_t reg); +uint16_t I2cRead16LE(uint8_t addr, uint8_t reg); +int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg); +int32_t I2cRead24(uint8_t addr, uint8_t reg); +bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size); +bool I2cWrite8(uint8_t addr, uint8_t reg, uint16_t val); +bool I2cWrite16(uint8_t addr, uint8_t reg, uint16_t val); +int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len); +int8_t I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len); +void I2cScan(char *devs, unsigned int devs_len); +void I2cSetActiveFound(uint32_t addr, const char *types); +bool I2cActive(uint32_t addr); +bool I2cSetDevice(uint32_t addr); +void SetSeriallog(uint32_t loglevel); +void SetSyslog(uint32_t loglevel); +void GetLog(uint32_t idx, char** entry_pp, size_t* len_p); +void Syslog(void); +void AddLog(uint32_t loglevel); +void AddLog_P(uint32_t loglevel, const char *formatP); +void AddLog_P(uint32_t loglevel, const char *formatP, const char *formatP2); +void PrepLog_P2(uint32_t loglevel, PGM_P formatP, ...); +void AddLog_P2(uint32_t loglevel, PGM_P formatP, ...); +void AddLog_Debug(PGM_P formatP, ...); +void AddLogBuffer(uint32_t loglevel, uint8_t *buffer, uint32_t count); +void AddLogSerial(uint32_t loglevel); +void AddLogMissed(char *sensor, uint32_t misses); +void ButtonPullupFlag(uint8 button_bit); +void ButtonInvertFlag(uint8 button_bit); +void ButtonInit(void); +uint8_t ButtonSerial(uint8_t serial_in_byte); +void ButtonHandler(void); +void ButtonLoop(void); +void ResponseCmndNumber(int value); +void ResponseCmndFloat(float value, uint32_t decimals); +void ResponseCmndIdxNumber(int value); +void ResponseCmndChar(const char* value); +void ResponseCmndStateText(uint32_t value); +void ResponseCmndDone(void); +void ResponseCmndIdxChar(const char* value); +void ResponseCmndAll(uint32_t text_index, uint32_t count); +void ExecuteCommand(const char *cmnd, uint32_t source); +void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len); +void CmndBacklog(void); +void CmndDelay(void); +void CmndPower(void); +void CmndStatus(void); +void CmndState(void); +void CmndTempOffset(void); +void CmndSleep(void); +void CmndUpgrade(void); +void CmndOtaUrl(void); +void CmndSeriallog(void); +void CmndRestart(void); +void CmndPowerOnState(void); +void CmndPulsetime(void); +void CmndBlinktime(void); +void CmndBlinkcount(void); +void CmndSavedata(void); +void CmndSetoption(void); +void CmndTemperatureResolution(void); +void CmndHumidityResolution(void); +void CmndPressureResolution(void); +void CmndPowerResolution(void); +void CmndVoltageResolution(void); +void CmndFrequencyResolution(void); +void CmndCurrentResolution(void); +void CmndEnergyResolution(void); +void CmndWeightResolution(void); +void CmndModule(void); +void CmndModules(void); +void CmndGpio(void); +void CmndGpios(void); +void CmndTemplate(void); +void CmndPwm(void); +void CmndPwmfrequency(void); +void CmndPwmrange(void); +void CmndButtonDebounce(void); +void CmndSwitchDebounce(void); +void CmndBaudrate(void); +void CmndSerialConfig(void); +void CmndSerialSend(void); +void CmndSerialDelimiter(void); +void CmndSyslog(void); +void CmndLoghost(void); +void CmndLogport(void); +void CmndIpAddress(void); +void CmndNtpServer(void); +void CmndAp(void); +void CmndSsid(void); +void CmndPassword(void); +void CmndHostname(void); +void CmndWifiConfig(void); +void CmndFriendlyname(void); +void CmndSwitchMode(void); +void CmndInterlock(void); +void CmndTeleperiod(void); +void CmndReset(void); +void CmndTime(void); +void CmndTimezone(void); +void CmndTimeStdDst(uint32_t ts); +void CmndTimeStd(void); +void CmndTimeDst(void); +void CmndAltitude(void); +void CmndLedPower(void); +void CmndLedState(void); +void CmndLedMask(void); +void CmndWifiPower(void); +void CmndI2cScan(void); +void CmndI2cDriver(void); +void CmndSensor(void); +void CmndDriver(void); +void CmndCrash(void); +void CmndWDT(void); +void CmndBlockedLoop(void); +void CrashDumpClear(void); +bool CrashFlag(void); +void CrashDump(void); +static bool spiflash_is_ready(void); +static void spi_write_enable(void); +bool EsptoolEraseSector(uint32_t sector); +void EsptoolErase(uint32_t start_sector, uint32_t end_sector); +void GetFeatures(void); +float fmodf(float x, float y); +double FastPrecisePow(double a, double b); +float FastPrecisePowf(const float x, const float y); +double TaylorLog(double x); +inline float sinf(float x); +inline float cosf(float x); +inline float tanf(float x); +inline float atanf(float x); +inline float asinf(float x); +inline float acosf(float x); +inline float sqrtf(float x); +inline float powf(float x, float y); +float cos_52s(float x); +float cos_52(float x); +float sin_52(float x); +float tan_56s(float x); +float tan_56(float x); +float atan_66s(float x); +float atan_66(float x); +float asinf1(float x); +float acosf1(float x); +float sqrt1(const float x); +uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max, + uint16_t ito_min, uint16_t ito_max); +void* memchr(const void* ptr, int value, size_t num); +size_t strcspn(const char *str1, const char *str2); +char* strpbrk(const char *s1, const char *s2); +void* memmove_P(void *dest, const void *src, size_t n); +void update_rotary(void); +bool RotaryButtonPressed(void); +void RotaryInit(void); +void RotaryHandler(void); +void RotaryLoop(void); +uint32_t UtcTime(void); +uint32_t LocalTime(void); +int32_t DriftTime(void); +uint32_t Midnight(void); +bool MidnightNow(void); +bool IsDst(void); +String GetBuildDateAndTime(void); +String GetMinuteTime(uint32_t minutes); +String GetTimeZone(void); +String GetDuration(uint32_t time); +String GetDT(uint32_t time); +String GetDateAndTime(uint8_t time_type); +String GetTime(int type); +uint32_t UpTime(void); +uint32_t MinutesUptime(void); +String GetUptime(void); +uint32_t MinutesPastMidnight(void); +void BreakTime(uint32_t time_input, TIME_T &tm); +uint32_t MakeTime(TIME_T &tm); +uint32_t RuleToTime(TimeRule r, int yr); +void RtcSecond(void); +void RtcSetTime(uint32_t epoch); +void RtcInit(void); +String GetStatistics(void); +String GetStatistics(void); +void SwitchPullupFlag(uint16 switch_bit); +void SwitchSetVirtual(uint32_t index, uint8_t state); +uint8_t SwitchGetVirtual(uint32_t index); +uint8_t SwitchLastState(uint32_t index); +bool SwitchState(uint32_t index); +void SwitchProbe(void); +void SwitchInit(void); +void SwitchHandler(uint8_t mode); +void SwitchLoop(void); +char* Format(char* output, const char* input, int size); +char* GetOtaUrl(char *otaurl, size_t otaurl_size); +char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopic); +char* GetGroupTopic_P(char *stopic, const char* subtopic); +char* GetFallbackTopic_P(char *stopic, const char* subtopic); +char* GetStateText(uint32_t state); +void SetLatchingRelay(power_t lpower, uint32_t state); +void SetDevicePower(power_t rpower, uint32_t source); +void RestorePower(bool publish_power, uint32_t source); +void SetAllPower(uint32_t state, uint32_t source); +void SetPowerOnState(void); +void SetLedPowerIdx(uint32_t led, uint32_t state); +void SetLedPower(uint32_t state); +void SetLedPowerAll(uint32_t state); +void SetLedLink(uint32_t state); +void SetPulseTimer(uint32_t index, uint32_t time); +uint32_t GetPulseTimer(uint32_t index); +bool SendKey(uint32_t key, uint32_t device, uint32_t state); +void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source); +void StopAllPowerBlink(void); +void MqttShowPWMState(void); +void MqttShowState(void); +void MqttPublishTeleState(void); +bool MqttShowSensor(void); +void MqttPublishSensor(void); +void PerformEverySecond(void); +void Every100mSeconds(void); +void Every250mSeconds(void); +void ArduinoOTAInit(void); +void ArduinoOtaLoop(void); +void SerialInput(void); +void GpioInit(void); +bool UdpDisconnect(void); +bool UdpConnect(void); +void PollUdp(void); +int WifiGetRssiAsQuality(int rssi); +bool WifiConfigCounter(void); +void WifiConfig(uint8_t type); +void WifiSetMode(WiFiMode_t wifi_mode); +void WiFiSetSleepMode(void); +void WifiBegin(uint8_t flag, uint8_t channel); +void WifiBeginAfterScan(void); +uint16_t WifiLinkCount(void); +String WifiDowntime(void); +void WifiSetState(uint8_t state); +bool WifiCheckIPv6(void); +String WifiGetIPv6(void); +bool WifiCheckIPAddrStatus(void); +void WifiCheckIp(void); +void WifiCheck(uint8_t param); +int WifiState(void); +String WifiGetOutputPower(void); +void WifiSetOutputPower(void); +void WifiConnect(void); +void WifiDisconnect(void); +void WifiShutdown(void); +void EspRestart(void); +static void WebGetArg(const char* arg, char* out, size_t max); +static bool WifiIsInManagerMode(); +void ShowWebSource(uint32_t source); +void ExecuteWebCommand(char* svalue, uint32_t source); +void StartWebserver(int type, IPAddress ipweb); +void StopWebserver(void); +void WifiManagerBegin(bool reset_only); +void PollDnsWebserver(void); +bool WebAuthenticate(void); +void HttpHeaderCors(void); +void WSHeaderSend(void); +void WSSend(int code, int ctype, const String& content); +void WSContentBegin(int code, int ctype); +void _WSContentSend(const String& content); +void WSContentFlush(void); +void _WSContentSendBuffer(void); +void WSContentSend_P(const char* formatP, ...); +void WSContentSend_PD(const char* formatP, ...); +void WSContentStart_P(const char* title, bool auth); +void WSContentStart_P(const char* title); +void WSContentSendStyle_P(const char* formatP, ...); +void WSContentSendStyle(void); +void WSContentButton(uint32_t title_index); +void WSContentSpaceButton(uint32_t title_index); +void WSContentEnd(void); +void WSContentStop(void); +void WebRestart(uint32_t type); +void HandleWifiLogin(void); +void WebSliderColdWarm(void); +void HandleRoot(void); +bool HandleRootStatusRefresh(void); +int32_t IsShutterWebButton(uint32_t idx); +void HandleConfiguration(void); +void HandleTemplateConfiguration(void); +void TemplateSaveSettings(void); +void HandleModuleConfiguration(void); +void ModuleSaveSettings(void); +String HtmlEscape(const String unescaped); +void HandleWifiConfiguration(void); +void WifiSaveSettings(void); +void HandleLoggingConfiguration(void); +void LoggingSaveSettings(void); +void HandleOtherConfiguration(void); +void OtherSaveSettings(void); +void HandleBackupConfiguration(void); +void HandleResetConfiguration(void); +void HandleRestoreConfiguration(void); +void HandleInformation(void); +void HandleUpgradeFirmware(void); +void HandleUpgradeFirmwareStart(void); +void HandleUploadDone(void); +void HandleUploadLoop(void); +void HandlePreflightRequest(void); +void HandleHttpCommand(void); +void HandleConsole(void); +void HandleConsoleRefresh(void); +void HandleNotFound(void); +bool CaptivePortal(void); +String UrlEncode(const String& text); +int WebSend(char *buffer); +bool JsonWebColor(const char* dataBuf); +void CmndEmulation(void); +void CmndSendmail(void); +void CmndWebServer(void); +void CmndWebPassword(void); +void CmndWeblog(void); +void CmndWebRefresh(void); +void CmndWebSend(void); +void CmndWebColor(void); +void CmndWebSensor(void); +void CmndWebButton(void); +void CmndCors(void); +bool Xdrv01(uint8_t function); +bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value); +void MakeValidMqtt(uint32_t option, char* str); +void MqttDiscoverServer(void); +void MqttInit(void); +bool MqttIsConnected(void); +void MqttDisconnect(void); +void MqttSubscribeLib(const char *topic); +void MqttUnsubscribeLib(const char *topic); +bool MqttPublishLib(const char* topic, bool retained); +void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len); +void MqttRetryCounter(uint8_t value); +void MqttSubscribe(const char *topic); +void MqttUnsubscribe(const char *topic); +void MqttPublishLogging(const char *mxtime); +void MqttPublish(const char* topic, bool retained); +void MqttPublish(const char* topic); +void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retained); +void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic); +void MqttPublishTeleSensor(void); +void MqttPublishPowerState(uint32_t device); +void MqttPublishAllPowerState(void); +void MqttPublishPowerBlinkState(uint32_t device); +uint16_t MqttConnectCount(void); +void MqttDisconnected(int state); +void MqttConnected(void); +void MqttReconnect(void); +void MqttCheck(void); +void CmndMqttFingerprint(void); +void CmndMqttUser(void); +void CmndMqttPassword(void); +void CmndMqttlog(void); +void CmndMqttHost(void); +void CmndMqttPort(void); +void CmndMqttRetry(void); +void CmndStateText(void); +void CmndMqttClient(void); +void CmndFullTopic(void); +void CmndPrefix(void); +void CmndPublish(void); +void CmndGroupTopic(void); +void CmndTopic(void); +void CmndButtonTopic(void); +void CmndSwitchTopic(void); +void CmndButtonRetain(void); +void CmndSwitchRetain(void); +void CmndPowerRetain(void); +void CmndSensorRetain(void); +inline void TlsEraseBuffer(uint8_t *buffer); +void loadTlsDir(void); +void CmndTlsKey(void); +uint32_t bswap32(uint32_t x); +void CmndTlsDump(void); +void HandleMqttConfiguration(void); +void MqttSaveSettings(void); +bool Xdrv02(uint8_t function); +bool EnergyTariff1Active(); +void EnergyUpdateToday(void); +void EnergyUpdateTotal(float value, bool kwh); +void Energy200ms(void); +void EnergySaveState(void); +bool EnergyMargin(bool type, uint16_t margin, uint16_t value, bool &flag, bool &save_flag); +void EnergyMarginCheck(void); +void EnergyMqttShow(void); +void EnergyEverySecond(void); +void EnergyCommandCalResponse(uint32_t nvalue); +void CmndEnergyReset(void); +void CmndTariff(void); +void CmndPowerCal(void); +void CmndVoltageCal(void); +void CmndCurrentCal(void); +void CmndPowerSet(void); +void CmndVoltageSet(void); +void CmndCurrentSet(void); +void CmndFrequencySet(void); +void CmndModuleAddress(void); +void CmndPowerDelta(void); +void CmndPowerLow(void); +void CmndPowerHigh(void); +void CmndVoltageLow(void); +void CmndVoltageHigh(void); +void CmndCurrentLow(void); +void CmndCurrentHigh(void); +void CmndMaxPower(void); +void CmndMaxPowerHold(void); +void CmndMaxPowerWindow(void); +void CmndSafePower(void); +void CmndSafePowerHold(void); +void CmndSafePowerWindow(void); +void CmndMaxEnergy(void); +void CmndMaxEnergyStart(void); +void EnergySnsInit(void); +void EnergyShow(bool json); +bool Xdrv03(uint8_t function); +bool Xsns03(uint8_t function); +power_t LightPower(void); +power_t LightPowerIRAM(void); +uint8_t LightDevice(void); +static uint32_t min3(uint32_t a, uint32_t b, uint32_t c); +uint16_t change8to10(uint8_t v); +uint8_t change10to8(uint16_t v); +uint16_t ledGamma_internal(uint16_t v, const struct gamma_table_t *gt_ptr); +uint16_t ledGammaReverse_internal(uint16_t vg, const struct gamma_table_t *gt_ptr); +uint16_t ledGamma10_10(uint16_t v); +uint16_t ledGamma10(uint8_t v); +uint8_t ledGamma(uint8_t v); +void LightPwmOffset(uint32_t offset); +bool LightModuleInit(void); +void LightInit(void); +void LightUpdateColorMapping(void); +uint8_t LightGetDimmer(uint8_t dimmer); +void LightSetDimmer(uint8_t dimmer); +void LightGetHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri); +void LightHsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b); +uint8_t LightGetBri(uint8_t device); +void LightSetBri(uint8_t device, uint8_t bri); +void LightSetColorTemp(uint16_t ct); +uint16_t LightGetColorTemp(void); +void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value); +void LightPowerOn(void); +void LightState(uint8_t append); +void LightCycleColor(int8_t direction); +void LightSetPower(void); +void LightAnimate(void); +bool isChannelGammaCorrected(uint32_t channel); +uint16_t fadeGamma(uint32_t channel, uint16_t v); +uint16_t fadeGammaReverse(uint32_t channel, uint16_t vg); +bool LightApplyFade(void); +void LightApplyPower(uint8_t new_color[LST_MAX], power_t power); +void LightSetOutputs(const uint16_t *cur_col_10); +void calcGammaMultiChannels(uint16_t cur_col_10[5]); +void calcGammaBulbs(uint16_t cur_col_10[5]); +bool LightColorEntry(char *buffer, uint32_t buffer_length); +void CmndSupportColor(void); +void CmndColor(void); +void CmndWhite(void); +void CmndChannel(void); +void CmndHsbColor(void); +void CmndScheme(void); +void CmndWakeup(void); +void CmndColorTemperature(void); +void CmndDimmer(void); +void CmndDimmerRange(void); +void CmndLedTable(void); +void CmndRgbwwTable(void); +void CmndFade(void); +void CmndSpeed(void); +void CmndWakeupDuration(void); +void CmndUndocA(void); +bool Xdrv04(uint8_t function); +void IrSendInit(void); +void IrReceiveUpdateThreshold(void); +void IrReceiveInit(void); +void IrReceiveCheck(void); +uint32_t IrRemoteCmndIrSendJson(void); +void CmndIrSend(void); +void IrRemoteCmndResponse(uint32_t error); +bool Xdrv05(uint8_t function); +void IrSendInit(void); +uint8_t reverseBitsInByte(uint8_t b); +uint64_t reverseBitsInBytes64(uint64_t b); +void IrReceiveUpdateThreshold(void); +void IrReceiveInit(void); +String sendIRJsonState(const struct decode_results &results); +void IrReceiveCheck(void); +String listSupportedProtocols(bool hvac); +uint32_t IrRemoteCmndIrHvacJson(void); +void CmndIrHvac(void); +uint32_t IrRemoteCmndIrSendJson(void); +uint32_t IrRemoteCmndIrSendRaw(void); +void CmndIrSend(void); +void IrRemoteCmndResponse(uint32_t error); +bool Xdrv05(uint8_t function); +ssize_t rf_find_hex_record_start(uint8_t *buf, size_t size); +ssize_t rf_find_hex_record_end(uint8_t *buf, size_t size); +ssize_t rf_glue_remnant_with_new_data_and_write(const uint8_t *remnant_data, uint8_t *new_data, size_t new_data_len); +ssize_t rf_decode_and_write(uint8_t *record, size_t size); +ssize_t rf_search_and_write(uint8_t *buf, size_t size); +uint8_t rf_erase_flash(void); +uint8_t SnfBrUpdateInit(void); +void SonoffBridgeReceivedRaw(void); +void SonoffBridgeLearnFailed(void); +void SonoffBridgeReceived(void); +bool SonoffBridgeSerialInput(void); +void SonoffBridgeSendCommand(uint8_t code); +void SonoffBridgeSendAck(void); +void SonoffBridgeSendCode(uint32_t code); +void SonoffBridgeSend(uint8_t idx, uint8_t key); +void SonoffBridgeLearn(uint8_t key); +void CmndRfBridge(void); +void CmndRfKey(void); +void CmndRfRaw(void); +bool Xdrv06(uint8_t function); +int DomoticzBatteryQuality(void); +int DomoticzRssiQuality(void); +void MqttPublishDomoticzFanState(void); +void DomoticzUpdateFanState(void); +void MqttPublishDomoticzPowerState(uint8_t device); +void DomoticzUpdatePowerState(uint8_t device); +void DomoticzMqttUpdate(void); +void DomoticzMqttSubscribe(void); +bool DomoticzMqttData(void); +bool DomoticzSendKey(uint8_t key, uint8_t device, uint8_t state, uint8_t svalflg); +uint8_t DomoticzHumidityState(char *hum); +void DomoticzSensor(uint8_t idx, char *data); +void DomoticzSensor(uint8_t idx, uint32_t value); +void DomoticzTempHumSensor(char *temp, char *hum); +void DomoticzTempHumPressureSensor(char *temp, char *hum, char *baro); +void DomoticzSensorPowerEnergy(int power, char *energy); +void DomoticzSensorP1SmartMeter(char *usage1, char *usage2, char *return1, char *return2, int power); +void CmndDomoticzIdx(void); +void CmndDomoticzKeyIdx(void); +void CmndDomoticzSwitchIdx(void); +void CmndDomoticzSensorIdx(void); +void CmndDomoticzUpdateTimer(void); +void HandleDomoticzConfiguration(void); +void DomoticzSaveSettings(void); +bool Xdrv07(uint8_t function); +void SerialBridgeInput(void); +void SerialBridgeInit(void); +void CmndSSerialSend(void); +void CmndSBaudrate(void); +bool Xdrv08(uint8_t function); +float JulianischesDatum(void); +float InPi(float x); +float eps(float T); +float BerechneZeitgleichung(float *DK,float T); +void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8_t *minute_down); +void ApplyTimerOffsets(Timer *duskdawn); +String GetSun(uint32_t dawn); +uint16_t SunMinutes(uint32_t dawn); +void TimerSetRandomWindow(uint32_t index); +void TimerSetRandomWindows(void); +void TimerEverySecond(void); +void PrepShowTimer(uint32_t index); +void CmndTimer(void); +void CmndTimers(void); +void CmndLongitude(void); +void CmndLatitude(void); +void HandleTimerConfiguration(void); +void TimerSaveSettings(void); +bool Xdrv09(uint8_t function); +bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule); +int8_t parseCompareExpression(String &expr, String &leftExpr, String &rightExpr); +void RulesVarReplace(String &commands, const String &sfind, const String &replace); +bool RuleSetProcess(uint8_t rule_set, String &event_saved); +bool RulesProcessEvent(char *json_event); +bool RulesProcess(void); +void RulesInit(void); +void RulesEvery50ms(void); +void RulesEvery100ms(void); +void RulesEverySecond(void); +void RulesSaveBeforeRestart(void); +void RulesSetPower(void); +void RulesTeleperiod(void); +bool RulesMqttData(void); +void CmndSubscribe(void); +void CmndUnsubscribe(void); +bool findNextNumber(char * &pNumber, float &value); +bool findNextVariableValue(char * &pVarname, float &value); +bool findNextObjectValue(char * &pointer, float &value); +bool findNextOperator(char * &pointer, int8_t &op); +float calculateTwoValues(float v1, float v2, uint8_t op); +float evaluateExpression(const char * expression, unsigned int len); +void CmndIf(void); +bool evaluateComparisonExpression(const char *expression, int len); +bool findNextLogicOperator(char * &pointer, int8_t &op); +bool findNextLogicObjectValue(char * &pointer, bool &value); +bool evaluateLogicalExpression(const char * expression, int len); +int8_t findIfBlock(char * &pointer, int &lenWord, int8_t block_type); +void ExecuteCommandBlock(const char * commands, int len); +void ProcessIfStatement(const char* statements); +void RulesPreprocessCommand(char *pCommands); +void CmndRule(void); +void CmndRuleTimer(void); +void CmndEvent(void); +void CmndVariable(void); +void CmndMemory(void); +void CmndCalcResolution(void); +void CmndAddition(void); +void CmndSubtract(void); +void CmndMultiply(void); +void CmndScale(void); +float map_double(float x, float in_min, float in_max, float out_min, float out_max); +bool Xdrv10(uint8_t function); +void ScriptEverySecond(void); +void RulesTeleperiod(void); +int16_t Init_Scripter(void); +void ws2812_set_array(float *array ,uint8_t len); +float median_array(float *array,uint8_t len); +float Get_MFVal(uint8_t index,uint8_t bind); +void Set_MFVal(uint8_t index,uint8_t bind,float val); +float Get_MFilter(uint8_t index); +void Set_MFilter(uint8_t index, float invar); +float DoMedian5(uint8_t index, float in); +uint32_t HSVToRGB(uint16_t hue, uint8_t saturation, uint8_t value); +uint16_t GetStack(void); +uint16_t GetStack(void); +void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize); +void toLog(const char *str); +void toLogN(const char *cp,uint8_t len); +void toLogEOL(const char *s1,const char *str); +void toSLog(const char *str); +int16_t Run_Scripter(const char *type, int8_t tlen, char *js); +void ScripterEvery100ms(void); +void Scripter_save_pvars(void); +void ListDir(char *path, uint8_t depth); +void Script_FileUploadConfiguration(void); +void ScriptFileUploadSuccess(void); +void script_upload(void); +uint8_t DownloadFile(char *file); +void HandleScriptTextareaConfiguration(void); +void HandleScriptConfiguration(void); +void ScriptSaveSettings(void); +void Script_HueStatus(String *response, uint16_t hue_devs); +void Script_Check_Hue(String *response); +void Script_Handle_Hue(String *path); +bool Script_SubCmd(void); +void execute_script(char *script); +bool ScriptCommand(void); +uint16_t xFAT_DATE(uint16_t year, uint8_t month, uint8_t day); +uint16_t xFAT_TIME(uint8_t hour, uint8_t minute, uint8_t second); +void dateTime(uint16_t* date, uint16_t* time); +bool ScriptMqttData(void); +String ScriptSubscribe(const char *data, int data_len); +String ScriptUnsubscribe(const char * data, int data_len); +void Script_Check_HTML_Setvars(void); +void ScriptGetVarname(char *nbuf,char *sp, uint32_t blen); +void ScriptWebShow(void); +void ScriptJsonAppend(void); +bool Xdrv10(uint8_t function); +void KNX_ADD_GA( uint8_t GAop, uint8_t GA_FNUM, uint8_t GA_AREA, uint8_t GA_FDEF ); +void KNX_DEL_GA( uint8_t GAnum ); +void KNX_ADD_CB( uint8_t CBop, uint8_t CB_FNUM, uint8_t CB_AREA, uint8_t CB_FDEF ); +void KNX_DEL_CB( uint8_t CBnum ); +bool KNX_CONFIG_NOT_MATCH(void); +void KNXStart(void); +void KNX_INIT(void); +void KNX_CB_Action(message_t const &msg, void *arg); +void KnxUpdatePowerState(uint8_t device, power_t state); +void KnxSendButtonPower(void); +void KnxSensor(uint8_t sensor_type, float value); +void HandleKNXConfiguration(void); +void KNX_Save_Settings(void); +void CmndKnxTxCmnd(void); +void CmndKnxTxVal(void); +void CmndKnxEnabled(void); +void CmndKnxEnhanced(void); +void CmndKnxPa(void); +void CmndKnxGa(void); +void CmndKnxCb(void); +bool Xdrv11(uint8_t function); +void TryResponseAppend_P(const char *format, ...); +void HAssAnnounceRelayLight(void); +void HAssAnnounceButtonSwitch(uint8_t device, char *topic, uint8_t present, uint8_t key, uint8_t toggle); +void HAssAnnounceSwitches(void); +void HAssAnnounceButtons(void); +void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char *MultiSubName, uint8_t subqty, uint8_t subidx); +void HAssAnnounceSensors(void); +void HAssAnnounceStatusSensor(void); +void HAssPublishStatus(void); +void HAssDiscovery(void); +void HAssDiscover(void); +void HAssAnyKey(void); +bool Xdrv12(uint8_t function); +void DisplayInit(uint8_t mode); +void DisplayClear(void); +void DisplayDrawHLine(uint16_t x, uint16_t y, int16_t len, uint16_t color); +void DisplayDrawVLine(uint16_t x, uint16_t y, int16_t len, uint16_t color); +void DisplayDrawLine(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color); +void DisplayDrawCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color); +void DisplayDrawFilledCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color); +void DisplayDrawRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color); +void DisplayDrawFilledRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color); +void DisplayDrawFrame(void); +void DisplaySetSize(uint8_t size); +void DisplaySetFont(uint8_t font); +void DisplaySetRotation(uint8_t rotation); +void DisplayDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); +void DisplayOnOff(uint8_t on); +uint8_t fatoiv(char *cp,float *res); +uint8_t atoiv(char *cp, int16_t *res); +uint8_t atoiV(char *cp, uint16_t *res); +void alignright(char *string); +uint32_t decode_te(char *line); +void DisplayText(void); +void DisplayClearScreenBuffer(void); +void DisplayFreeScreenBuffer(void); +void DisplayAllocScreenBuffer(void); +void DisplayReAllocScreenBuffer(void); +void DisplayFillScreen(uint32_t line); +void DisplayClearLogBuffer(void); +void DisplayFreeLogBuffer(void); +void DisplayAllocLogBuffer(void); +void DisplayReAllocLogBuffer(void); +void DisplayLogBufferAdd(char* txt); +char* DisplayLogBuffer(char temp_code); +void DisplayLogBufferInit(void); +void DisplayJsonValue(const char* topic, const char* device, const char* mkey, const char* value); +void DisplayAnalyzeJson(char *topic, char *json); +void DisplayMqttSubscribe(void); +bool DisplayMqttData(void); +void DisplayLocalSensor(void); +void DisplayInitDriver(void); +void DisplaySetPower(void); +void CmndDisplay(void); +void CmndDisplayModel(void); +void CmndDisplayWidth(void); +void CmndDisplayHeight(void); +void CmndDisplayMode(void); +void CmndDisplayDimmer(void); +void CmndDisplaySize(void); +void CmndDisplayFont(void); +void CmndDisplayRotate(void); +void CmndDisplayText(void); +void CmndDisplayAddress(void); +void CmndDisplayRefresh(void); +void CmndDisplayColumns(void); +void CmndDisplayRows(void); +void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp); +void DrawAClock(uint16_t rad); +void ClrGraph(uint16_t num); +void DefineGraph(uint16_t num,uint16_t xp,uint16_t yp,int16_t xs,uint16_t ys,int16_t dec,float ymin, float ymax,uint8_t icol); +void DisplayCheckGraph(); +void Save_graph(uint8_t num, char *path); +void Restore_graph(uint8_t num, char *path); +void RedrawGraph(uint8_t num, uint8_t flags); +void AddGraph(uint8_t num,uint8_t val); +void AddValue(uint8_t num,float fval); +bool Xdrv13(uint8_t function); +uint16_t MP3_Checksum(uint8_t *array); +void MP3PlayerInit(void); +void MP3_CMD(uint8_t mp3cmd,uint16_t val); +bool MP3PlayerCmd(void); +bool Xdrv14(uint8_t function); +void PCA9685_Detect(void); +void PCA9685_Reset(void); +void PCA9685_SetPWMfreq(double freq); +void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off); +void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted); +bool PCA9685_Command(void); +void PCA9685_OutputTelemetry(bool telemetry); +bool Xdrv15(uint8_t function); +void CmndTuyaSend(void); +void CmndTuyaMcu(void); +void TuyaAddMcuFunc(uint8_t fnId, uint8_t dpId); +void UpdateDevices(); +inline bool TuyaFuncIdValid(uint8_t fnId); +uint8_t TuyaGetFuncId(uint8_t dpid); +uint8_t TuyaGetDpId(uint8_t fnId); +void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value); +void TuyaSendBool(uint8_t id, bool value); +void TuyaSendValue(uint8_t id, uint32_t value); +void TuyaSendEnum(uint8_t id, uint32_t value); +void TuyaSendString(uint8_t id, char data[]); +bool TuyaSetPower(void); +bool TuyaSetChannels(void); +void LightSerialDuty(uint16_t duty); +void TuyaRequestState(void); +void TuyaResetWifi(void); +void TuyaProcessStatePacket(void); +void TuyaLowPowerModePacketProcess(void); +void TuyaHandleProductInfoPacket(void); +void TuyaSendLowPowerSuccessIfNeeded(void); +void TuyaNormalPowerModePacketProcess(void); +bool TuyaModuleSelected(void); +void TuyaInit(void); +void TuyaSerialInput(void); +bool TuyaButtonPressed(void); +uint8_t TuyaGetTuyaWifiState(void); +void TuyaSetWifiLed(void); +bool Xnrg16(uint8_t function); +bool Xdrv16(uint8_t function); +void RfReceiveCheck(void); +void RfInit(void); +void CmndRfSend(void); +bool Xdrv17(uint8_t function); +bool ArmtronixSetChannels(void); +void LightSerial2Duty(uint8_t duty1, uint8_t duty2); +void ArmtronixRequestState(void); +bool ArmtronixModuleSelected(void); +void ArmtronixInit(void); +void ArmtronixSerialInput(void); +void ArmtronixSetWifiLed(void); +bool Xdrv18(uint8_t function); +void PS16DZSerialSend(const char *tx_buffer); +void PS16DZSerialSendOk(void); +void PS16DZSerialSendUpdateCommand(void); +void PS16DZSerialInput(void); +bool PS16DZSerialSendUpdateCommandIfRequired(void); +void PS16DZInit(void); +bool PS16DZModuleSelected(void); +bool Xdrv19(uint8_t function); +String HueBridgeId(void); +String HueSerialnumber(void); +String HueUuid(void); +void HueRespondToMSearch(void); +String GetHueDeviceId(uint8_t id); +String GetHueUserId(void); +void HandleUpnpSetupHue(void); +void HueNotImplemented(String *path); +void HueConfigResponse(String *response); +void HueConfig(String *path); +uint8_t getLocalLightSubtype(uint8_t device); +void HueLightStatus1(uint8_t device, String *response); +bool HueActive(uint8_t device); +void HueLightStatus2(uint8_t device, String *response); +uint32_t EncodeLightId(uint8_t relay_id); +uint32_t DecodeLightId(uint32_t hue_id); +uint32_t findEchoGeneration(void); +void HueGlobalConfig(String *path); +void HueAuthentication(String *path); +void HueLights(String *path); +void HueGroups(String *path); +void HandleHueApi(String *path); +bool Xdrv20(uint8_t function); +String WemoSerialnumber(void); +String WemoUuid(void); +void WemoRespondToMSearch(int echo_type); +void HandleUpnpEvent(void); +void HandleUpnpService(void); +void HandleUpnpMetaService(void); +void HandleUpnpSetupWemo(void); +bool Xdrv21(uint8_t function); +bool IsModuleIfan(void); +uint8_t MaxFanspeed(void); +uint8_t GetFanspeed(void); +void SonoffIFanSetFanspeed(uint8_t fanspeed, bool sequence); +void SonoffIfanReceived(void); +bool SonoffIfanSerialInput(void); +void CmndFanspeed(void); +bool SonoffIfanInit(void); +void SonoffIfanUpdate(void); +bool Xdrv22(uint8_t function); +void CopyJsonVariant(JsonObject &to, const String &key, const JsonVariant &val); +void CopyJsonArray(JsonArray &to, const JsonArray &arr); +void CopyJsonObject(JsonObject &to, const JsonObject &from); +uint16_t fromClusterCode(uint8_t c); +uint8_t toClusterCode(uint16_t c); +class SBuffer hibernateDevice(const struct Z_Device &device); +class SBuffer hibernateDevices(void); +void hidrateDevices(const SBuffer &buf); +void loadZigbeeDevices(void); +void saveZigbeeDevices(void); +void eraseZigbeeDevices(void); +uint8_t toPercentageCR2032(uint32_t voltage); +uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer &buf, + uint32_t offset, uint32_t len); +int32_t Z_ManufKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); +int32_t Z_ModelKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); +int32_t Z_Remove(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); +int32_t Z_Copy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); +int32_t Z_AddPressureUnit(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); +int32_t Z_FloatDiv100(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); +int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); +int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value); +int32_t Z_AqaraCube(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); +int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); +int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); +int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value); +void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint); +const __FlashStringHelper* zigbeeFindCommand(const char *command); +inline bool isXYZ(char c); +inline char hexDigit(uint32_t h); +String zigbeeCmdAddParams(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t z); +uint32_t ZigbeeAliasOrNumber(const char *state_text); +uint8_t ZigbeeGetInstructionSize(uint8_t instr); +void ZigbeeGotoLabel(uint8_t label); +void ZigbeeStateMachine_Run(void); +int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf); +int32_t Z_CheckNVWrite(int32_t res, class SBuffer &buf); +int32_t Z_Reboot(int32_t res, class SBuffer &buf); +int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf); +bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match); +int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf); +void Z_SendActiveEpReq(uint16_t shortaddr); +void Z_SendSimpleDescReq(uint16_t shortaddr, uint8_t endpoint); +int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf); +int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf); +void Z_SendAFInfoRequest(uint16_t shortaddr, uint8_t endpoint, uint16_t clusterid, uint8_t transacid); +int32_t Z_ReceiveSimpleDesc(int32_t res, const class SBuffer &buf); +int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf); +int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf); +void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, const JsonObject *json); +int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value); +int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf); +int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf); +int32_t Z_Load_Devices(uint8_t value); +int32_t Z_State_Ready(uint8_t value); +int32_t ZigbeeProcessInput(class SBuffer &buf); +void ZigbeeInput(void); +void ZigbeeInit(void); +uint32_t strToUInt(const JsonVariant &val); +void CmndZbReset(void); +void CmndZbZNPSendOrReceive(bool send); +void CmndZbZNPReceive(void); +void CmndZbZNPSend(void); +void ZigbeeZNPSend(const uint8_t *msg, size_t len); +void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp, uint8_t transacId); +inline int8_t hexValue(char c); +void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data); +void CmndZbSend(void); +void CmndZbBind(void); +void CmndZbProbe(void); +void CmndZbName(void); +void CmndZbForget(void); +void CmndZbSave(void); +void CmndZbRead(void); +void CmndZbPermitJoin(void); +void CmndZbStatus(void); +bool Xdrv23(uint8_t function); +void BuzzerOff(void); +void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32_t mode); +void BuzzerSetStateToLed(uint32_t state); +void BuzzerBeep(uint32_t count); +void BuzzerEnabledBeep(uint32_t count, uint32_t duration); +bool BuzzerPinState(void); +void BuzzerInit(void); +void BuzzerEvery100mSec(void); +void CmndBuzzer(void); +bool Xdrv24(uint8_t function); +void A4988Init(void); +void CmndDoMove(void); +void CmndDoRotate(void); +void CmndDoTurn(void); +void CmndSetMIS(void); +void CmndSetSPR(void); +void CmndSetRPM(void); +bool Xdrv25(uint8_t function); +void AriluxRfInterrupt(void); +void AriluxRfHandler(void); +void AriluxRfInit(void); +void AriluxRfDisable(void); +bool Xdrv26(uint8_t function); +void ShutterLogPos(uint32_t i); +void ShutterRtc50mS(void); +int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index); +uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index); +void ShutterInit(void); +void ShutterReportPosition(bool always); +void ShutterLimitRealAndTargetPositions(uint32_t i); +void ShutterUpdatePosition(void); +bool ShutterState(uint32_t device); +void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos); +void ShutterWaitForMotorStop(uint32_t i); +int32_t ShutterCounterBasedPosition(uint32_t i); +void ShutterRelayChanged(void); +bool ShutterButtonIsSimultaneousHold(uint32_t button_index, uint32_t shutter_index); +void ShutterButtonHandler(void); +void ShutterSetPosition(uint32_t device, uint32_t position); +void CmndShutterOpen(void); +void CmndShutterClose(void); +void CmndShutterStop(void); +void CmndShutterPosition(void); +void CmndShutterOpenTime(void); +void CmndShutterCloseTime(void); +void CmndShutterMotorDelay(void); +void CmndShutterRelay(void); +void CmndShutterButton(void); +void CmndShutterSetHalfway(void); +void CmndShutterFrequency(void); +void CmndShutterSetClose(void); +void CmndShutterInvert(void); +void CmndShutterCalibration(void); +void CmndShutterLock(void); +void CmndShutterEnableEndStopTime(void); +bool Xdrv27(uint8_t function); +void Pcf8574SwitchRelay(void); +void Pcf8574Init(void); +void HandlePcf8574(void); +void Pcf8574SaveSettings(void); +bool Xdrv28(uint8_t function); +bool DeepSleepEnabled(void); +void DeepSleepReInit(void); +void DeepSleepPrepare(void); +void DeepSleepStart(void); +void DeepSleepEverySecond(void); +void CmndDeepsleepTime(void); +bool Xdrv29(uint8_t function); +uint8_t crc8(const uint8_t *p, uint8_t len); +void ExsSendCmd(uint8_t cmd, uint8_t value); +uint8_t ExsSetPower(uint8_t device, uint8_t power); +uint8_t ExsSetBri(uint8_t device, uint8_t bri); +uint8_t ExsSyncState(uint8_t device); +bool ExsSyncState(); +void ExsDebugState(); +void ExsPacketProcess(void); +bool ExsModuleSelected(void); +bool ExsSetChannels(void); +bool ExsSetPower(void); +void EsxMcuStart(void); +void ExsInit(void); +void ExsSerialInput(void); +void CmndExsDimm(void); +void CmndExsDimmTbl(void); +void CmndExsDimmVal(void); +void CmndExsDimms(void); +void CmndExsChLock(void); +void CmndExsState(void); +bool Xdrv30(uint8_t function); +uint32_t TasmotaSlave_FlashStart(void); +uint8_t TasmotaSlave_UpdateInit(void); +void TasmotaSlave_Reset(void); +uint8_t TasmotaSlave_waitForSerialData(int dataCount, int timeout); +uint8_t TasmotaSlave_sendBytes(uint8_t* bytes, int count); +uint8_t TasmotaSlave_execCmd(uint8_t cmd); +uint8_t TasmotaSlave_execParam(uint8_t cmd, uint8_t* params, int count); +uint8_t TasmotaSlave_exitProgMode(void); +uint8_t TasmotaSlave_SetupFlash(void); +uint8_t TasmotaSlave_loadAddress(uint8_t adrHi, uint8_t adrLo); +void TasmotaSlave_FlashPage(uint8_t addr_h, uint8_t addr_l, uint8_t* data); +void TasmotaSlave_Flash(void); +void TasmotaSlave_SetFlagFlashing(bool value); +bool TasmotaSlave_GetFlagFlashing(void); +void TasmotaSlave_WriteBuffer(uint8_t *buf, size_t size); +void TasmotaSlave_Init(void); +void TasmotaSlave_Show(void); +void TasmotaSlave_sendCmnd(uint8_t cmnd, uint8_t param); +void CmndTasmotaSlaveReset(void); +void CmndTasmotaSlaveSend(void); +void TasmotaSlave_ProcessIn(void); +bool Xdrv31(uint8_t function); +void HotPlugInit(void); +void HotPlugEverySecond(void); +void CmndHotPlugTime(void); +bool Xdrv32(uint8_t function); +bool NRF24initRadio(); +bool NRF24Detect(void); +bool Xdrv33(uint8_t function); +void ExceptionTest(uint8_t type); +void CpuLoadLoop(void); +void DebugFreeMem(void); +void DebugFreeMem(void); +void DebugRtcDump(char* parms); +void DebugCfgDump(char* parms); +void DebugCfgPeek(char* parms); +void DebugCfgPoke(char* parms); +void SetFlashMode(uint8_t mode); +void CmndHelp(void); +void CmndRtcDump(void); +void CmndCfgDump(void); +void CmndCfgPeek(void); +void CmndCfgPoke(void); +void CmndCfgXor(void); +void CmndException(void); +void CmndCpuCheck(void); +void CmndFreemem(void); +void CmndSetSensor(void); +void CmndFlashMode(void); +uint32_t DebugSwap32(uint32_t x); +void CmndFlashDump(void); +void CmndI2cWrite(void); +void CmndI2cRead(void); +void CmndI2cStretch(void); +void CmndI2cClock(void); +bool Xdrv99(uint8_t function); +void XsnsDriverState(void); +bool XdrvRulesProcess(void); +void ShowFreeMem(const char *where); +bool XdrvCallDriver(uint32_t driver, uint8_t Function); +bool XdrvCall(uint8_t Function); +void LcdInitMode(void); +void LcdInit(uint8_t mode); +void LcdInitDriver(void); +void LcdDrawStringAt(void); +void LcdDisplayOnOff(uint8_t on); +void LcdCenter(uint8_t row, char* txt); +bool LcdPrintLog(void); +void LcdTime(void); +void LcdRefresh(void); +bool Xdsp01(uint8_t function); +void SSD1306InitDriver(void); +void Ssd1306PrintLog(void); +void Ssd1306Time(void); +void Ssd1306Refresh(void); +bool Xdsp02(byte function); +void MatrixWrite(void); +void MatrixClear(void); +void MatrixFixed(char* txt); +void MatrixCenter(char* txt); +void MatrixScrollLeft(char* txt, int loop); +void MatrixScrollUp(char* txt, int loop); +void MatrixInitMode(void); +void MatrixInit(uint8_t mode); +void MatrixInitDriver(void); +void MatrixOnOff(void); +void MatrixDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); +void MatrixPrintLog(uint8_t direction); +void MatrixRefresh(void); +bool Xdsp03(uint8_t function); +void Ili9341InitMode(void); +void Ili9341Init(uint8_t mode); +void Ili9341InitDriver(void); +void Ili9341Clear(void); +void Ili9341DrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); +void Ili9341DisplayOnOff(uint8_t on); +void Ili9341OnOff(void); +void Ili9341PrintLog(void); +void Ili9341Refresh(void); +bool Xdsp04(uint8_t function); +void EpdInitDriver29(); +void EpdPrintLog29(void); +void EpdRefresh29(void); +bool Xdsp05(uint8_t function); +void EpdInitDriver42(); +void EpdRefresh42(); +bool Xdsp06(uint8_t function); +void SH1106InitDriver(); +void SH1106PrintLog(void); +void SH1106Time(void); +void SH1106Refresh(void); +bool Xdsp07(uint8_t function); +void ILI9488_InitDriver(); +void ILI9488_MQTT(uint8_t count,const char *cp); +void ILI9488_RDW_BUTT(uint32_t count,uint32_t pwr); +void FT6236Check(); +bool Xdsp08(uint8_t function); +void SSD1351_InitDriver(); +void SSD1351PrintLog(void); +void SSD1351Time(void); +void SSD1351Refresh(void); +bool Xdsp09(uint8_t function); +void RA8876_InitDriver(); +void RA8876_MQTT(uint8_t count,const char *cp); +void RA8876_RDW_BUTT(uint32_t count,uint32_t pwr); +void FT5316Check(); +bool Xdsp10(uint8_t function); +uint8_t XdspPresent(void); +bool XdspCall(uint8_t Function); +void Ws2812StripShow(void); +int mod(int a, int b); +void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offset); +void Ws2812UpdateHand(int position, uint32_t index); +void Ws2812Clock(void); +void Ws2812GradientColor(uint32_t schemenr, struct WsColor* mColor, uint32_t range, uint32_t gradRange, uint32_t i); +void Ws2812Gradient(uint32_t schemenr); +void Ws2812Bars(uint32_t schemenr); +void Ws2812Clear(void); +void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white); +char* Ws2812GetColor(uint32_t led, char* scolor); +void Ws2812ForceSuspend (void); +void Ws2812ForceUpdate (void); +bool Ws2812SetChannels(void); +void Ws2812ShowScheme(void); +void Ws2812ModuleSelected(void); +void CmndLed(void); +void CmndPixels(void); +void CmndRotation(void); +void CmndWidth(void); +bool Xlgt01(uint8_t function); +void LightDiPulse(uint8_t times); +void LightDckiPulse(uint8_t times); +void LightMy92x1Write(uint8_t data); +void LightMy92x1Init(void); +void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty_w, uint8_t duty_c); +bool My92x1SetChannels(void); +void My92x1ModuleSelected(void); +bool Xlgt02(uint8_t function); +void SM16716_SendBit(uint8_t v); +void SM16716_SendByte(uint8_t v); +void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b); +void SM16716_Init(void); +bool Sm16716SetChannels(void); +void Sm16716ModuleSelected(void); +bool Xlgt03(uint8_t function); +uint8_t Sm2135Write(uint8_t data); +void Sm2135Send(uint8_t *buffer, uint8_t size); +bool Sm2135SetChannels(void); +void Sm2135ModuleSelected(void); +bool Xlgt04(uint8_t function); +void SnfL1Send(const char *buffer); +void SnfL1SerialSendOk(void); +bool SnfL1SerialInput(void); +bool SnfL1SetChannels(void); +void SnfL1ModuleSelected(void); +bool Xlgt05(uint8_t function); +bool XlgtCall(uint8_t function); +void HlwCfInterrupt(void); +void HlwCf1Interrupt(void); +void HlwEvery200ms(void); +void HlwEverySecond(void); +void HlwSnsInit(void); +void HlwDrvInit(void); +bool HlwCommand(void); +bool Xnrg01(uint8_t function); +void CseReceived(void); +bool CseSerialInput(void); +void CseEverySecond(void); +void CseSnsInit(void); +void CseDrvInit(void); +bool CseCommand(void); +bool Xnrg02(uint8_t function); +uint8_t PzemCrc(uint8_t *data); +void PzemSend(uint8_t cmd); +bool PzemReceiveReady(void); +bool PzemRecieve(uint8_t resp, float *data); +void PzemEvery250ms(void); +void PzemSnsInit(void); +void PzemDrvInit(void); +bool PzemCommand(void); +bool Xnrg03(uint8_t function); +uint8_t McpChecksum(uint8_t *data); +unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size); +void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size); +void McpSend(uint8_t *data); +void McpGetAddress(void); +void McpAddressReceive(void); +void McpGetCalibration(void); +void McpParseCalibration(void); +bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift); +void McpSetCalibration(struct mcp_cal_registers_type *cal_registers); +void McpSetSystemConfiguration(uint16 interval); +void McpGetFrequency(void); +void McpParseFrequency(void); +void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency); +void McpGetData(void); +void McpParseData(void); +void McpSerialInput(void); +void McpEverySecond(void); +void McpSnsInit(void); +void McpDrvInit(void); +bool McpCommand(void); +bool Xnrg04(uint8_t function); +void PzemAcEverySecond(void); +void PzemAcSnsInit(void); +void PzemAcDrvInit(void); +bool PzemAcCommand(void); +bool Xnrg05(uint8_t function); +void PzemDcEverySecond(void); +void PzemDcSnsInit(void); +void PzemDcDrvInit(void); +bool PzemDcCommand(void); +bool Xnrg06(uint8_t function); +int Ade7953RegSize(uint16_t reg); +void Ade7953Write(uint16_t reg, uint32_t val); +int32_t Ade7953Read(uint16_t reg); +void Ade7953Init(void); +void Ade7953GetData(void); +void Ade7953EnergyEverySecond(void); +void Ade7953DrvInit(void); +bool Ade7953Command(void); +bool Xnrg07(uint8_t function); +void SDM120Every250ms(void); +void Sdm120SnsInit(void); +void Sdm120DrvInit(void); +void Sdm220Reset(void); +void Sdm220Show(bool json); +bool Xnrg08(uint8_t function); +void Dds2382EverySecond(void); +void Dds2382SnsInit(void); +void Dds2382DrvInit(void); +bool Xnrg09(uint8_t function); +void SDM630Every250ms(void); +void Sdm630SnsInit(void); +void Sdm630DrvInit(void); +bool Xnrg10(uint8_t function); +void DDSU666Every250ms(void); +void Ddsu666SnsInit(void); +void Ddsu666DrvInit(void); +bool Xnrg11(uint8_t function); +bool solaxX1_RS485ReceiveReady(void); +void solaxX1_RS485Send(uint16_t msgLen); +uint8_t solaxX1_RS485Receive(uint8_t *value); +uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen); +void solaxX1_SendInverterAddress(void); +void solaxX1_QueryLiveData(void); +uint8_t solaxX1_ParseErrorCode(uint32_t code); +void solaxX1250MSecond(void); +void solaxX1SnsInit(void); +void solaxX1DrvInit(void); +void solaxX1Show(bool json); +bool Xnrg12(uint8_t function); +void FifLEEvery250ms(void); +void FifLESnsInit(void); +void FifLEDrvInit(void); +void FifLEReset(void); +void FifLEShow(bool json); +bool Xnrg13(uint8_t function); +bool XnrgCall(uint8_t function); +void CounterUpdate(uint8_t index); +void CounterUpdate1(void); +void CounterUpdate2(void); +void CounterUpdate3(void); +void CounterUpdate4(void); +bool CounterPinState(void); +void CounterInit(void); +void CounterEverySecond(void); +void CounterSaveState(void); +void CounterShow(bool json); +void CmndCounter(void); +void CmndCounterType(void); +void CmndCounterDebounce(void); +bool Xsns01(uint8_t function); +void AdcInit(void); +uint16_t AdcRead(uint8_t factor); +void AdcEvery250ms(void); +uint16_t AdcGetLux(void); +uint16_t AdcGetRange(void); +void AdcGetCurrentPower(uint8_t factor); +void AdcEverySecond(void); +void AdcShow(bool json); +void CmndAdc(void); +void CmndAdcs(void); +void CmndAdcParam(void); +bool Xsns02(uint8_t function); +void SonoffScSend(const char *data); +void SonoffScInit(void); +void SonoffScSerialInput(char *rcvstat); +void SonoffScShow(bool json); +bool Xsns04(uint8_t function); +uint8_t OneWireReset(void); +void OneWireWriteBit(uint8_t v); +uint8_t OneWireReadBit(void); +void OneWireWrite(uint8_t v); +uint8_t OneWireRead(void); +void OneWireSelect(const uint8_t rom[8]); +void OneWireResetSearch(void); +uint8_t OneWireSearch(uint8_t *newAddr); +bool OneWireCrc8(uint8_t *addr); +void Ds18x20Init(void); +void Ds18x20Convert(void); +bool Ds18x20Read(uint8_t sensor); +void Ds18x20Name(uint8_t sensor); +void Ds18x20EverySecond(void); +void Ds18x20Show(bool json); +bool Xsns05(uint8_t function); +void DhtReadPrep(void); +int32_t DhtExpectPulse(uint8_t sensor, bool level); +bool DhtRead(uint8_t sensor); +void DhtReadTempHum(uint8_t sensor); +bool DhtPinState(); +void DhtInit(void); +void DhtEverySecond(void); +void DhtShow(bool json); +bool Xsns06(uint8_t function); +void DhtReadPrep(void); +int32_t DhtExpectPulse(uint8_t sensor, bool level); +bool DhtRead(uint8_t sensor); +void DhtReadTempHum(uint8_t sensor); +bool DhtPinState(); +void DhtInit(void); +void DhtEverySecond(void); +void DhtShow(bool json); +bool Xsns06(uint8_t function); +bool DhtExpectPulse(uint8_t sensor, int level); +int DhtReadDat(uint8_t sensor); +bool DhtRead(uint8_t sensor); +void DhtReadTempHum(uint8_t sensor); +bool DhtPinState(); +void DhtInit(void); +void DhtEverySecond(void); +void DhtShow(bool json); +bool Xsns06(uint8_t function); +bool DhtExpectPulse(uint32_t sensor, uint32_t level); +bool DhtRead(uint32_t sensor); +void DhtReadTempHum(uint32_t sensor); +bool DhtPinState(); +void DhtInit(void); +void DhtEverySecond(void); +void DhtShow(bool json); +bool Xsns06(uint8_t function); +bool DhtWaitState(uint32_t sensor, uint32_t level); +bool DhtRead(uint32_t sensor); +bool DhtPinState(); +void DhtInit(void); +void DhtEverySecond(void); +void DhtShow(bool json); +bool Xsns06(uint8_t function); +bool ShtReset(void); +bool ShtSendCommand(const uint8_t cmd); +bool ShtAwaitResult(void); +int ShtReadData(void); +bool ShtRead(void); +void ShtDetect(void); +void ShtEverySecond(void); +void ShtShow(bool json); +bool Xsns07(uint8_t function); +uint8_t HtuCheckCrc8(uint16_t data); +uint8_t HtuReadDeviceId(void); +void HtuSetResolution(uint8_t resolution); +void HtuReset(void); +void HtuHeater(uint8_t heater); +void HtuInit(void); +bool HtuRead(void); +void HtuDetect(void); +void HtuEverySecond(void); +void HtuShow(bool json); +bool Xsns08(uint8_t function); +bool Bmp180Calibration(uint8_t bmp_idx); +void Bmp180Read(uint8_t bmp_idx); +bool Bmx280Calibrate(uint8_t bmp_idx); +void Bme280Read(uint8_t bmp_idx); +static void BmeDelayMs(uint32_t ms); +bool Bme680Init(uint8_t bmp_idx); +void Bme680Read(uint8_t bmp_idx); +void BmpDetect(void); +void BmpRead(void); +void BmpShow(bool json); +void BMP_EnterSleep(void); +bool Xsns09(uint8_t function); +bool Bh1750Read(void); +void Bh1750Detect(void); +void Bh1750EverySecond(void); +void Bh1750Show(bool json); +bool Xsns10(uint8_t function); +void Veml6070Detect(void); +void Veml6070UvTableInit(void); +void Veml6070EverySecond(void); +void Veml6070ModeCmd(bool mode_cmd); +uint16_t Veml6070ReadUv(void); +double Veml6070UvRiskLevel(uint16_t uv_level); +double Veml6070UvPower(double uvrisk); +void Veml6070Show(bool json); +bool Xsns11(uint8_t function); +void Ads1115StartComparator(uint8_t channel, uint16_t mode); +int16_t Ads1115GetConversion(uint8_t channel); +void Ads1115Detect(void); +void Ads1115Show(bool json); +bool Xsns12(uint8_t function); +bool Ina219SetCalibration(uint8_t mode, uint16_t addr); +float Ina219GetShuntVoltage_mV(uint16_t addr); +float Ina219GetBusVoltage_V(uint16_t addr); +float Ina219GetCurrent_mA(uint16_t addr); +bool Ina219Read(void); +bool Ina219CommandSensor(void); +void Ina219Detect(void); +void Ina219EverySecond(void); +void Ina219Show(bool json); +bool Xsns13(uint8_t function); +bool Sht3xRead(float &t, float &h, uint8_t sht3x_address); +void Sht3xDetect(void); +void Sht3xShow(bool json); +bool Xsns14(uint8_t function); +uint8_t MhzCalculateChecksum(uint8_t *array); +size_t MhzSendCmd(uint8_t command_id); +bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s); +void MhzEverySecond(void); +bool MhzCommandSensor(void); +void MhzInit(void); +void MhzShow(bool json); +bool Xsns15(uint8_t function); +bool Tsl2561Read(void); +void Tsl2561Detect(void); +void Tsl2561EverySecond(void); +void Tsl2561Show(bool json); +bool Xsns16(uint8_t function); +void Senseair250ms(void); +void SenseairInit(void); +void SenseairShow(bool json); +bool Xsns17(uint8_t function); +bool PmsReadData(void); +void PmsSecond(void); +void PmsInit(void); +void PmsShow(bool json); +bool Xsns18(uint8_t function); +void MGSInit(void); +void MGSPrepare(void); +char* measure_gas(int gas_type, char* buffer); +void MGSShow(bool json); +bool Xsns19(uint8_t function); +bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensorid, uint8_t *buffer); +void NovaSdsSetWorkPeriod(void); +bool NovaSdsReadData(void); +void NovaSdsSecond(void); +bool NovaSdsCommandSensor(void); +void NovaSdsInit(void); +void NovaSdsShow(bool json); +bool Xsns20(uint8_t function); +void sgp30_Init(void); +float sgp30_AbsoluteHumidity(float temperature, float humidity,char tempUnit); +void Sgp30Update(void); +void Sgp30Show(bool json); +bool Xsns21(uint8_t function); +uint8_t Sr04TModeDetect(void); +uint16_t Sr04TMiddleValue(uint16_t first, uint16_t second, uint16_t third); +uint16_t Sr04TMode3Distance(); +uint16_t Sr04TMode2Distance(void); +void Sr04TReading(void); +void Sr04Show(bool json); +bool Xsns22(uint8_t function); +uint8_t Si1145ReadByte(uint8_t reg); +uint16_t Si1145ReadHalfWord(uint8_t reg); +bool Si1145WriteByte(uint8_t reg, uint16_t val); +uint8_t Si1145WriteParamData(uint8_t p, uint8_t v); +bool Si1145Present(void); +void Si1145Reset(void); +void Si1145DeInit(void); +bool Si1145Begin(void); +uint16_t Si1145ReadUV(void); +uint16_t Si1145ReadVisible(void); +uint16_t Si1145ReadIR(void); +bool Si1145Read(void); +void Si1145Detect(void); +void Si1145Update(void); +void Si1145Show(bool json); +bool Xsns24(uint8_t function); +void LM75ADDetect(void); +float LM75ADGetTemp(void); +void LM75ADShow(bool json); +bool Xsns26(uint8_t function); +int8_t wireReadDataBlock( uint8_t reg, + uint8_t *val, + uint16_t len); +void calculateColorTemperature(void); +bool APDS9960_init(void); +uint8_t getMode(void); +void setMode(uint8_t mode, uint8_t enable); +void enableLightSensor(void); +void disableLightSensor(void); +void enableProximitySensor(void); +void disableProximitySensor(void); +void enableGestureSensor(void); +void disableGestureSensor(void); +bool isGestureAvailable(void); +int16_t readGesture(void); +void enablePower(void); +void disablePower(void); +void readAllColorAndProximityData(void); +void resetGestureParameters(void); +bool processGestureData(void); +bool decodeGesture(void); +void handleGesture(void); +void APDS9960_adjustATime(void); +void APDS9960_loop(void); +void APDS9960_detect(void); +void APDS9960_show(bool json); +bool APDS9960CommandSensor(void); +bool Xsns27(uint8_t function); +void Tm16XXSend(uint8_t data); +void Tm16XXSendCommand(uint8_t cmd); +void TM16XXSendData(uint8_t address, uint8_t data); +uint8_t Tm16XXReceive(void); +void Tm16XXClearDisplay(void); +void Tm1638SetLED(uint8_t color, uint8_t pos); +void Tm1638SetLEDs(word leds); +uint8_t Tm1638GetButtons(void); +void TmInit(void); +void TmLoop(void); +bool Xsns28(uint8_t function); +void MCP230xx_CheckForIntCounter(void); +void MCP230xx_CheckForIntRetainer(void); +const char* IntModeTxt(uint8_t intmo); +uint8_t MCP230xx_readGPIO(uint8_t port); +void MCP230xx_ApplySettings(void); +void MCP230xx_Detect(void); +void MCP230xx_CheckForInterrupt(void); +void MCP230xx_Show(bool json); +void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate); +void MCP230xx_Reset(uint8_t pinmode); +bool MCP230xx_Command(void); +void MCP230xx_UpdateWebData(void); +void MCP230xx_OutputTelemetry(void); +void MCP230xx_Interrupt_Counter_Report(void); +void MCP230xx_Interrupt_Retain_Report(void); +bool Xsns29(uint8_t function); +void Mpr121Init(struct mpr121 *pS, bool initial); +void Mpr121Show(struct mpr121 *pS, uint8_t function); +bool Xsns30(uint8_t function); +void CCS811Detect(void); +void CCS811Update(void); +void CCS811Show(bool json); +bool Xsns31(uint8_t function); +void MPU_6050PerformReading(void); +void MPU_6050Detect(void); +void MPU_6050Show(bool json); +bool Xsns32(uint8_t function); +void DS3231Detect(void); +uint8_t bcd2dec(uint8_t n); +uint8_t dec2bcd(uint8_t n); +uint32_t ReadFromDS3231(void); +void SetDS3231Time (uint32_t epoch_time); +void DS3231EverySecond(void); +bool Xsns33(uint8_t function); +bool HxIsReady(uint16_t timeout); +long HxRead(void); +void HxResetPart(void); +void HxReset(void); +void HxCalibrationStateTextJson(uint8_t msg_id); +void SetWeightDelta(); +bool HxCommand(void); +long HxWeight(void); +void HxInit(void); +void HxEvery100mSecond(void); +void HxSaveBeforeRestart(void); +void HxShow(bool json); +void HandleHxAction(void); +void HxSaveSettings(void); +void HxLogUpdates(void); +bool Xsns34(uint8_t function); +void Tx20StartRead(void); +void Tx20Read(void); +void Tx20Init(void); +void Tx20Show(bool json); +bool Xsns35(uint8_t function); +void MGC3130_handleSensorData(); +void MGC3130_sendMessage(uint8_t data[], uint8_t length); +void MGC3130_handleGesture(); +bool MGC3130_handleTouch(); +void MGC3130_handleAirWheel(); +void MGC3130_handleSystemStatus(); +bool MGC3130_receiveMessage(); +bool MGC3130_readData(); +void MGC3130_nextMode(); +void MGC3130_loop(); +void MGC3130_detect(void); +void MGC3130_show(bool json); +bool MGC3130CommandSensor(); +bool Xsns36(uint8_t function); +bool RfSnsFetchSignal(uint8_t DataPin, bool StateSignal); +void RfSnsInitTheoV2(void); +void RfSnsAnalyzeTheov2(void); +void RfSnsTheoV2Show(bool json); +void RfSnsInitAlectoV2(void); +void RfSnsAnalyzeAlectov2(); +void RfSnsAlectoResetRain(void); +uint8_t RfSnsAlectoCRC8(uint8_t *addr, uint8_t len); +void RfSnsAlectoV2Show(bool json); +void RfSnsInit(void); +void RfSnsAnalyzeRawSignal(void); +void RfSnsEverySecond(void); +void RfSnsShow(bool json); +bool Xsns37(uint8_t function); +void AzEverySecond(void); +void AzInit(void); +void AzShow(bool json); +bool Xsns38(uint8_t function); +void MAX31855_Init(void); +void MAX31855_GetResult(void); +float MAX31855_GetProbeTemperature(int32_t RawData); +float MAX31855_GetReferenceTemperature(int32_t RawData); +int32_t MAX31855_ShiftIn(uint8_t Length); +void MAX31855_Show(bool Json); +bool Xsns39(uint8_t function); +void PN532_Init(void); +int8_t PN532_receive(uint8_t *buf, int len, uint16_t timeout); +int8_t PN532_readAckFrame(void); +uint32_t PN532_getFirmwareVersion(void); +void PN532_wakeup(void); +bool PN532_setPassiveActivationRetries(uint8_t maxRetries); +bool PN532_SAMConfig(void); +uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData); +uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data); +uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data); +void PN532_ScanForTag(void); +bool PN532_Command(void); +bool Xsns40(uint8_t function); +bool Max4409Read_lum(void); +void Max4409Detect(void); +void Max4409EverySecond(void); +void Max4409Show(bool json); +bool Xsns41(uint8_t function); +void Scd30Detect(void); +void Scd30Update(void); +int Scd30GetCommand(int command_code, uint16_t *pvalue); +int Scd30SetCommand(int command_code, uint16_t value); +bool Scd30CommandSensor(); +void Scd30Show(bool json); +bool Xsns42(byte function); +int hreReadBit(); +char hreReadChar(int &parity_errors); +void hreInit(void); +void hreEvery50ms(void); +void hreShow(boolean json); +bool Xsns43(byte function); +uint8_t sps30_calc_CRC(uint8_t *data); +void sps30_get_data(uint16_t cmd, uint8_t *data, uint8_t dlen); +void sps30_cmd(uint16_t cmd); +void SPS30_Detect(void); +void SPS30_Every_Second(); +void SPS30_Show(bool json); +bool SPS30_cmd(void); +bool Xsns44(byte function); +void Vl53l0Detect(void); +void Vl53l0Every_250MSecond(void); +void Vl53l0Show(boolean json); +bool Xsns45(byte function); +void MLX90614_Init(void); +void MLX90614_Every_Second(void); +void MLX90614_Show(uint8_t json); +bool Xsns46(byte function); +void MAX31865_Init(void); +void MAX31865_GetResult(void); +void MAX31865_Show(bool Json); +bool Xsns47(uint8_t function); +void ChirpWriteI2CRegister(uint8_t addr, uint8_t reg); +uint16_t ChirpFinishReadI2CRegister16bit(uint8_t addr); +void ChirpReset(uint8_t addr); +void ChirpResetAll(void); +void ChirpClockSet(); +void ChirpSleep(uint8_t addr); +void ChirpSelect(uint8_t sensor); +uint8_t ChirpReadVersion(uint8_t addr); +bool ChirpSet(uint8_t addr); +bool ChirpScan(); +void ChirpDetect(void); +void ChirpServiceAllSensors(uint8_t job); +void ChirpEvery100MSecond(void); +void ChirpShow(bool json); +bool ChirpCmd(void); +bool Xsns48(uint8_t function); +void PAJ7620SelectBank(uint8_t bank); +void PAJ7620DecodeGesture(void); +void PAJ7620ReadGesture(void); +void PAJ7620Detect(void); +void PAJ7620Init(void); +void PAJ7620Loop(void); +void PAJ7620Show(bool json); +bool PAJ7620CommandSensor(void); +bool Xsns50(uint8_t function); +void RDM6300_Init(); +void RDM6300_ScanForTag(); +uint8_t rm6300_hexnibble(char chr); +void rm6300_hstring_to_array(uint8_t array[], uint8_t len, char buffer[]); +void RDM6300_Show(void); +bool Xsns51(byte function); +void IBEACON_Init(); +void hm17_every_second(void); +void hm17_sbclr(void); +void hm17_sendcmd(uint8_t cmd); +uint32_t ibeacon_add(struct IBEACON *ib); +void hm17_decode(void); +void IBEACON_loop(); +void IBEACON_Show(void); +bool xsns52_cmd(void); +bool ibeacon_cmd(void); +void ib_sendbeep(void); +void ibeacon_mqtt(const char *mac,const char *rssi); +bool Xsns52(byte function); +double sml_median_array(double *array,uint8_t len); +double sml_median(struct SML_MEDIAN_FILTER* mf, double in); +void ADS1115_init(void); +bool Serial_available(); +uint8_t Serial_read(); +uint8_t Serial_peek(); +void Dump2log(void); +double sml_getvalue(unsigned char *cp,uint8_t index); +uint8_t hexnibble(char chr); +double CharToDouble(const char *str); +void ebus_esc(uint8_t *ebus_buffer, unsigned char len); +uint8_t ebus_crc8(uint8_t data, uint8_t crc_init); +uint8_t ebus_CalculateCRC( uint8_t *Data, uint16_t DataLen ); +void sml_empty_receiver(uint32_t meters); +void sml_shift_in(uint32_t meters,uint32_t shard); +void SML_Poll(void); +void SML_Decode(uint8_t index); +void SML_Immediate_MQTT(const char *mp,uint8_t index,uint8_t mindex); +void SML_Show(boolean json); +void SML_CounterUpd(uint8_t index); +void SML_CounterUpd1(void); +void SML_CounterUpd2(void); +void SML_CounterUpd3(void); +void SML_CounterUpd4(void); +bool Gpio_used(uint8_t gpiopin); +void SML_Init(void); +uint32_t SML_SetBaud(uint32_t meter, uint32_t br); +uint32_t SML_Write(uint32_t meter,char *hstr); +void SetDBGLed(uint8_t srcpin, uint8_t ledpin); +void SML_Counter_Poll(void); +void SML_Check_Send(void); +uint8_t sml_hexnibble(char chr); +void SML_Send_Seq(uint32_t meter,char *seq); +uint16_t MBUS_calculateCRC(uint8_t *frame, uint8_t num); +uint8_t SML_PzemCrc(uint8_t *data, uint8_t len); +uint8_t CalcEvenParity(uint8_t data); +bool XSNS_53_cmd(void); +void InjektCounterValue(uint8_t meter,uint32_t counter); +void SML_CounterSaveState(void); +bool Xsns53(byte function); +static uint32_t _expand_r_shunt(uint16_t compact_r_shunt); +void Ina226SetCalibration(uint8_t slaveIndex); +bool Ina226TestPresence(uint8_t device); +void Ina226ResetActive(void); +void Ina226Init(); +float Ina226ReadBus_v(uint8_t device); +float Ina226ReadShunt_i(uint8_t device); +float Ina226ReadPower_w(uint8_t device); +void Ina226Read(uint8_t device); +void Ina226EverySecond(); +bool Ina226CommandSensor(); +void Ina226Show(bool json); +bool Xsns54(byte callback_id); +bool Hih6Read(void); +void Hih6Detect(void); +void Hih6EverySecond(void); +void Hih6Show(bool json); +bool Xsns55(uint8_t function); +void HpmaSecond(void); +void HpmaInit(void); +void HpmaShow(bool json); +bool Xsns56(uint8_t function); +void Tsl2591Init(void); +bool Tsl2591Read(void); +void Tsl2591EverySecond(void); +void Tsl2591Show(bool json); +bool Xsns57(uint8_t function); +bool Dht12Read(void); +void Dht12Detect(void); +void Dht12EverySecond(void); +void Dht12Show(bool json); +bool Xsns58(uint8_t function); +uint32_t DS1624_Idx2Addr(uint32_t idx); +int DS1624_Restart(uint8_t config, uint32_t idx); +void DS1624_HotPlugUp(uint32_t idx); +void DS1624_HotPlugDown(int idx); +bool DS1624GetTemp(float *value, int idx); +void DS1624HotPlugScan(void); +void DS1624EverySecond(void); +void DS1624Show(bool json); +bool Xsns59(uint8_t function); +void UBXcalcChecksum(char* CK, size_t msgSize); +bool UBXcompareMsgHeader(const char* msgHeader); +void UBXinitCFG(void); +void UBXTriggerTele(void); +void UBXDetect(void); +uint32_t UBXprocessGPS(); +void UBXsendHeader(void); +void UBXsendRecord(uint8_t *buf); +void UBXsendFooter(void); +void UBXsendFile(void); +void UBXSetRate(uint16_t interval); +void UBXSelectMode(uint16_t mode); +bool UBXHandlePOSLLH(); +void UBXHandleSTATUS(); +void UBXHandleTIME(); +void UBXHandleOther(void); +void UBXLoop50msec(void); +void UBXLoop(void); +void UBXShow(bool json); +bool UBXCmd(void); +bool Xsns60(uint8_t function); +bool MINRFinitBLE(uint8_t _mode); +void MINRFhopChannel(); +bool MINRFreceivePacket(void); +void MINRFswapbuf(uint8_t len); +void MINRFwhiten(uint8_t *buf, uint8_t len, uint8_t lfsr); +void MINRFreverseMAC(uint8_t _mac[]); +void MINRFchangePacketModeTo(uint8_t _mode); +void MINRFpurgeFakeSensors(void); +void MINRFhandleFloraPacket(void); +void MINRFhandleMJ_HT_V1Packet(void); +void MINRFhandleLYWSD02Packet(void); +void MINRFhandleLYWSD03Packet(void); +void MINRF_EVERY_50_MSECOND(); +void MINRFShow(bool json); +bool Xsns61(uint8_t function); +void HM10_Launchtask(uint8_t task, uint8_t slot, uint8_t delay); +void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot); +void HM10_Reset(void); +void HM10_Discovery_Scan(void); +void HM10_Read_LYWSD03(void); +void HM10_Read_LYWSD02(void); +void HM10_Time_LYWSD02(void); +void HM10SerialInit(void); +void HM10MACStringToBytes(const char* string, uint8_t _mac[]); +void HM10ParseResponse(char *buf); +void HM10readTempHum(char *_buf); +bool HM10readBat(char *_buf); +bool HM10SerialHandleFeedback(); +void HM10_TaskEvery100ms(); +void HM10EverySecond(); +bool HM10Cmd(void); +void HM10Show(bool json); +bool Xsns62(uint8_t function); +bool begin(); +bool AHT10Read(void); +unsigned char readStatus(void); +void AHT10Detect(void); +void AHT10EverySecond(void); +void AHT10Show(bool json); +bool Xsns64(uint8_t function); +void HandleMetrics(void); +bool Xsns91(uint8_t function); +bool XsnsEnabled(uint32_t sns_index); +void XsnsSensorState(void); +bool XsnsNextCall(uint8_t Function, uint8_t &xsns_index); +bool XsnsCall(uint8_t Function); +bool I2cEnabled(uint32_t i2c_index); +void I2cDriverState(void); +#line 184 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota.ino" +void setup(void) +{ + global_state.data = 3; + + RtcRebootLoad(); + if (!RtcRebootValid()) { + RtcReboot.fast_reboot_count = 0; + } + RtcReboot.fast_reboot_count++; + RtcRebootSave(); + + Serial.begin(APP_BAUDRATE); + seriallog_level = LOG_LEVEL_INFO; + + snprintf_P(my_version, sizeof(my_version), PSTR("%d.%d.%d"), VERSION >> 24 & 0xff, VERSION >> 16 & 0xff, VERSION >> 8 & 0xff); + if (VERSION & 0xff) { + snprintf_P(my_version, sizeof(my_version), PSTR("%s.%d"), my_version, VERSION & 0xff); + } + + snprintf_P(my_image, sizeof(my_image), PSTR("(%s)"), CODE_IMAGE_STR); + + SettingsLoad(); + SettingsDelta(); + + OsWatchInit(); + + GetFeatures(); + + if (1 == RtcReboot.fast_reboot_count) { + UpdateQuickPowerCycle(true); + XdrvCall(FUNC_SETTINGS_OVERRIDE); + } + + + seriallog_level = Settings.seriallog_level; + seriallog_timer = SERIALLOG_TIMER; + syslog_level = Settings.syslog_level; + stop_flash_rotate = Settings.flag.stop_flash_rotate; + save_data_counter = Settings.save_data; + sleep = Settings.sleep; +#ifndef USE_EMULATION + Settings.flag2.emulation = 0; +#else +#ifndef USE_EMULATION_WEMO + if (EMUL_WEMO == Settings.flag2.emulation) { Settings.flag2.emulation = 0; } +#endif +#ifndef USE_EMULATION_HUE + if (EMUL_HUE == Settings.flag2.emulation) { Settings.flag2.emulation = 0; } +#endif +#endif + + if (Settings.param[P_BOOT_LOOP_OFFSET]) { + + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET]) { + Settings.flag3.user_esp8285_enable = 0; + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +1) { + for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { + if (bitRead(Settings.rule_stop, i)) { + bitWrite(Settings.rule_enabled, i, 0); + } + } + } + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +2) { + Settings.rule_enabled = 0; + } + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +3) { + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + Settings.my_gp.io[i] = GPIO_NONE; + } + Settings.my_adc0 = ADC0_NONE; + } + if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +4) { + Settings.module = SONOFF_BASIC; + + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_LOG_SOME_SETTINGS_RESET " (%d)"), RtcReboot.fast_reboot_count); + } + } + + Format(mqtt_client, SettingsText(SET_MQTT_CLIENT), sizeof(mqtt_client)); + Format(mqtt_topic, SettingsText(SET_MQTT_TOPIC), sizeof(mqtt_topic)); + if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); + snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME), mqtt_topic, ESP.getChipId() & 0x1FFF); + } else { + snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME)); + } + + GetEspHardwareType(); + GpioInit(); + + + + WifiConnect(); + + SetPowerOnState(); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_ESP8266_RELEASE), PROJECT, SettingsText(SET_FRIENDLYNAME1), my_version, my_image); +#ifdef FIRMWARE_MINIMAL + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION)); +#endif + + memcpy_P(log_data, VERSION_MARKER, 1); + + RtcInit(); + +#ifdef USE_ARDUINO_OTA + ArduinoOTAInit(); +#endif + + XdrvCall(FUNC_INIT); + XsnsCall(FUNC_INIT); +} + +void BacklogLoop(void) +{ + if (TimeReached(backlog_delay)) { + if (!BACKLOG_EMPTY && !backlog_mutex) { +#ifdef SUPPORT_IF_STATEMENT + backlog_mutex = true; + String cmd = backlog.shift(); + backlog_mutex = false; + ExecuteCommand((char*)cmd.c_str(), SRC_BACKLOG); +#else + backlog_mutex = true; + ExecuteCommand((char*)backlog[backlog_pointer].c_str(), SRC_BACKLOG); + backlog_pointer++; + if (backlog_pointer >= MAX_BACKLOG) { backlog_pointer = 0; } + backlog_mutex = false; +#endif + } + } +} + +void loop(void) +{ + uint32_t my_sleep = millis(); + + XdrvCall(FUNC_LOOP); + XsnsCall(FUNC_LOOP); + + OsWatchLoop(); + + ButtonLoop(); + SwitchLoop(); +#ifdef ROTARY_V1 + RotaryLoop(); +#endif + BacklogLoop(); + + if (TimeReached(state_50msecond)) { + SetNextTimeInterval(state_50msecond, 50); + XdrvCall(FUNC_EVERY_50_MSECOND); + XsnsCall(FUNC_EVERY_50_MSECOND); + } + if (TimeReached(state_100msecond)) { + SetNextTimeInterval(state_100msecond, 100); + Every100mSeconds(); + XdrvCall(FUNC_EVERY_100_MSECOND); + XsnsCall(FUNC_EVERY_100_MSECOND); + } + if (TimeReached(state_250msecond)) { + SetNextTimeInterval(state_250msecond, 250); + Every250mSeconds(); + XdrvCall(FUNC_EVERY_250_MSECOND); + XsnsCall(FUNC_EVERY_250_MSECOND); + } + if (TimeReached(state_second)) { + SetNextTimeInterval(state_second, 1000); + PerformEverySecond(); + XdrvCall(FUNC_EVERY_SECOND); + XsnsCall(FUNC_EVERY_SECOND); + } + + if (!serial_local) { SerialInput(); } + +#ifdef USE_ARDUINO_OTA + ArduinoOtaLoop(); +#endif + + uint32_t my_activity = millis() - my_sleep; + + if (Settings.flag3.sleep_normal) { + + delay(sleep); + } else { + if (my_activity < (uint32_t)sleep) { + delay((uint32_t)sleep - my_activity); + } else { + if (global_state.wifi_down) { + delay(my_activity /2); + } + } + } + + if (!my_activity) { my_activity++; } + uint32_t loop_delay = sleep; + if (!loop_delay) { loop_delay++; } + uint32_t loops_per_second = 1000 / loop_delay; + uint32_t this_cycle_ratio = 100 * my_activity / loop_delay; + loop_load_avg = loop_load_avg - (loop_load_avg / loops_per_second) + (this_cycle_ratio / loops_per_second); +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/sendemail.ino" +#ifdef USE_SENDMAIL + +#include "sendemail.h" +# 25 "C:/shared/sonoff/Git/Tasmota/tasmota/sendemail.ino" +#ifndef SEND_MAIL_MINRAM +#define SEND_MAIL_MINRAM 12*1024 +#endif + +#define xPSTR(a) a + +uint16_t SendMail(char *buffer) { + char *params,*oparams; + const char *mserv; + uint16_t port; + const char *user; + const char *pstr; + const char *passwd; + const char *from; + const char *to; + const char *subject; + const char *cmd; + char auth=0; + uint16_t status=1; + SendEmail *mail=0; + uint16_t blen; + char *endcmd; + + + + uint16_t mem=ESP.getFreeHeap(); + if (memsend(from,to,subject,cmd); + delete mail; + if (result==true) status=0; + } + +exit: + if (oparams) free(oparams); + return status; +} + +void script_send_email_body(BearSSL::WiFiClientSecure_light *client); + + +SendEmail::SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used) : + host(host), port(port), user(user), passwd(passwd), timeout(timeout), ssl(ssl), auth_used(auth_used), client(new BearSSL::WiFiClientSecure_light(1024,1024)) { +} + +String SendEmail::readClient() { + delay(0); + String r = client->readStringUntil('\n'); + + r.trim(); + while (client->available()) { + delay(0); + r += client->readString(); + } + return r; +} + +bool SendEmail::send(const String& from, const String& to, const String& subject, const char *msg) { +bool status=false; +String buffer; + + if (!host.length()) { + return status; + } + + client->setTimeout(timeout); + +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("Connecting: %s on port %d"),host.c_str(),port); +#endif + + if (!client->connect(host.c_str(), port)) { +#ifdef DEBUG_EMAIL_PORT + AddLog_P(LOG_LEVEL_INFO, PSTR("Connection failed")); +#endif + goto exit; + } + + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("220"))) { + goto exit; + } + + buffer = F("EHLO "); + buffer += client->localIP().toString(); + + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("250"))) { + goto exit; + } + if (user.length()>0 && passwd.length()>0 ) { + + buffer = F("AUTH LOGIN"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("334"))) + { + goto exit; + } + base64 b; + buffer = b.encode(user); + + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("334"))) { + goto exit; + } + buffer = b.encode(passwd); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("235"))) { + goto exit; + } + } + + + buffer = F("MAIL FROM:"); + buffer += from; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("250"))) { + goto exit; + } + buffer = F("RCPT TO:"); + buffer += to; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("250"))) { + goto exit; + } + + buffer = F("DATA"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + if (!buffer.startsWith(F("354"))) { + goto exit; + } + buffer = F("From: "); + buffer += from; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = F("To: "); + buffer += to; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + buffer = F("Subject: "); + buffer += subject; + buffer += F("\r\n"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + +#ifdef USE_SCRIPT + if (*msg=='*' && *(msg+1)==0) { + script_send_email_body(client); + } else { + client->println(msg); + } +#else + client->println(msg); +#endif + client->println('.'); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + + buffer = F("QUIT"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); +#endif + + status=true; +exit: + + return status; +} + + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" +#ifndef DOMOTICZ_UPDATE_TIMER +#define DOMOTICZ_UPDATE_TIMER 0 +#endif + +#ifndef EMULATION +#define EMULATION EMUL_NONE +#endif + +#ifndef MTX_ADDRESS1 +#define MTX_ADDRESS1 0 +#endif +#ifndef MTX_ADDRESS2 +#define MTX_ADDRESS2 0 +#endif +#ifndef MTX_ADDRESS3 +#define MTX_ADDRESS3 0 +#endif +#ifndef MTX_ADDRESS4 +#define MTX_ADDRESS4 0 +#endif +#ifndef MTX_ADDRESS5 +#define MTX_ADDRESS5 0 +#endif +#ifndef MTX_ADDRESS6 +#define MTX_ADDRESS6 0 +#endif +#ifndef MTX_ADDRESS7 +#define MTX_ADDRESS7 0 +#endif +#ifndef MTX_ADDRESS8 +#define MTX_ADDRESS8 0 +#endif + +#ifndef HOME_ASSISTANT_DISCOVERY_ENABLE +#define HOME_ASSISTANT_DISCOVERY_ENABLE 0 +#endif + +#ifndef LATITUDE +#define LATITUDE 48.858360 +#endif +#ifndef LONGITUDE +#define LONGITUDE 2.294442 +#endif + +#ifndef WORKING_PERIOD +#define WORKING_PERIOD 5 +#endif + +#ifndef COLOR_TEXT +#define COLOR_TEXT "#000" +#endif +#ifndef COLOR_BACKGROUND +#define COLOR_BACKGROUND "#fff" +#endif +#ifndef COLOR_FORM +#define COLOR_FORM "#f2f2f2" +#endif +#ifndef COLOR_INPUT_TEXT +#define COLOR_INPUT_TEXT "#000" +#endif +#ifndef COLOR_INPUT +#define COLOR_INPUT "#fff" +#endif +#ifndef COLOR_CONSOLE_TEXT +#define COLOR_CONSOLE_TEXT "#000" +#endif +#ifndef COLOR_CONSOLE +#define COLOR_CONSOLE "#fff" +#endif +#ifndef COLOR_TEXT_WARNING +#define COLOR_TEXT_WARNING "#f00" +#endif +#ifndef COLOR_TEXT_SUCCESS +#define COLOR_TEXT_SUCCESS "#008000" +#endif +#ifndef COLOR_BUTTON_TEXT +#define COLOR_BUTTON_TEXT "#fff" +#endif +#ifndef COLOR_BUTTON +#define COLOR_BUTTON "#1fa3ec" +#endif +#ifndef COLOR_BUTTON_HOVER +#define COLOR_BUTTON_HOVER "#0e70a4" +#endif +#ifndef COLOR_BUTTON_RESET +#define COLOR_BUTTON_RESET "#d43535" +#endif +#ifndef COLOR_BUTTON_RESET_HOVER +#define COLOR_BUTTON_RESET_HOVER "#931f1f" +#endif +#ifndef COLOR_BUTTON_SAVE +#define COLOR_BUTTON_SAVE "#47c266" +#endif +#ifndef COLOR_BUTTON_SAVE_HOVER +#define COLOR_BUTTON_SAVE_HOVER "#5aaf6f" +#endif +#ifndef COLOR_TIMER_TAB_TEXT +#define COLOR_TIMER_TAB_TEXT "#fff" +#endif +#ifndef COLOR_TIMER_TAB_BACKGROUND +#define COLOR_TIMER_TAB_BACKGROUND "#999" +#endif +#ifndef COLOR_TITLE_TEXT +#define COLOR_TITLE_TEXT COLOR_TEXT +#endif +#ifndef IR_RCV_MIN_UNKNOWN_SIZE +#define IR_RCV_MIN_UNKNOWN_SIZE 6 +#endif +#ifndef ENERGY_OVERTEMP +#define ENERGY_OVERTEMP 90 +#endif +#ifndef DEFAULT_DIMMER_MAX +#define DEFAULT_DIMMER_MAX 100 +#endif +#ifndef DEFAULT_DIMMER_MIN +#define DEFAULT_DIMMER_MIN 0 +#endif +#ifndef DEFAULT_LIGHT_DIMMER +#define DEFAULT_LIGHT_DIMMER 10 +#endif +#ifndef DEFAULT_LIGHT_COMPONENT +#define DEFAULT_LIGHT_COMPONENT 255 +#endif +#ifndef CORS_ENABLED_ALL +#define CORS_ENABLED_ALL "*" +#endif + + +enum WebColors { + COL_TEXT, COL_BACKGROUND, COL_FORM, + COL_INPUT_TEXT, COL_INPUT, COL_CONSOLE_TEXT, COL_CONSOLE, + COL_TEXT_WARNING, COL_TEXT_SUCCESS, + COL_BUTTON_TEXT, COL_BUTTON, COL_BUTTON_HOVER, COL_BUTTON_RESET, COL_BUTTON_RESET_HOVER, COL_BUTTON_SAVE, COL_BUTTON_SAVE_HOVER, + COL_TIMER_TAB_TEXT, COL_TIMER_TAB_BACKGROUND, COL_TITLE, + COL_LAST }; + +const char kWebColors[] PROGMEM = + COLOR_TEXT "|" COLOR_BACKGROUND "|" COLOR_FORM "|" + COLOR_INPUT_TEXT "|" COLOR_INPUT "|" COLOR_CONSOLE_TEXT "|" COLOR_CONSOLE "|" + COLOR_TEXT_WARNING "|" COLOR_TEXT_SUCCESS "|" + COLOR_BUTTON_TEXT "|" COLOR_BUTTON "|" COLOR_BUTTON_HOVER "|" COLOR_BUTTON_RESET "|" COLOR_BUTTON_RESET_HOVER "|" COLOR_BUTTON_SAVE "|" COLOR_BUTTON_SAVE_HOVER "|" + COLOR_TIMER_TAB_TEXT "|" COLOR_TIMER_TAB_BACKGROUND "|" COLOR_TITLE_TEXT; + +enum TasmotaSerialConfig { + TS_SERIAL_5N1, TS_SERIAL_6N1, TS_SERIAL_7N1, TS_SERIAL_8N1, + TS_SERIAL_5N2, TS_SERIAL_6N2, TS_SERIAL_7N2, TS_SERIAL_8N2, + TS_SERIAL_5E1, TS_SERIAL_6E1, TS_SERIAL_7E1, TS_SERIAL_8E1, + TS_SERIAL_5E2, TS_SERIAL_6E2, TS_SERIAL_7E2, TS_SERIAL_8E2, + TS_SERIAL_5O1, TS_SERIAL_6O1, TS_SERIAL_7O1, TS_SERIAL_8O1, + TS_SERIAL_5O2, TS_SERIAL_6O2, TS_SERIAL_7O2, TS_SERIAL_8O2 }; + +const uint8_t kTasmotaSerialConfig[] PROGMEM = { + SERIAL_5N1, SERIAL_6N1, SERIAL_7N1, SERIAL_8N1, + SERIAL_5N2, SERIAL_6N2, SERIAL_7N2, SERIAL_8N2, + SERIAL_5E1, SERIAL_6E1, SERIAL_7E1, SERIAL_8E1, + SERIAL_5E2, SERIAL_6E2, SERIAL_7E2, SERIAL_8E2, + SERIAL_5O1, SERIAL_6O1, SERIAL_7O1, SERIAL_8O1, + SERIAL_5O2, SERIAL_6O2, SERIAL_7O2, SERIAL_8O2 +}; + + + + + +const uint16_t RTC_MEM_VALID = 0xA55A; + +uint32_t rtc_settings_crc = 0; + +uint32_t GetRtcSettingsCrc(void) +{ + uint32_t crc = 0; + uint8_t *bytes = (uint8_t*)&RtcSettings; + + for (uint32_t i = 0; i < sizeof(RTCMEM); i++) { + crc += bytes[i]*(i+1); + } + return crc; +} + +void RtcSettingsSave(void) +{ + if (GetRtcSettingsCrc() != rtc_settings_crc) { + RtcSettings.valid = RTC_MEM_VALID; + ESP.rtcUserMemoryWrite(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); + rtc_settings_crc = GetRtcSettingsCrc(); + } +} + +void RtcSettingsLoad(void) +{ + ESP.rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); + if (RtcSettings.valid != RTC_MEM_VALID) { + memset(&RtcSettings, 0, sizeof(RTCMEM)); + RtcSettings.valid = RTC_MEM_VALID; + RtcSettings.energy_kWhtoday = Settings.energy_kWhtoday; + RtcSettings.energy_kWhtotal = Settings.energy_kWhtotal; + RtcSettings.energy_usage = Settings.energy_usage; + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + RtcSettings.pulse_counter[i] = Settings.pulse_counter[i]; + } + RtcSettings.power = Settings.power; + RtcSettingsSave(); + } + rtc_settings_crc = GetRtcSettingsCrc(); +} + +bool RtcSettingsValid(void) +{ + return (RTC_MEM_VALID == RtcSettings.valid); +} + + + +uint32_t rtc_reboot_crc = 0; + +uint32_t GetRtcRebootCrc(void) +{ + uint32_t crc = 0; + uint8_t *bytes = (uint8_t*)&RtcReboot; + + for (uint32_t i = 0; i < sizeof(RTCRBT); i++) { + crc += bytes[i]*(i+1); + } + return crc; +} + +void RtcRebootSave(void) +{ + if (GetRtcRebootCrc() != rtc_reboot_crc) { + RtcReboot.valid = RTC_MEM_VALID; + ESP.rtcUserMemoryWrite(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT)); + rtc_reboot_crc = GetRtcRebootCrc(); + } +} + +void RtcRebootReset(void) +{ + RtcReboot.fast_reboot_count = 0; + RtcRebootSave(); +} + +void RtcRebootLoad(void) +{ + ESP.rtcUserMemoryRead(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT)); + if (RtcReboot.valid != RTC_MEM_VALID) { + memset(&RtcReboot, 0, sizeof(RTCRBT)); + RtcReboot.valid = RTC_MEM_VALID; + + RtcRebootSave(); + } + rtc_reboot_crc = GetRtcRebootCrc(); +} + +bool RtcRebootValid(void) +{ + return (RTC_MEM_VALID == RtcReboot.valid); +} + + + + + +extern "C" { +#include "spi_flash.h" +} +#include "eboot_command.h" + +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) + +extern "C" uint32_t _SPIFFS_end; + +const uint32_t SPIFFS_END = ((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; + +#else + +#if AUTOFLASHSIZE + +#include "flash_hal.h" + + +const uint32_t SPIFFS_END = (FS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; + +#else + +extern "C" uint32_t _FS_end; + +const uint32_t SPIFFS_END = ((uint32_t)&_FS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; + +#endif + +#endif + + +const uint32_t SETTINGS_LOCATION = SPIFFS_END; + +const uint8_t CFG_ROTATES = 8; + +uint32_t settings_location = SETTINGS_LOCATION; +uint32_t settings_crc32 = 0; +uint8_t *settings_buffer = nullptr; + + + + + +void SetFlashModeDout(void) +{ + uint8_t *_buffer; + uint32_t address; + + eboot_command ebcmd; + eboot_command_read(&ebcmd); + address = ebcmd.args[0]; + _buffer = new uint8_t[FLASH_SECTOR_SIZE]; + + if (ESP.flashRead(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) { + if (_buffer[2] != 3) { + _buffer[2] = 3; + if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) { + ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); + } + } + } + delete[] _buffer; +} + +bool VersionCompatible(void) +{ + if (Settings.flag3.compatibility_check) { + return true; + } + + eboot_command ebcmd; + eboot_command_read(&ebcmd); + uint32_t start_address = ebcmd.args[0]; + uint32_t end_address = start_address + (ebcmd.args[2] & 0xFFFFF000) + FLASH_SECTOR_SIZE; + uint32_t* buffer = new uint32_t[FLASH_SECTOR_SIZE / 4]; + + uint32_t version[3] = { 0 }; + bool found = false; + for (uint32_t address = start_address; address < end_address; address = address + FLASH_SECTOR_SIZE) { + ESP.flashRead(address, (uint32_t*)buffer, FLASH_SECTOR_SIZE); + if ((address == start_address) && (0x1F == (buffer[0] & 0xFF))) { + version[1] = 0xFFFFFFFF; + found = true; + } else { + for (uint32_t i = 0; i < (FLASH_SECTOR_SIZE / 4); i++) { + version[0] = version[1]; + version[1] = version[2]; + version[2] = buffer[i]; + if ((MARKER_START == version[0]) && (MARKER_END == version[2])) { + found = true; + break; + } + } + } + if (found) { break; } + } + delete[] buffer; + + if (!found) { version[1] = 0; } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("OTA: Version 0x%08X, Compatible 0x%08X"), version[1], VERSION_COMPATIBLE); + + if (version[1] < VERSION_COMPATIBLE) { + uint32_t eboot_magic = 0; + ESP.rtcUserMemoryWrite(0, (uint32_t*)&eboot_magic, sizeof(eboot_magic)); + return false; + } + + return true; +} + +void SettingsBufferFree(void) +{ + if (settings_buffer != nullptr) { + free(settings_buffer); + settings_buffer = nullptr; + } +} + +bool SettingsBufferAlloc(void) +{ + SettingsBufferFree(); + if (!(settings_buffer = (uint8_t *)malloc(sizeof(Settings)))) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_UPLOAD_ERR_2)); + return false; + } + return true; +} + +uint16_t GetCfgCrc16(uint8_t *bytes, uint32_t size) +{ + uint16_t crc = 0; + + for (uint32_t i = 0; i < size; i++) { + if ((i < 14) || (i > 15)) { crc += bytes[i]*(i+1); } + } + return crc; +} + +uint16_t GetSettingsCrc(void) +{ + + uint32_t size = ((Settings.version < 0x06060007) || (Settings.version > 0x0606000A)) ? 3584 : sizeof(SYSCFG); + return GetCfgCrc16((uint8_t*)&Settings, size); +} + +uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size) +{ + + uint32_t crc = 0; + + while (size--) { + crc ^= *bytes++; + for (uint32_t j = 0; j < 8; j++) { + crc = (crc >> 1) ^ (-int(crc & 1) & 0xEDB88320); + } + } + return ~crc; +} + +uint32_t GetSettingsCrc32(void) +{ + return GetCfgCrc32((uint8_t*)&Settings, sizeof(SYSCFG) -4); +} + +void SettingsSaveAll(void) +{ + if (Settings.flag.save_state) { + Settings.power = power; + } else { + Settings.power = 0; + } + XsnsCall(FUNC_SAVE_BEFORE_RESTART); + XdrvCall(FUNC_SAVE_BEFORE_RESTART); + SettingsSave(0); +} + + + + + +void UpdateQuickPowerCycle(bool update) +{ + if (Settings.flag3.fast_power_cycle_disable) { return; } + + uint32_t pc_register; + uint32_t pc_location = SETTINGS_LOCATION - CFG_ROTATES; + + ESP.flashRead(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); + if (update && ((pc_register & 0xFFFFFFF0) == 0xFFA55AB0)) { + uint32_t counter = ((pc_register & 0xF) << 1) & 0xF; + if (0 == counter) { + SettingsErase(3); + EspRestart(); + } else { + pc_register = 0xFFA55AB0 | counter; + ESP.flashWrite(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("QPC: Flag %02X"), counter); + } + } + else if (pc_register != 0xFFA55ABF) { + pc_register = 0xFFA55ABF; + + if (ESP.flashEraseSector(pc_location)) { + ESP.flashWrite(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("QPC: Reset")); + } +} + + + + + +uint32_t GetSettingsTextLen(void) +{ + char* position = Settings.text_pool; + for (uint32_t size = 0; size < SET_MAX; size++) { + while (*position++ != '\0') { } + } + return position - Settings.text_pool; +} + +bool SettingsUpdateText(uint32_t index, const char* replace_me) +{ + if (index >= SET_MAX) { + return false; + } + + + uint32_t replace_len = strlen(replace_me); + char replace[replace_len +1]; + memcpy(replace, replace_me, sizeof(replace)); + + uint32_t start_pos = 0; + uint32_t end_pos = 0; + char* position = Settings.text_pool; + for (uint32_t size = 0; size < SET_MAX; size++) { + while (*position++ != '\0') { } + if (1 == index) { + start_pos = position - Settings.text_pool; + } + else if (0 == index) { + end_pos = position - Settings.text_pool -1; + } + index--; + } + uint32_t char_len = position - Settings.text_pool; + + uint32_t current_len = end_pos - start_pos; + int diff = replace_len - current_len; + + + + + int too_long = (char_len + diff) - settings_text_size; + if (too_long > 0) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_CONFIG "Text overflow by %d char(s)"), too_long); + return false; + } + + if (diff != 0) { + + memmove_P(Settings.text_pool + start_pos + replace_len, Settings.text_pool + end_pos, char_len - end_pos); + } + + memmove_P(Settings.text_pool + start_pos, replace, replace_len); + + memset(Settings.text_pool + char_len + diff, 0x00, settings_text_size - char_len - diff); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d"), GetSettingsTextLen(), settings_text_size); + + return true; +} + +char* SettingsText(uint32_t index) +{ + char* position = Settings.text_pool; + + if (index >= SET_MAX) { + position += settings_text_size -1; + } else { + for (;index > 0; index--) { + while (*position++ != '\0') { } + } + } + return position; +} + + + + + +uint32_t GetSettingsAddress(void) +{ + return settings_location * SPI_FLASH_SEC_SIZE; +} + +void SettingsSave(uint8_t rotate) +{ +# 590 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" +#ifndef FIRMWARE_MINIMAL + if ((GetSettingsCrc32() != settings_crc32) || rotate) { + if (1 == rotate) { + stop_flash_rotate = 1; + } + if (2 == rotate) { + settings_location = SETTINGS_LOCATION +1; + } + if (stop_flash_rotate) { + settings_location = SETTINGS_LOCATION; + } else { + settings_location--; + if (settings_location <= (SETTINGS_LOCATION - CFG_ROTATES)) { + settings_location = SETTINGS_LOCATION; + } + } + + Settings.save_flag++; + if (UtcTime() > START_VALID_TIME) { + Settings.cfg_timestamp = UtcTime(); + } else { + Settings.cfg_timestamp++; + } + Settings.cfg_size = sizeof(SYSCFG); + Settings.cfg_crc = GetSettingsCrc(); + Settings.cfg_crc32 = GetSettingsCrc32(); + + if (ESP.flashEraseSector(settings_location)) { + ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); + } + + if (!stop_flash_rotate && rotate) { + for (uint32_t i = 1; i < CFG_ROTATES; i++) { + ESP.flashEraseSector(settings_location -i); + delay(1); + } + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_SAVED_TO_FLASH_AT " %X, " D_COUNT " %d, " D_BYTES " %d"), settings_location, Settings.save_flag, sizeof(SYSCFG)); + + settings_crc32 = Settings.cfg_crc32; + } +#endif + RtcSettingsSave(); +} + +void SettingsLoad(void) +{ + + struct SYSCFGH { + uint16_t cfg_holder; + uint16_t cfg_size; + unsigned long save_flag; + } _SettingsH; + unsigned long save_flag = 0; + + settings_location = 0; + uint32_t flash_location = SETTINGS_LOCATION +1; + uint16_t cfg_holder = 0; + for (uint32_t i = 0; i < CFG_ROTATES; i++) { + flash_location--; + ESP.flashRead(flash_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); + + bool valid = false; + if (Settings.version > 0x06000000) { + bool almost_valid = (Settings.cfg_crc32 == GetSettingsCrc32()); + if (Settings.version < 0x0606000B) { + almost_valid = (Settings.cfg_crc == GetSettingsCrc()); + } + + if (almost_valid && (0 == cfg_holder)) { cfg_holder = Settings.cfg_holder; } + valid = (cfg_holder == Settings.cfg_holder); + } else { + ESP.flashRead((flash_location -1) * SPI_FLASH_SEC_SIZE, (uint32*)&_SettingsH, sizeof(SYSCFGH)); + valid = (Settings.cfg_holder == _SettingsH.cfg_holder); + } + if (valid) { + if (Settings.save_flag > save_flag) { + save_flag = Settings.save_flag; + settings_location = flash_location; + if (Settings.flag.stop_flash_rotate && (0 == i)) { + break; + } + } + } + + delay(1); + } + if (settings_location > 0) { + ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); + AddLog_P2(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %lu"), settings_location, Settings.save_flag); + } + +#ifndef FIRMWARE_MINIMAL + if (!settings_location || (Settings.cfg_holder != (uint16_t)CFG_HOLDER)) { + SettingsDefault(); + } + settings_crc32 = GetSettingsCrc32(); +#endif + + RtcSettingsLoad(); +} + +void EspErase(uint32_t start_sector, uint32_t end_sector) +{ + bool serial_output = (LOG_LEVEL_DEBUG_MORE <= seriallog_level); + for (uint32_t sector = start_sector; sector < end_sector; sector++) { + + bool result = ESP.flashEraseSector(sector); + + + + if (serial_output) { +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + Serial.printf(D_LOG_APPLICATION D_ERASED_SECTOR " %d %s\n", sector, (result) ? D_OK : D_ERROR); +#else + Serial.printf_P(PSTR(D_LOG_APPLICATION D_ERASED_SECTOR " %d %s\n"), sector, (result) ? D_OK : D_ERROR); +#endif + delay(10); + } else { + yield(); + } + OsWatchLoop(); + } +} + +void SettingsErase(uint8_t type) +{ +# 733 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" +#ifndef FIRMWARE_MINIMAL + uint32_t _sectorStart = (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 1; + uint32_t _sectorEnd = ESP.getFlashChipRealSize() / SPI_FLASH_SEC_SIZE; + if (1 == type) { + + _sectorStart = (ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE) - 4; + } + else if (2 == type) { + _sectorStart = SETTINGS_LOCATION - CFG_ROTATES; + _sectorEnd = SETTINGS_LOCATION +1; + } + else if (3 == type) { + _sectorStart = SETTINGS_LOCATION - CFG_ROTATES; + _sectorEnd = ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " %d " D_UNIT_SECTORS), _sectorEnd - _sectorStart); + + + EsptoolErase(_sectorStart, _sectorEnd); +#endif +} + +void SettingsSdkErase(void) +{ + WiFi.disconnect(true); + SettingsErase(1); + delay(1000); +} + + + +void SettingsDefault(void) +{ + AddLog_P(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_USE_DEFAULTS)); + SettingsDefaultSet1(); + SettingsDefaultSet2(); + SettingsSave(2); +} + +void SettingsDefaultSet1(void) +{ + memset(&Settings, 0x00, sizeof(SYSCFG)); + + Settings.cfg_holder = (uint16_t)CFG_HOLDER; + Settings.cfg_size = sizeof(SYSCFG); + + Settings.version = VERSION; + + +} + +void SettingsDefaultSet2(void) +{ + memset((char*)&Settings +16, 0x00, sizeof(SYSCFG) -16); + + Settings.flag.stop_flash_rotate = APP_FLASH_CYCLE; + Settings.flag.global_state = APP_ENABLE_LEDLINK; + Settings.flag3.sleep_normal = APP_NORMAL_SLEEP; + Settings.flag3.no_power_feedback = APP_NO_RELAY_SCAN; + Settings.flag3.fast_power_cycle_disable = APP_DISABLE_POWERCYCLE; + Settings.flag3.bootcount_update = DEEPSLEEP_BOOTCOUNT; + Settings.flag3.compatibility_check = OTA_COMPATIBILITY; + Settings.save_data = SAVE_DATA; + Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY; + Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; + Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; + Settings.sleep = APP_SLEEP; + if (Settings.sleep < 50) { + Settings.sleep = 50; + } + + + + Settings.interlock[0] = 0xFF; + Settings.module = MODULE; + ModuleDefault(WEMOS); + + SettingsUpdateText(SET_FRIENDLYNAME1, FRIENDLY_NAME); + SettingsUpdateText(SET_FRIENDLYNAME2, FRIENDLY_NAME"2"); + SettingsUpdateText(SET_FRIENDLYNAME3, FRIENDLY_NAME"3"); + SettingsUpdateText(SET_FRIENDLYNAME4, FRIENDLY_NAME"4"); + SettingsUpdateText(SET_OTAURL, OTA_URL); + + + Settings.flag.save_state = SAVE_STATE; + Settings.power = APP_POWER; + Settings.poweronstate = APP_POWERON_STATE; + Settings.blinktime = APP_BLINKTIME; + Settings.blinkcount = APP_BLINKCOUNT; + Settings.ledstate = APP_LEDSTATE; + Settings.ledmask = APP_LEDMASK; + Settings.pulse_timer[0] = APP_PULSETIME; + + + + Settings.serial_config = TS_SERIAL_8N1; + Settings.baudrate = APP_BAUDRATE / 300; + Settings.sbaudrate = SOFT_BAUDRATE / 300; + Settings.serial_delimiter = 0xff; + Settings.seriallog_level = SERIAL_LOG_LEVEL; + + + Settings.flag3.use_wifi_scan = WIFI_SCAN_AT_RESTART; + Settings.flag3.use_wifi_rescan = WIFI_SCAN_REGULARLY; + Settings.wifi_output_power = 170; + ParseIp(&Settings.ip_address[0], WIFI_IP_ADDRESS); + ParseIp(&Settings.ip_address[1], WIFI_GATEWAY); + ParseIp(&Settings.ip_address[2], WIFI_SUBNETMASK); + ParseIp(&Settings.ip_address[3], WIFI_DNS); + Settings.sta_config = WIFI_CONFIG_TOOL; + + SettingsUpdateText(SET_STASSID1, STA_SSID1); + SettingsUpdateText(SET_STASSID2, STA_SSID2); + SettingsUpdateText(SET_STAPWD1, STA_PASS1); + SettingsUpdateText(SET_STAPWD2, STA_PASS2); + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); + + + SettingsUpdateText(SET_SYSLOG_HOST, SYS_LOG_HOST); + Settings.syslog_port = SYS_LOG_PORT; + Settings.syslog_level = SYS_LOG_LEVEL; + + + Settings.flag2.emulation = EMULATION; + Settings.flag3.gui_hostname_ip = GUI_SHOW_HOSTNAME; + Settings.flag3.mdns_enabled = MDNS_ENABLED; + Settings.webserver = WEB_SERVER; + Settings.weblog_level = WEB_LOG_LEVEL; + SettingsUpdateText(SET_WEBPWD, WEB_PASSWORD); + SettingsUpdateText(SET_CORS, CORS_DOMAIN); + + + Settings.flag.button_restrict = KEY_DISABLE_MULTIPRESS; + Settings.flag.button_swap = KEY_SWAP_DOUBLE_PRESS; + Settings.flag.button_single = KEY_ONLY_SINGLE_PRESS; + Settings.param[P_HOLD_TIME] = KEY_HOLD_TIME; + + + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { Settings.switchmode[i] = SWITCH_MODE; } + + + Settings.flag.mqtt_enabled = MQTT_USE; + Settings.flag.mqtt_response = MQTT_RESULT_COMMAND; + Settings.flag.mqtt_offline = MQTT_LWT_MESSAGE; + 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.mqtt_sensor_retain = MQTT_SENSOR_RETAIN; + + Settings.flag.device_index_enable = MQTT_POWER_FORMAT; + Settings.flag3.time_append_timezone = MQTT_APPEND_TIMEZONE; + Settings.flag3.button_switch_force_local = MQTT_BUTTON_SWITCH_FORCE_LOCAL; + Settings.flag3.no_hold_retain = MQTT_NO_HOLD_RETAIN; + Settings.flag3.use_underscore = MQTT_INDEX_SEPARATOR; + Settings.flag3.grouptopic_mode = MQTT_GROUPTOPIC_FORMAT; + SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST); + Settings.mqtt_port = MQTT_PORT; + SettingsUpdateText(SET_MQTT_CLIENT, MQTT_CLIENT_ID); + SettingsUpdateText(SET_MQTT_USER, MQTT_USER); + SettingsUpdateText(SET_MQTT_PWD, MQTT_PASS); + SettingsUpdateText(SET_MQTT_TOPIC, MQTT_TOPIC); + SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC); + SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); + SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC); + SettingsUpdateText(SET_MQTT_FULLTOPIC, MQTT_FULLTOPIC); + Settings.mqtt_retry = MQTT_RETRY_SECS; + SettingsUpdateText(SET_MQTTPREFIX1, SUB_PREFIX); + SettingsUpdateText(SET_MQTTPREFIX2, PUB_PREFIX); + SettingsUpdateText(SET_MQTTPREFIX3, PUB_PREFIX2); + SettingsUpdateText(SET_STATE_TXT1, MQTT_STATUS_OFF); + SettingsUpdateText(SET_STATE_TXT2, MQTT_STATUS_ON); + SettingsUpdateText(SET_STATE_TXT3, MQTT_CMND_TOGGLE); + SettingsUpdateText(SET_STATE_TXT4, MQTT_CMND_HOLD); + char fingerprint[60]; + strlcpy(fingerprint, MQTT_FINGERPRINT1, sizeof(fingerprint)); + char *p = fingerprint; + for (uint32_t i = 0; i < 20; i++) { + Settings.mqtt_fingerprint[0][i] = strtol(p, &p, 16); + } + strlcpy(fingerprint, MQTT_FINGERPRINT2, sizeof(fingerprint)); + p = fingerprint; + for (uint32_t i = 0; i < 20; i++) { + Settings.mqtt_fingerprint[1][i] = strtol(p, &p, 16); + } + Settings.tele_period = TELE_PERIOD; + Settings.mqttlog_level = MQTT_LOG_LEVEL; + + + Settings.flag.no_power_on_check = ENERGY_VOLTAGE_ALWAYS; + Settings.flag2.current_resolution = 3; + + + Settings.flag2.energy_resolution = ENERGY_RESOLUTION; + Settings.flag3.dds2382_model = ENERGY_DDS2382_MODE; + Settings.flag3.hardware_energy_total = ENERGY_HARDWARE_TOTALS; + Settings.param[P_MAX_POWER_RETRY] = MAX_POWER_RETRY; + + Settings.energy_power_calibration = HLW_PREF_PULSE; + Settings.energy_voltage_calibration = HLW_UREF_PULSE; + Settings.energy_current_calibration = HLW_IREF_PULSE; +# 944 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" + Settings.energy_max_power_limit_hold = MAX_POWER_HOLD; + Settings.energy_max_power_limit_window = MAX_POWER_WINDOW; + + Settings.energy_max_power_safe_limit_hold = SAFE_POWER_HOLD; + Settings.energy_max_power_safe_limit_window = SAFE_POWER_WINDOW; + + + + RtcSettings.energy_kWhtotal = 0; + + memset((char*)&RtcSettings.energy_usage, 0x00, sizeof(RtcSettings.energy_usage)); + Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP; + + + Settings.flag.ir_receive_decimal = IR_DATA_RADIX; + Settings.flag3.receive_raw = IR_ADD_RAW_DATA; + Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; + + + Settings.flag.rf_receive_decimal = RF_DATA_RADIX; + + memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9); + + + Settings.domoticz_update_timer = DOMOTICZ_UPDATE_TIMER; +# 979 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" + Settings.flag.temperature_conversion = TEMP_CONVERSION; + Settings.flag.pressure_conversion = PRESSURE_CONVERSION; + Settings.flag2.pressure_resolution = PRESSURE_RESOLUTION; + Settings.flag2.humidity_resolution = HUMIDITY_RESOLUTION; + Settings.flag2.temperature_resolution = TEMP_RESOLUTION; + Settings.flag3.ds18x20_internal_pullup = DS18X20_PULL_UP; + Settings.flag3.counter_reset_on_tele = COUNTER_RESET; + + + + + + + Settings.flag2.calc_resolution = CALC_RESOLUTION; + + + Settings.flag3.timers_enable = TIMERS_ENABLED; + + + Settings.flag.hass_light = HASS_AS_LIGHT; + Settings.flag.hass_discovery = HOME_ASSISTANT_DISCOVERY_ENABLE; + Settings.flag3.hass_tele_on_power = TELE_ON_POWER; + + + Settings.flag.knx_enabled = KNX_ENABLED; + Settings.flag.knx_enable_enhancement = KNX_ENHANCED; + + + Settings.flag.pwm_control = LIGHT_MODE; + Settings.flag.ws_clock_reverse = LIGHT_CLOCK_DIRECTION; + Settings.flag.light_signal = LIGHT_PAIRS_CO2; + Settings.flag.not_power_linked = LIGHT_POWER_CONTROL; + Settings.flag.decimal_text = LIGHT_COLOR_RADIX; + Settings.flag3.pwm_multi_channels = LIGHT_CHANNEL_MODE; + Settings.flag3.slider_dimmer_stay_on = LIGHT_SLIDER_POWER; + Settings.flag4.alexa_ct_range = LIGHT_ALEXA_CT_RANGE; + + Settings.pwm_frequency = PWM_FREQ; + Settings.pwm_range = PWM_RANGE; + for (uint32_t i = 0; i < MAX_PWMS; i++) { + Settings.light_color[i] = DEFAULT_LIGHT_COMPONENT; + + } + Settings.light_correction = 1; + Settings.light_dimmer = DEFAULT_LIGHT_DIMMER; + + Settings.light_speed = 1; + + Settings.light_width = 1; + + Settings.light_pixels = WS2812_LEDS; + + Settings.ws_width[WS_SECOND] = 1; + Settings.ws_color[WS_SECOND][WS_RED] = 255; + + Settings.ws_color[WS_SECOND][WS_BLUE] = 255; + Settings.ws_width[WS_MINUTE] = 3; + + Settings.ws_color[WS_MINUTE][WS_GREEN] = 255; + + Settings.ws_width[WS_HOUR] = 5; + Settings.ws_color[WS_HOUR][WS_RED] = 255; + + + + Settings.dimmer_hw_max = DEFAULT_DIMMER_MAX; + Settings.dimmer_hw_min = DEFAULT_DIMMER_MIN; + + + + Settings.display_mode = 1; + Settings.display_refresh = 2; + Settings.display_rows = 2; + Settings.display_cols[0] = 16; + Settings.display_cols[1] = 8; + Settings.display_dimmer = 1; + Settings.display_size = 1; + Settings.display_font = 1; + + Settings.display_address[0] = MTX_ADDRESS1; + Settings.display_address[1] = MTX_ADDRESS2; + Settings.display_address[2] = MTX_ADDRESS3; + Settings.display_address[3] = MTX_ADDRESS4; + Settings.display_address[4] = MTX_ADDRESS5; + Settings.display_address[5] = MTX_ADDRESS6; + Settings.display_address[6] = MTX_ADDRESS7; + Settings.display_address[7] = MTX_ADDRESS8; + + + if (((APP_TIMEZONE > -14) && (APP_TIMEZONE < 15)) || (99 == APP_TIMEZONE)) { + Settings.timezone = APP_TIMEZONE; + Settings.timezone_minutes = 0; + } else { + Settings.timezone = APP_TIMEZONE / 60; + Settings.timezone_minutes = abs(APP_TIMEZONE % 60); + } + SettingsUpdateText(SET_NTPSERVER1, NTP_SERVER1); + SettingsUpdateText(SET_NTPSERVER2, NTP_SERVER2); + SettingsUpdateText(SET_NTPSERVER3, NTP_SERVER3); + for (uint32_t i = 0; i < MAX_NTP_SERVERS; i++) { + SettingsUpdateText(SET_NTPSERVER1 +i, ReplaceCommaWithDot(SettingsText(SET_NTPSERVER1 +i))); + } + Settings.latitude = (int)((double)LATITUDE * 1000000); + Settings.longitude = (int)((double)LONGITUDE * 1000000); + SettingsResetStd(); + SettingsResetDst(); + + Settings.button_debounce = KEY_DEBOUNCE_TIME; + Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; + + for (uint32_t j = 0; j < 5; j++) { + Settings.rgbwwTable[j] = 255; + } + + Settings.novasds_startingoffset = STARTING_OFFSET; + + SettingsDefaultWebColor(); + + memset(&Settings.monitors, 0xFF, 20); + SettingsEnableAllI2cDrivers(); + + + Settings.flag3.tuya_apply_o20 = TUYA_SETOPTION_20; + Settings.flag3.tuya_serial_mqtt_publish = MQTT_TUYA_RECEIVED; + + Settings.flag3.buzzer_enable = BUZZER_ENABLE; + Settings.flag3.shutter_mode = SHUTTER_SUPPORT; + Settings.flag3.pcf8574_ports_inverted = PCF8574_INVERT_PORTS; + Settings.flag4.zigbee_use_names = ZIGBEE_FRIENDLY_NAMES; +} + + + +void SettingsResetStd(void) +{ + Settings.tflag[0].hemis = TIME_STD_HEMISPHERE; + Settings.tflag[0].week = TIME_STD_WEEK; + Settings.tflag[0].dow = TIME_STD_DAY; + Settings.tflag[0].month = TIME_STD_MONTH; + Settings.tflag[0].hour = TIME_STD_HOUR; + Settings.toffset[0] = TIME_STD_OFFSET; +} + +void SettingsResetDst(void) +{ + Settings.tflag[1].hemis = TIME_DST_HEMISPHERE; + Settings.tflag[1].week = TIME_DST_WEEK; + Settings.tflag[1].dow = TIME_DST_DAY; + Settings.tflag[1].month = TIME_DST_MONTH; + Settings.tflag[1].hour = TIME_DST_HOUR; + Settings.toffset[1] = TIME_DST_OFFSET; +} + +void SettingsDefaultWebColor(void) +{ + char scolor[10]; + for (uint32_t i = 0; i < COL_LAST; i++) { + WebHexCode(i, GetTextIndexed(scolor, sizeof(scolor), i, kWebColors)); + } +} + +void SettingsEnableAllI2cDrivers(void) +{ + Settings.i2c_drivers[0] = 0xFFFFFFFF; + Settings.i2c_drivers[1] = 0xFFFFFFFF; + Settings.i2c_drivers[2] = 0xFFFFFFFF; +} + + + +void SettingsDelta(void) +{ + if (Settings.version != VERSION) { + if (Settings.version < 0x06000000) { + Settings.cfg_size = sizeof(SYSCFG); + Settings.cfg_crc = GetSettingsCrc(); + } + if (Settings.version < 0x06000002) { + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + if (i < 4) { + Settings.switchmode[i] = Settings.interlock[i]; + } else { + Settings.switchmode[i] = SWITCH_MODE; + } + } + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + if (Settings.my_gp.io[i] >= GPIO_SWT5) { + Settings.my_gp.io[i] += 4; + } + } + } + if (Settings.version < 0x06000003) { + Settings.flag.mqtt_serial_raw = 0; + Settings.flag.pressure_conversion = 0; + Settings.flag3.data = 0; + } + if (Settings.version < 0x06010103) { + Settings.flag3.timers_enable = 1; + } + if (Settings.version < 0x0601010C) { + Settings.button_debounce = KEY_DEBOUNCE_TIME; + Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; + } + if (Settings.version < 0x0602010A) { + for (uint32_t j = 0; j < 5; j++) { + Settings.rgbwwTable[j] = 255; + } + } + if (Settings.version < 0x06030002) { + Settings.timezone_minutes = 0; + } + if (Settings.version < 0x06030004) { + memset(&Settings.monitors, 0xFF, 20); + } + if (Settings.version < 0x0603000E) { + Settings.flag2.calc_resolution = CALC_RESOLUTION; + } + if (Settings.version < 0x0603000F) { + if (Settings.sleep < 50) { + Settings.sleep = 50; + } + } + if (Settings.version < 0x06040105) { + Settings.flag3.mdns_enabled = MDNS_ENABLED; + Settings.param[P_MDNS_DELAYED_START] = 0; + } + if (Settings.version < 0x0604010B) { + Settings.interlock[0] = 0xFF; + for (uint32_t i = 1; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } + } + if (Settings.version < 0x0604010D) { + Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; + } + if (Settings.version < 0x06040110) { + ModuleDefault(WEMOS); + } + if (Settings.version < 0x06040113) { + Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; + } + if (Settings.version < 0x06050003) { + Settings.novasds_startingoffset = STARTING_OFFSET; + } + if (Settings.version < 0x06050006) { + SettingsDefaultWebColor(); + } + if (Settings.version < 0x06050007) { + Settings.ledmask = APP_LEDMASK; + } + if (Settings.version < 0x0605000A) { + Settings.my_adc0 = ADC0_NONE; + } + if (Settings.version < 0x0605000D) { + Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; + } + if (Settings.version < 0x06060001) { + Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP; + } + if (Settings.version < 0x06060007) { + memset((char*)&Settings +0xE00, 0x00, sizeof(SYSCFG) -0xE00); + } + if (Settings.version < 0x06060008) { + + if (Settings.flag3.tuya_serial_mqtt_publish) { + Settings.param[P_ex_DIMMER_MAX] = 100; + } else { + Settings.param[P_ex_DIMMER_MAX] = 255; + } + } + if (Settings.version < 0x06060009) { + Settings.baudrate = APP_BAUDRATE / 300; + Settings.sbaudrate = SOFT_BAUDRATE / 300; + } + if (Settings.version < 0x0606000A) { + uint8_t tuyaindex = 0; + if (Settings.param[P_BACKLOG_DELAY] > 0) { + Settings.tuya_fnid_map[tuyaindex].fnid = 21; + Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_BACKLOG_DELAY]; + tuyaindex++; + } else if (Settings.flag3.fast_power_cycle_disable == 1) { + Settings.tuya_fnid_map[tuyaindex].fnid = 11; + Settings.tuya_fnid_map[tuyaindex].dpid = 1; + tuyaindex++; + } + if (Settings.param[P_ex_TUYA_RELAYS] > 0) { + for (uint8_t i = 0 ; i < Settings.param[P_ex_TUYA_RELAYS]; i++) { + Settings.tuya_fnid_map[tuyaindex].fnid = 12 + i; + Settings.tuya_fnid_map[tuyaindex].dpid = i + 2; + tuyaindex++; + } + } + if (Settings.param[P_ex_TUYA_POWER_ID] > 0) { + Settings.tuya_fnid_map[tuyaindex].fnid = 31; + Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_POWER_ID]; + tuyaindex++; + } + if (Settings.param[P_ex_TUYA_VOLTAGE_ID] > 0) { + Settings.tuya_fnid_map[tuyaindex].fnid = 33; + Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_VOLTAGE_ID]; + tuyaindex++; + } + if (Settings.param[P_ex_TUYA_CURRENT_ID] > 0) { + Settings.tuya_fnid_map[tuyaindex].fnid = 32; + Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_CURRENT_ID]; + } + } + if (Settings.version < 0x0606000C) { + memset((char*)&Settings +0x1D6, 0x00, 16); + } + if (Settings.version < 0x0606000F) { + Settings.ex_shutter_accuracy = 0; + Settings.ex_mqttlog_level = MQTT_LOG_LEVEL; + } + if (Settings.version < 0x06060011) { + Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY; + } + if (Settings.version < 0x06060012) { + Settings.dimmer_hw_min = DEFAULT_DIMMER_MIN; + Settings.dimmer_hw_max = DEFAULT_DIMMER_MAX; + if (TUYA_DIMMER == Settings.module) { + if (Settings.flag3.ex_tuya_dimmer_min_limit) { + Settings.dimmer_hw_min = 25; + } else { + Settings.dimmer_hw_min = 1; + } + Settings.dimmer_hw_max = Settings.param[P_ex_DIMMER_MAX]; + } + else if (PS_16_DZ == Settings.module) { + Settings.dimmer_hw_min = 10; + Settings.dimmer_hw_max = Settings.param[P_ex_DIMMER_MAX]; + } + } + if (Settings.version < 0x06060014) { +# 1323 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" + Settings.flag3.fast_power_cycle_disable = 0; + Settings.energy_power_delta = Settings.ex_energy_power_delta; + Settings.ex_energy_power_delta = 0; + } + if (Settings.version < 0x06060015) { + if ((EX_WIFI_SMARTCONFIG == Settings.ex_sta_config) || (EX_WIFI_WPSCONFIG == Settings.ex_sta_config)) { + Settings.ex_sta_config = WIFI_MANAGER; + } + } + + if (Settings.version < 0x07000002) { + Settings.web_color2[0][0] = Settings.web_color[0][0]; + Settings.web_color2[0][1] = Settings.web_color[0][1]; + Settings.web_color2[0][2] = Settings.web_color[0][2]; + } + if (Settings.version < 0x07000003) { + SettingsEnableAllI2cDrivers(); + } + if (Settings.version < 0x07000004) { + Settings.ex_wifi_output_power = 170; + } + if (Settings.version < 0x07010202) { + Settings.ex_serial_config = TS_SERIAL_8N1; + } + if (Settings.version < 0x07010204) { + if (Settings.flag3.ex_cors_enabled == 1) { + strlcpy(Settings.ex_cors_domain, CORS_ENABLED_ALL, sizeof(Settings.ex_cors_domain)); + } else { + Settings.ex_cors_domain[0] = 0; + } + } + if (Settings.version < 0x07010205) { + Settings.seriallog_level = Settings.ex_seriallog_level; + Settings.sta_config = Settings.ex_sta_config; + Settings.sta_active = Settings.ex_sta_active; + memcpy((char*)&Settings.rule_stop, (char*)&Settings.ex_rule_stop, 47); + } + if (Settings.version < 0x07010206) { + Settings.flag4 = Settings.ex_flag4; + Settings.mqtt_port = Settings.ex_mqtt_port; + memcpy((char*)&Settings.serial_config, (char*)&Settings.ex_serial_config, 5); + } + + if (Settings.version < 0x08000000) { + char temp[strlen(Settings.text_pool) +1]; strncpy(temp, Settings.text_pool, sizeof(temp)); + char temp21[strlen(Settings.ex_mqtt_prefix[0]) +1]; strncpy(temp21, Settings.ex_mqtt_prefix[0], sizeof(temp21)); + char temp22[strlen(Settings.ex_mqtt_prefix[1]) +1]; strncpy(temp22, Settings.ex_mqtt_prefix[1], sizeof(temp22)); + char temp23[strlen(Settings.ex_mqtt_prefix[2]) +1]; strncpy(temp23, Settings.ex_mqtt_prefix[2], sizeof(temp23)); + char temp31[strlen(Settings.ex_sta_ssid[0]) +1]; strncpy(temp31, Settings.ex_sta_ssid[0], sizeof(temp31)); + char temp32[strlen(Settings.ex_sta_ssid[1]) +1]; strncpy(temp32, Settings.ex_sta_ssid[1], sizeof(temp32)); + char temp41[strlen(Settings.ex_sta_pwd[0]) +1]; strncpy(temp41, Settings.ex_sta_pwd[0], sizeof(temp41)); + char temp42[strlen(Settings.ex_sta_pwd[1]) +1]; strncpy(temp42, Settings.ex_sta_pwd[1], sizeof(temp42)); + char temp5[strlen(Settings.ex_hostname) +1]; strncpy(temp5, Settings.ex_hostname, sizeof(temp5)); + char temp6[strlen(Settings.ex_syslog_host) +1]; strncpy(temp6, Settings.ex_syslog_host, sizeof(temp6)); + char temp7[strlen(Settings.ex_mqtt_host) +1]; strncpy(temp7, Settings.ex_mqtt_host, sizeof(temp7)); + char temp8[strlen(Settings.ex_mqtt_client) +1]; strncpy(temp8, Settings.ex_mqtt_client, sizeof(temp8)); + char temp9[strlen(Settings.ex_mqtt_user) +1]; strncpy(temp9, Settings.ex_mqtt_user, sizeof(temp9)); + char temp10[strlen(Settings.ex_mqtt_pwd) +1]; strncpy(temp10, Settings.ex_mqtt_pwd, sizeof(temp10)); + char temp11[strlen(Settings.ex_mqtt_topic) +1]; strncpy(temp11, Settings.ex_mqtt_topic, sizeof(temp11)); + char temp12[strlen(Settings.ex_button_topic) +1]; strncpy(temp12, Settings.ex_button_topic, sizeof(temp12)); + char temp13[strlen(Settings.ex_mqtt_grptopic) +1]; strncpy(temp13, Settings.ex_mqtt_grptopic, sizeof(temp13)); + + memset(Settings.text_pool, 0x00, settings_text_size); + SettingsUpdateText(SET_OTAURL, temp); + SettingsUpdateText(SET_MQTTPREFIX1, temp21); + SettingsUpdateText(SET_MQTTPREFIX2, temp22); + SettingsUpdateText(SET_MQTTPREFIX3, temp23); + SettingsUpdateText(SET_STASSID1, temp31); + SettingsUpdateText(SET_STASSID2, temp32); + SettingsUpdateText(SET_STAPWD1, temp41); + SettingsUpdateText(SET_STAPWD2, temp42); + SettingsUpdateText(SET_HOSTNAME, temp5); + SettingsUpdateText(SET_SYSLOG_HOST, temp6); +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + if (!strlen(Settings.ex_mqtt_user)) { + SettingsUpdateText(SET_MQTT_HOST, temp7); + SettingsUpdateText(SET_MQTT_USER, temp9); + } else { + char aws_mqtt_host[66]; + snprintf_P(aws_mqtt_host, sizeof(aws_mqtt_host), PSTR("%s%s"), temp9, temp7); + SettingsUpdateText(SET_MQTT_HOST, aws_mqtt_host); + SettingsUpdateText(SET_MQTT_USER, ""); + } +#else + SettingsUpdateText(SET_MQTT_HOST, temp7); + SettingsUpdateText(SET_MQTT_USER, temp9); +#endif + SettingsUpdateText(SET_MQTT_CLIENT, temp8); + SettingsUpdateText(SET_MQTT_PWD, temp10); + SettingsUpdateText(SET_MQTT_TOPIC, temp11); + SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, temp12); + SettingsUpdateText(SET_MQTT_GRP_TOPIC, temp13); + + SettingsUpdateText(SET_WEBPWD, Settings.ex_web_password); + SettingsUpdateText(SET_CORS, Settings.ex_cors_domain); + SettingsUpdateText(SET_MQTT_FULLTOPIC, Settings.ex_mqtt_fulltopic); + SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, Settings.ex_switch_topic); + SettingsUpdateText(SET_STATE_TXT1, Settings.ex_state_text[0]); + SettingsUpdateText(SET_STATE_TXT2, Settings.ex_state_text[1]); + SettingsUpdateText(SET_STATE_TXT3, Settings.ex_state_text[2]); + SettingsUpdateText(SET_STATE_TXT4, Settings.ex_state_text[3]); + SettingsUpdateText(SET_NTPSERVER1, Settings.ex_ntp_server[0]); + SettingsUpdateText(SET_NTPSERVER2, Settings.ex_ntp_server[1]); + SettingsUpdateText(SET_NTPSERVER3, Settings.ex_ntp_server[2]); + SettingsUpdateText(SET_MEM1, Settings.script_pram[0]); + SettingsUpdateText(SET_MEM2, Settings.script_pram[1]); + SettingsUpdateText(SET_MEM3, Settings.script_pram[2]); + SettingsUpdateText(SET_MEM4, Settings.script_pram[3]); + SettingsUpdateText(SET_MEM5, Settings.script_pram[4]); + SettingsUpdateText(SET_FRIENDLYNAME1, Settings.ex_friendlyname[0]); + SettingsUpdateText(SET_FRIENDLYNAME2, Settings.ex_friendlyname[1]); + SettingsUpdateText(SET_FRIENDLYNAME3, Settings.ex_friendlyname[2]); + SettingsUpdateText(SET_FRIENDLYNAME4, Settings.ex_friendlyname[3]); + } + + Settings.version = VERSION; + SettingsSave(1); + } +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support.ino" +IPAddress syslog_host_addr; +uint32_t syslog_host_hash = 0; + +extern "C" { +extern struct rst_info resetInfo; +} + + + + + +#include + +Ticker tickerOSWatch; + +const uint32_t OSWATCH_RESET_TIME = 120; + +static unsigned long oswatch_last_loop_time; +uint8_t oswatch_blocked_loop = 0; + +#ifndef USE_WS2812_DMA + +#endif + +#ifdef USE_KNX +bool knx_started = false; +#endif + +void OsWatchTicker(void) +{ + uint32_t t = millis(); + uint32_t last_run = abs(t - oswatch_last_loop_time); + +#ifdef DEBUG_THEO + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d %% (%d dBm), last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI(), last_run); +#endif + if (last_run >= (OSWATCH_RESET_TIME * 1000)) { + + RtcSettings.oswatch_blocked_loop = 1; + RtcSettingsSave(); + + + + + + volatile uint32_t dummy; + dummy = *((uint32_t*) 0x00000000); + } +} + +void OsWatchInit(void) +{ + oswatch_blocked_loop = RtcSettings.oswatch_blocked_loop; + RtcSettings.oswatch_blocked_loop = 0; + oswatch_last_loop_time = millis(); + tickerOSWatch.attach_ms(((OSWATCH_RESET_TIME / 3) * 1000), OsWatchTicker); +} + +void OsWatchLoop(void) +{ + oswatch_last_loop_time = millis(); + +} + +bool OsWatchBlockedLoop(void) +{ + return oswatch_blocked_loop; +} + +uint32_t ResetReason(void) +{ +# 101 "C:/shared/sonoff/Git/Tasmota/tasmota/support.ino" + return resetInfo.reason; +} + +String GetResetReason(void) +{ + if (oswatch_blocked_loop) { + char buff[32]; + strncpy_P(buff, PSTR(D_JSON_BLOCKED_LOOP), sizeof(buff)); + return String(buff); + } else { + return ESP.getResetReason(); + } +} + + + + + + +size_t strchrspn(const char *str1, int character) +{ + size_t ret = 0; + char *start = (char*)str1; + char *end = strchr(str1, character); + if (end) ret = end - start; + return ret; +} + + +char* subStr(char* dest, char* str, const char *delim, int index) +{ + char *act; + char *sub = nullptr; + char *ptr; + int i; + + + strncpy(dest, str, strlen(str)+1); + for (i = 1, act = dest; i <= index; i++, act = nullptr) { + sub = strtok_r(act, delim, &ptr); + if (sub == nullptr) break; + } + sub = Trim(sub); + return sub; +} + +float CharToFloat(const char *str) +{ + + char strbuf[24]; + + strlcpy(strbuf, str, sizeof(strbuf)); + char *pt = strbuf; + while ((*pt != '\0') && isblank(*pt)) { pt++; } + + signed char sign = 1; + if (*pt == '-') { sign = -1; } + if (*pt == '-' || *pt=='+') { pt++; } + + float left = 0; + if (*pt != '.') { + left = atoi(pt); + while (isdigit(*pt)) { pt++; } + } + + float right = 0; + if (*pt == '.') { + pt++; + right = atoi(pt); + while (isdigit(*pt)) { + pt++; + right /= 10.0f; + } + } + + float result = left + right; + if (sign < 0) { + return -result; + } + return result; +} + +int TextToInt(char *str) +{ + char *p; + uint8_t radix = 10; + if ('#' == str[0]) { + radix = 16; + str++; + } + return strtol(str, &p, radix); +} + +char* ulltoa(unsigned long long value, char *str, int radix) +{ + char digits[64]; + char *dst = str; + int i = 0; + + + + do { + int n = value % radix; + digits[i++] = (n < 10) ? (char)n+'0' : (char)n-10+'A'; + value /= radix; + } while (value != 0); + + while (i > 0) { *dst++ = digits[--i]; } + + *dst = 0; + return str; +} + + + +char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween) +{ + + + + static const char * hex = "0123456789ABCDEF"; + int between = (inbetween) ? 3 : 2; + const unsigned char * pin = in; + char * pout = out; + for (; pin < in+insz; pout += between, pin++) { + pout[0] = hex[(pgm_read_byte(pin)>>4) & 0xF]; + pout[1] = hex[ pgm_read_byte(pin) & 0xF]; + if (inbetween) { pout[2] = inbetween; } + if (pout + 3 - out > outsz) { break; } + } + pout[(inbetween && insz) ? -1 : 0] = 0; + return out; +} + +char* Uint64toHex(uint64_t value, char *str, uint16_t bits) +{ + ulltoa(value, str, 16); + + int fill = 8; + if ((bits > 3) && (bits < 65)) { + fill = bits / 4; + if (bits % 4) { fill++; } + } + int len = strlen(str); + fill -= len; + if (fill > 0) { + memmove(str + fill, str, len +1); + memset(str, '0', fill); + } + return str; +} + +char* dtostrfd(double number, unsigned char prec, char *s) +{ + if ((isnan(number)) || (isinf(number))) { + strcpy(s, "null"); + return s; + } else { + return dtostrf(number, 1, prec, s); + } +} + +char* Unescape(char* buffer, uint32_t* size) +{ + uint8_t* read = (uint8_t*)buffer; + uint8_t* write = (uint8_t*)buffer; + int32_t start_size = *size; + int32_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; + case 'a': che = '\a'; break; + case 'b': che = '\b'; break; + case 'e': che = '\e'; break; + case 'f': che = '\f'; break; + case 'n': che = '\n'; break; + case 'r': che = '\r'; break; + case 's': che = ' '; break; + case 't': che = '\t'; break; + case 'v': che = '\v'; break; + case 'x': { + uint8_t* start = read; + che = (uint8_t)strtol((const char*)read, (char**)&read, 16); + start_size -= (uint16_t)(read - start); + end_size -= (uint16_t)(read - start); + break; + } + case '"': che = '\"'; break; + + default : { + che = chi; + *write++ = ch; + end_size++; + } + } + *write++ = che; + } + } + } + *size = end_size; + *write++ = 0; + + + return buffer; +} + +char* RemoveSpace(char* p) +{ + char* write = p; + char* read = p; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + if (!isspace(ch)) { + *write++ = ch; + } + } + + return p; +} + +char* ReplaceCommaWithDot(char* p) +{ + char* write = (char*)p; + char* read = (char*)p; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + if (ch == ',') { + ch = '.'; + } + *write++ = ch; + } + return p; +} + +char* LowerCase(char* dest, const char* source) +{ + char* write = dest; + const char* read = source; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + *write++ = tolower(ch); + } + return dest; +} + +char* UpperCase(char* dest, const char* source) +{ + char* write = dest; + const char* read = source; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + *write++ = toupper(ch); + } + return dest; +} + +char* UpperCase_P(char* dest, const char* source) +{ + char* write = dest; + const char* read = source; + char ch = '.'; + + while (ch != '\0') { + ch = pgm_read_byte(read++); + *write++ = toupper(ch); + } + return dest; +} + +char* Trim(char* p) +{ + while ((*p != '\0') && isblank(*p)) { p++; } + char* q = p + strlen(p) -1; + while ((q >= p) && isblank(*q)) { q--; } + q++; + *q = '\0'; + return p; +} + +char* RemoveAllSpaces(char* p) +{ + + char *cursor = p; + uint32_t offset = 0; + while (1) { + *cursor = *(cursor + offset); + if ((' ' == *cursor) || ('\t' == *cursor) || ('\n' == *cursor)) { + offset++; + } else { + if (0 == *cursor) { break; } + cursor++; + } + } + return p; +} + +char* NoAlNumToUnderscore(char* dest, const char* source) +{ + char* write = dest; + const char* read = source; + char ch = '.'; + + while (ch != '\0') { + ch = *read++; + *write++ = (isalnum(ch) || ('\0' == ch)) ? ch : '_'; + } + return dest; +} + +char IndexSeparator(void) +{ + + + + + + + if (Settings.flag3.use_underscore) { + return '_'; + } else { + return '-'; + } +} + +void SetShortcutDefault(void) +{ + if ('\0' != XdrvMailbox.data[0]) { + XdrvMailbox.data[0] = '0' + SC_DEFAULT; + XdrvMailbox.data[1] = '\0'; + } +} + +uint8_t Shortcut(void) +{ + uint8_t result = 10; + + if ('\0' == XdrvMailbox.data[1]) { + if (('"' == XdrvMailbox.data[0]) || ('0' == XdrvMailbox.data[0])) { + result = SC_CLEAR; + } else { + result = atoi(XdrvMailbox.data); + if (0 == result) { + result = 10; + } + } + } + return result; +} + +bool ValidIpAddress(const char* str) +{ + const char* p = str; + + while (*p && ((*p == '.') || ((*p >= '0') && (*p <= '9')))) { p++; } + return (*p == '\0'); +} + +bool ParseIp(uint32_t* addr, const char* str) +{ + uint8_t *part = (uint8_t*)addr; + uint8_t i; + + *addr = 0; + for (i = 0; i < 4; i++) { + part[i] = strtoul(str, nullptr, 10); + str = strchr(str, '.'); + if (str == nullptr || *str == '\0') { + break; + } + str++; + } + return (3 == i); +} + +uint32_t ParseParameters(uint32_t count, uint32_t *params) +{ + char *p; + uint32_t i = 0; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < count; str = strtok_r(nullptr, ", ", &p), i++) { + params[i] = strtoul(str, nullptr, 0); + } + return i; +} + + +bool NewerVersion(char* version_str) +{ + uint32_t version = 0; + uint32_t i = 0; + char *str_ptr; + + char version_dup[strlen(version_str) +1]; + strncpy(version_dup, version_str, sizeof(version_dup)); + + for (char *str = strtok_r(version_dup, ".", &str_ptr); str && i < sizeof(VERSION); str = strtok_r(nullptr, ".", &str_ptr), i++) { + int field = atoi(str); + + if ((field < 0) || (field > 255)) { + return false; + } + + version = (version << 8) + field; + + if ((2 == i) && isalpha(str[strlen(str)-1])) { + field = str[strlen(str)-1] & 0x1f; + version = (version << 8) + field; + i++; + } + } + + + if ((i < 2) || (i > sizeof(VERSION))) { + return false; + } + + + while (i < sizeof(VERSION)) { + version <<= 8; + i++; + } + + return (version > VERSION); +} + +char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option) +{ + strncpy_P(dest, S_RSLT_POWER, size); + if ((devices_present + option) > 1) { + char sidx[8]; + snprintf_P(sidx, sizeof(sidx), PSTR("%d"), idx); + strncat(dest, sidx, size - strlen(dest) -1); + } + return dest; +} + +char* GetPowerDevice(char* dest, uint32_t idx, size_t size) +{ + return GetPowerDevice(dest, idx, size, 0); +} + +void GetEspHardwareType(void) +{ + + uint32_t efuse1 = *(uint32_t*)(0x3FF00050); + uint32_t efuse2 = *(uint32_t*)(0x3FF00054); + + + + is_8285 = ( (efuse1 & (1 << 4)) || (efuse2 & (1 << 16)) ); + if (is_8285 && (ESP.getFlashChipRealSize() > 1048576)) { + is_8285 = false; + } +} + +String GetDeviceHardware(void) +{ + char buff[10]; + if (is_8285) { + strcpy_P(buff, PSTR("ESP8285")); + } else { + strcpy_P(buff, PSTR("ESP8266EX")); + } + return String(buff); +} + +float ConvertTemp(float c) +{ + float result = c; + + global_update = uptime; + global_temperature = c; + + if (!isnan(c) && Settings.flag.temperature_conversion) { + result = c * 1.8 + 32; + } + result = result + (0.1 * Settings.temp_comp); + return result; +} + +float ConvertTempToCelsius(float c) +{ + float result = c; + + if (!isnan(c) && Settings.flag.temperature_conversion) { + result = (c - 32) / 1.8; + } + result = result + (0.1 * Settings.temp_comp); + return result; +} + +char TempUnit(void) +{ + return (Settings.flag.temperature_conversion) ? 'F' : 'C'; +} + +float ConvertHumidity(float h) +{ + global_update = uptime; + global_humidity = h; + + return h; +} + +float ConvertPressure(float p) +{ + float result = p; + + global_update = uptime; + global_pressure = p; + + if (!isnan(p) && Settings.flag.pressure_conversion) { + result = p * 0.75006375541921; + } + return result; +} + +String PressureUnit(void) +{ + return (Settings.flag.pressure_conversion) ? String(D_UNIT_MILLIMETER_MERCURY) : String(D_UNIT_PRESSURE); +} + +void ResetGlobalValues(void) +{ + if ((uptime - global_update) > GLOBAL_VALUES_VALID) { + global_update = 0; + global_temperature = 9999; + global_humidity = 0; + global_pressure = 0; + } +} + +uint32_t SqrtInt(uint32_t num) +{ + if (num <= 1) { + return num; + } + + uint32_t x = num / 2; + uint32_t y; + do { + y = (x + num / x) / 2; + if (y >= x) { + return x; + } + x = y; + } while (true); +} + +uint32_t RoundSqrtInt(uint32_t num) +{ + uint32_t s = SqrtInt(4 * num); + if (s & 1) { + s++; + } + return s / 2; +} + +char* GetTextIndexed(char* destination, size_t destination_size, uint32_t index, const char* haystack) +{ + + + char* write = destination; + const char* read = haystack; + + index++; + while (index--) { + size_t size = destination_size -1; + write = destination; + char ch = '.'; + while ((ch != '\0') && (ch != '|')) { + ch = pgm_read_byte(read++); + if (size && (ch != '|')) { + *write++ = ch; + size--; + } + } + if (0 == ch) { + if (index) { + write = destination; + } + break; + } + } + *write = '\0'; + return destination; +} + +int GetCommandCode(char* destination, size_t destination_size, const char* needle, const char* haystack) +{ + + + int result = -1; + const char* read = haystack; + char* write = destination; + + while (true) { + result++; + size_t size = destination_size -1; + write = destination; + char ch = '.'; + while ((ch != '\0') && (ch != '|')) { + ch = pgm_read_byte(read++); + if (size && (ch != '|')) { + *write++ = ch; + size--; + } + } + *write = '\0'; + if (!strcasecmp(needle, destination)) { + break; + } + if (0 == ch) { + result = -1; + break; + } + } + return result; +} + +bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void)) +{ + GetTextIndexed(XdrvMailbox.command, CMDSZ, 0, haystack); + int prefix_length = strlen(XdrvMailbox.command); + if (prefix_length) { + char prefix[prefix_length +1]; + snprintf_P(prefix, sizeof(prefix), XdrvMailbox.topic); + if (strcasecmp(prefix, XdrvMailbox.command)) { + return false; + } + } + int command_code = GetCommandCode(XdrvMailbox.command + prefix_length, CMDSZ, XdrvMailbox.topic + prefix_length, haystack); + if (command_code > 0) { + XdrvMailbox.command_code = command_code -1; + MyCommand[XdrvMailbox.command_code](); + return true; + } + return false; +} + +const char kOptions[] PROGMEM = "OFF|" D_OFF "|FALSE|" D_FALSE "|STOP|" D_STOP "|" D_CELSIUS "|" + "ON|" D_ON "|TRUE|" D_TRUE "|START|" D_START "|" D_FAHRENHEIT "|" D_USER "|" + "TOGGLE|" D_TOGGLE "|" D_ADMIN "|" + "BLINK|" D_BLINK "|" + "BLINKOFF|" D_BLINKOFF "|" + "ALL" ; + +const uint8_t sNumbers[] PROGMEM = { 0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1, + 2,2,2, + 3,3, + 4,4, + 255 }; + +int GetStateNumber(char *state_text) +{ + char command[CMDSZ]; + int state_number = GetCommandCode(command, sizeof(command), state_text, kOptions); + if (state_number >= 0) { + state_number = pgm_read_byte(sNumbers + state_number); + } + return state_number; +} + +String GetSerialConfig(void) +{ + + + + + + const char kParity[] = "NEOI"; + + char config[4]; + config[0] = '5' + (Settings.serial_config & 0x3); + config[1] = kParity[(Settings.serial_config >> 3) & 0x3]; + config[2] = '1' + ((Settings.serial_config >> 2) & 0x1); + config[3] = '\0'; + return String(config); +} + +void SetSerialBegin() +{ + uint32_t baudrate = Settings.baudrate * 300; + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_SERIAL "Set to %s %d bit/s"), GetSerialConfig().c_str(), baudrate); + Serial.flush(); + Serial.begin(baudrate, (SerialConfig)pgm_read_byte(kTasmotaSerialConfig + Settings.serial_config)); +} + +void SetSerialConfig(uint32_t serial_config) +{ + if (serial_config > TS_SERIAL_8O2) { + serial_config = TS_SERIAL_8N1; + } + if (serial_config != Settings.serial_config) { + Settings.serial_config = serial_config; + SetSerialBegin(); + } +} + +void SetSerialBaudrate(uint32_t baudrate) +{ + Settings.baudrate = baudrate / 300; + if (Serial.baudRate() != baudrate) { + SetSerialBegin(); + } +} + +void SetSerial(uint32_t baudrate, uint32_t serial_config) +{ + Settings.flag.mqtt_serial = 0; + Settings.serial_config = serial_config; + Settings.baudrate = baudrate / 300; + SetSeriallog(LOG_LEVEL_NONE); + SetSerialBegin(); +} + +void ClaimSerial(void) +{ + serial_local = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("SNS: Hardware Serial")); + SetSeriallog(LOG_LEVEL_NONE); + Settings.baudrate = Serial.baudRate() / 300; +} + +void SerialSendRaw(char *codes) +{ + char *p; + char stemp[3]; + uint8_t code; + + int size = strlen(codes); + + while (size > 1) { + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, &p, 16); + Serial.write(code); + size -= 2; + codes += 2; + } +} + +uint32_t GetHash(const char *buffer, size_t size) +{ + uint32_t hash = 0; + for (uint32_t i = 0; i <= size; i++) { + hash += (uint8_t)*buffer++ * (i +1); + } + return hash; +} + +void ShowSource(uint32_t source) +{ + if ((source > 0) && (source < SRC_MAX)) { + char stemp1[20]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SRC: %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource)); + } +} + +void WebHexCode(uint32_t i, const char* code) +{ + char scolor[10]; + + strlcpy(scolor, code, sizeof(scolor)); + char* p = scolor; + if ('#' == p[0]) { p++; } + + if (3 == strlen(p)) { + p[6] = p[3]; + p[5] = p[2]; + p[4] = p[2]; + p[3] = p[1]; + p[2] = p[1]; + p[1] = p[0]; + } + + uint32_t color = strtol(p, nullptr, 16); + + + + + + + uint32_t j = sizeof(Settings.web_color) / 3; +# 916 "C:/shared/sonoff/Git/Tasmota/tasmota/support.ino" + if (i >= j) { + + i += ((((uint8_t*)&Settings.web_color2 - (uint8_t*)&Settings.web_color) / 3) - j); + } + Settings.web_color[i][0] = (color >> 16) & 0xFF; + Settings.web_color[i][1] = (color >> 8) & 0xFF; + Settings.web_color[i][2] = color & 0xFF; +} + +uint32_t WebColor(uint32_t i) +{ + uint32_t j = sizeof(Settings.web_color) / 3; + + + + + if (i >= j) { + + i += ((((uint8_t*)&Settings.web_color2 - (uint8_t*)&Settings.web_color) / 3) - j); + } + uint32_t tcolor = (Settings.web_color[i][0] << 16) | (Settings.web_color[i][1] << 8) | Settings.web_color[i][2]; + + return tcolor; +} + + + + + +const uint16_t TIMESZ = 100; + +char* ResponseGetTime(uint32_t format, char* time_str) +{ + switch (format) { + case 1: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%u"), GetDateAndTime(DT_LOCAL).c_str(), UtcTime()); + break; + case 2: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":%u"), UtcTime()); + break; + default: + snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); + } + return time_str; +} + +int Response_P(const char* format, ...) +{ + + va_list args; + va_start(args, format); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), format, args); + va_end(args); + return len; +} + +int ResponseTime_P(const char* format, ...) +{ + + va_list args; + va_start(args, format); + + ResponseGetTime(Settings.flag2.time_format, mqtt_data); + + int mlen = strlen(mqtt_data); + int len = vsnprintf_P(mqtt_data + mlen, sizeof(mqtt_data) - mlen, format, args); + va_end(args); + return len + mlen; +} + +int ResponseAppend_P(const char* format, ...) +{ + + va_list args; + va_start(args, format); + int mlen = strlen(mqtt_data); + int len = vsnprintf_P(mqtt_data + mlen, sizeof(mqtt_data) - mlen, format, args); + va_end(args); + return len + mlen; +} + +int ResponseAppendTimeFormat(uint32_t format) +{ + char time_str[TIMESZ]; + return ResponseAppend_P(ResponseGetTime(format, time_str)); +} + +int ResponseAppendTime(void) +{ + return ResponseAppendTimeFormat(Settings.flag2.time_format); +} + +int ResponseJsonEnd(void) +{ + return ResponseAppend_P(PSTR("}")); +} + +int ResponseJsonEndEnd(void) +{ + return ResponseAppend_P(PSTR("}}")); +} + + + + + +void DigitalWrite(uint32_t gpio_pin, uint32_t state) +{ + if (pin[gpio_pin] < 99) { + digitalWrite(pin[gpio_pin], state &1); + } +} + +uint8_t ModuleNr(void) +{ + + + return (USER_MODULE == Settings.module) ? 0 : Settings.module +1; +} + +bool ValidTemplateModule(uint32_t index) +{ + for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { + if (index == pgm_read_byte(kModuleNiceList + i)) { + return true; + } + } + return false; +} + +bool ValidModule(uint32_t index) +{ + if (index == USER_MODULE) { return true; } + return ValidTemplateModule(index); +} + +String AnyModuleName(uint32_t index) +{ + if (USER_MODULE == index) { + return String(Settings.user_template.name); + } else { + return FPSTR(kModules[index].name); + } +} + +String ModuleName(void) +{ + return AnyModuleName(Settings.module); +} + +void ModuleGpios(myio *gp) +{ + uint8_t *dest = (uint8_t *)gp; + memset(dest, GPIO_NONE, sizeof(myio)); + + uint8_t src[sizeof(mycfgio)]; + if (USER_MODULE == Settings.module) { + memcpy(&src, &Settings.user_template.gp, sizeof(mycfgio)); + } else { + memcpy_P(&src, &kModules[Settings.module].gp, sizeof(mycfgio)); + } + + + + + uint32_t j = 0; + for (uint32_t i = 0; i < sizeof(mycfgio); i++) { + if (6 == i) { j = 9; } + if (8 == i) { j = 12; } + dest[j] = src[i]; + j++; + } + + + +} + +gpio_flag ModuleFlag(void) +{ + gpio_flag flag; + + if (USER_MODULE == Settings.module) { + flag = Settings.user_template.flag; + } else { + memcpy_P(&flag, &kModules[Settings.module].flag, sizeof(gpio_flag)); + } + + return flag; +} + +void ModuleDefault(uint32_t module) +{ + if (USER_MODULE == module) { module = WEMOS; } + Settings.user_template_base = module; + memcpy_P(&Settings.user_template, &kModules[module], sizeof(mytmplt)); +} + +void SetModuleType(void) +{ + my_module_type = (USER_MODULE == Settings.module) ? Settings.user_template_base : Settings.module; +} + +bool FlashPin(uint32_t pin) +{ + return (((pin > 5) && (pin < 9)) || (11 == pin)); +} + +uint8_t ValidPin(uint32_t pin, uint32_t gpio) +{ + if (FlashPin(pin)) { + return GPIO_NONE; + } + + + if ((WEMOS == Settings.module) && !Settings.flag3.user_esp8285_enable) { + if ((pin == 9) || (pin == 10)) { + return GPIO_NONE; + } + } + + return gpio; +} + +bool ValidGPIO(uint32_t pin, uint32_t gpio) +{ + return (GPIO_USER == ValidPin(pin, gpio)); +} + +bool ValidAdc(void) +{ + gpio_flag flag = ModuleFlag(); + uint32_t template_adc0 = flag.data &15; + return (ADC0_USER == template_adc0); +} + +bool GetUsedInModule(uint32_t val, uint8_t *arr) +{ + int offset = 0; + + if (!val) { return false; } + + if ((val >= GPIO_KEY1) && (val < GPIO_KEY1 + MAX_KEYS)) { + offset = (GPIO_KEY1_NP - GPIO_KEY1); + } + if ((val >= GPIO_KEY1_NP) && (val < GPIO_KEY1_NP + MAX_KEYS)) { + offset = -(GPIO_KEY1_NP - GPIO_KEY1); + } + if ((val >= GPIO_KEY1_INV) && (val < GPIO_KEY1_INV + MAX_KEYS)) { + offset = -(GPIO_KEY1_INV - GPIO_KEY1); + } + if ((val >= GPIO_KEY1_INV_NP) && (val < GPIO_KEY1_INV_NP + MAX_KEYS)) { + offset = -(GPIO_KEY1_INV_NP - GPIO_KEY1); + } + + if ((val >= GPIO_SWT1) && (val < GPIO_SWT1 + MAX_SWITCHES)) { + offset = (GPIO_SWT1_NP - GPIO_SWT1); + } + if ((val >= GPIO_SWT1_NP) && (val < GPIO_SWT1_NP + MAX_SWITCHES)) { + offset = -(GPIO_SWT1_NP - GPIO_SWT1); + } + + if ((val >= GPIO_REL1) && (val < GPIO_REL1 + MAX_RELAYS)) { + offset = (GPIO_REL1_INV - GPIO_REL1); + } + if ((val >= GPIO_REL1_INV) && (val < GPIO_REL1_INV + MAX_RELAYS)) { + offset = -(GPIO_REL1_INV - GPIO_REL1); + } + + if ((val >= GPIO_LED1) && (val < GPIO_LED1 + MAX_LEDS)) { + offset = (GPIO_LED1_INV - GPIO_LED1); + } + if ((val >= GPIO_LED1_INV) && (val < GPIO_LED1_INV + MAX_LEDS)) { + offset = -(GPIO_LED1_INV - GPIO_LED1); + } + + if ((val >= GPIO_PWM1) && (val < GPIO_PWM1 + MAX_PWMS)) { + offset = (GPIO_PWM1_INV - GPIO_PWM1); + } + if ((val >= GPIO_PWM1_INV) && (val < GPIO_PWM1_INV + MAX_PWMS)) { + offset = -(GPIO_PWM1_INV - GPIO_PWM1); + } + + if ((val >= GPIO_CNTR1) && (val < GPIO_CNTR1 + MAX_COUNTERS)) { + offset = (GPIO_CNTR1_NP - GPIO_CNTR1); + } + if ((val >= GPIO_CNTR1_NP) && (val < GPIO_CNTR1_NP + MAX_COUNTERS)) { + offset = -(GPIO_CNTR1_NP - GPIO_CNTR1); + } + + for (uint32_t i = 0; i < MAX_GPIO_PIN; i++) { + if (arr[i] == val) { return true; } + if (arr[i] == val + offset) { return true; } + } + return false; +} + +bool JsonTemplate(const char* dataBuf) +{ + + + if (strlen(dataBuf) < 9) { return false; } + + StaticJsonBuffer<350> jb; + JsonObject& obj = jb.parseObject(dataBuf); + if (!obj.success()) { return false; } + + + const char* name = obj[D_JSON_NAME]; + if (name != nullptr) { + strlcpy(Settings.user_template.name, name, sizeof(Settings.user_template.name)); + } + if (obj[D_JSON_GPIO].success()) { + for (uint32_t i = 0; i < sizeof(mycfgio); i++) { + Settings.user_template.gp.io[i] = obj[D_JSON_GPIO][i] | 0; + } + } + if (obj[D_JSON_FLAG].success()) { + uint8_t flag = obj[D_JSON_FLAG] | 0; + memcpy(&Settings.user_template.flag, &flag, sizeof(gpio_flag)); + } + if (obj[D_JSON_BASE].success()) { + uint8_t base = obj[D_JSON_BASE]; + if ((0 == base) || !ValidTemplateModule(base -1)) { base = 18; } + Settings.user_template_base = base -1; + } + return true; +} + +void TemplateJson(void) +{ + Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), Settings.user_template.name); + for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { + ResponseAppend_P(PSTR("%s%d"), (i>0)?",":"", Settings.user_template.gp.io[i]); + } + ResponseAppend_P(PSTR("],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), Settings.user_template.flag, Settings.user_template_base +1); +} + + + + + +inline int32_t TimeDifference(uint32_t prev, uint32_t next) +{ + return ((int32_t) (next - prev)); +} + +int32_t TimePassedSince(uint32_t timestamp) +{ + + + return TimeDifference(timestamp, millis()); +} + +bool TimeReached(uint32_t timer) +{ + + const long passed = TimePassedSince(timer); + return (passed >= 0); +} + +void SetNextTimeInterval(unsigned long& timer, const unsigned long step) +{ + timer += step; + const long passed = TimePassedSince(timer); + if (passed < 0) { return; } + if (static_cast(passed) > step) { + + timer = millis() + step; + return; + } + + timer = millis() + (step - passed); +} + +int32_t TimePassedSinceUsec(uint32_t timestamp) +{ + return TimeDifference(timestamp, micros()); +} + +bool TimeReachedUsec(uint32_t timer) +{ + + const long passed = TimePassedSinceUsec(timer); + return (passed >= 0); +} + + + + + +#ifdef USE_I2C +const uint8_t I2C_RETRY_COUNTER = 3; + +uint32_t i2c_active[4] = { 0 }; +uint32_t i2c_buffer = 0; + +bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size) +{ + uint8_t retry = I2C_RETRY_COUNTER; + bool status = false; + + i2c_buffer = 0; + while (!status && retry) { + Wire.beginTransmission(addr); + Wire.write(reg); + if (0 == Wire.endTransmission(false)) { + Wire.requestFrom((int)addr, (int)size); + if (Wire.available() == size) { + for (uint32_t i = 0; i < size; i++) { + i2c_buffer = i2c_buffer << 8 | Wire.read(); + } + status = true; + } + } + retry--; + } + return status; +} + +bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg) +{ + bool status = I2cValidRead(addr, reg, 1); + *data = (uint8_t)i2c_buffer; + return status; +} + +bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg) +{ + bool status = I2cValidRead(addr, reg, 2); + *data = (uint16_t)i2c_buffer; + return status; +} + +bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg) +{ + bool status = I2cValidRead(addr, reg, 2); + *data = (int16_t)i2c_buffer; + return status; +} + +bool I2cValidRead16LE(uint16_t *data, uint8_t addr, uint8_t reg) +{ + uint16_t ldata; + bool status = I2cValidRead16(&ldata, addr, reg); + *data = (ldata >> 8) | (ldata << 8); + return status; +} + +bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg) +{ + uint16_t ldata; + bool status = I2cValidRead16LE(&ldata, addr, reg); + *data = (int16_t)ldata; + return status; +} + +bool I2cValidRead24(int32_t *data, uint8_t addr, uint8_t reg) +{ + bool status = I2cValidRead(addr, reg, 3); + *data = i2c_buffer; + return status; +} + +uint8_t I2cRead8(uint8_t addr, uint8_t reg) +{ + I2cValidRead(addr, reg, 1); + return (uint8_t)i2c_buffer; +} + +uint16_t I2cRead16(uint8_t addr, uint8_t reg) +{ + I2cValidRead(addr, reg, 2); + return (uint16_t)i2c_buffer; +} + +int16_t I2cReadS16(uint8_t addr, uint8_t reg) +{ + I2cValidRead(addr, reg, 2); + return (int16_t)i2c_buffer; +} + +uint16_t I2cRead16LE(uint8_t addr, uint8_t reg) +{ + I2cValidRead(addr, reg, 2); + uint16_t temp = (uint16_t)i2c_buffer; + return (temp >> 8) | (temp << 8); +} + +int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg) +{ + return (int16_t)I2cRead16LE(addr, reg); +} + +int32_t I2cRead24(uint8_t addr, uint8_t reg) +{ + I2cValidRead(addr, reg, 3); + return i2c_buffer; +} + +bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size) +{ + uint8_t x = I2C_RETRY_COUNTER; + + do { + Wire.beginTransmission((uint8_t)addr); + Wire.write(reg); + uint8_t bytes = size; + while (bytes--) { + Wire.write((val >> (8 * bytes)) & 0xFF); + } + x--; + } while (Wire.endTransmission(true) != 0 && x != 0); + return (x); +} + +bool I2cWrite8(uint8_t addr, uint8_t reg, uint16_t val) +{ + return I2cWrite(addr, reg, val, 1); +} + +bool I2cWrite16(uint8_t addr, uint8_t reg, uint16_t val) +{ + return I2cWrite(addr, reg, val, 2); +} + +int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len) +{ + Wire.beginTransmission((uint8_t)addr); + Wire.write((uint8_t)reg); + Wire.endTransmission(); + if (len != Wire.requestFrom((uint8_t)addr, (uint8_t)len)) { + return 1; + } + while (len--) { + *reg_data = (uint8_t)Wire.read(); + reg_data++; + } + return 0; +} + +int8_t I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len) +{ + Wire.beginTransmission((uint8_t)addr); + Wire.write((uint8_t)reg); + while (len--) { + Wire.write(*reg_data); + reg_data++; + } + Wire.endTransmission(); + return 0; +} + +void I2cScan(char *devs, unsigned int devs_len) +{ + + + + + + + + uint8_t error = 0; + uint8_t address = 0; + uint8_t any = 0; + + snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_DEVICES_FOUND_AT)); + for (address = 1; address <= 127; address++) { + Wire.beginTransmission(address); + error = Wire.endTransmission(); + if (0 == error) { + any = 1; + snprintf_P(devs, devs_len, PSTR("%s 0x%02x"), devs, address); + } + else if (error != 2) { + any = 2; + snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"Error %d at 0x%02x"), error, address); + break; + } + } + if (any) { + strncat(devs, "\"}", devs_len - strlen(devs) -1); + } + else { + snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_NO_DEVICES_FOUND "\"}")); + } +} + +void I2cResetActive(uint32_t addr, uint32_t count = 1) +{ + addr &= 0x7F; + count &= 0x7F; + while (count-- && (addr < 128)) { + i2c_active[addr / 32] &= ~(1 << (addr % 32)); + addr++; + } + +} + +void I2cSetActive(uint32_t addr, uint32_t count = 1) +{ + addr &= 0x7F; + count &= 0x7F; + while (count-- && (addr < 128)) { + i2c_active[addr / 32] |= (1 << (addr % 32)); + addr++; + } + +} + +void I2cSetActiveFound(uint32_t addr, const char *types) +{ + I2cSetActive(addr); + AddLog_P2(LOG_LEVEL_INFO, S_LOG_I2C_FOUND_AT, types, addr); +} + +bool I2cActive(uint32_t addr) +{ + addr &= 0x7F; + if (i2c_active[addr / 32] & (1 << (addr % 32))) { + return true; + } + return false; +} + +bool I2cSetDevice(uint32_t addr) +{ + addr &= 0x7F; + if (I2cActive(addr)) { + return false; + } + Wire.beginTransmission((uint8_t)addr); + return (0 == Wire.endTransmission()); +} +#endif +# 1559 "C:/shared/sonoff/Git/Tasmota/tasmota/support.ino" +void SetSeriallog(uint32_t loglevel) +{ + Settings.seriallog_level = loglevel; + seriallog_level = loglevel; + seriallog_timer = 0; +} + +void SetSyslog(uint32_t loglevel) +{ + Settings.syslog_level = loglevel; + syslog_level = loglevel; + syslog_timer = 0; +} + +#ifdef USE_WEBSERVER +void GetLog(uint32_t idx, char** entry_pp, size_t* len_p) +{ + char* entry_p = nullptr; + size_t len = 0; + + if (idx) { + char* it = web_log; + do { + uint32_t cur_idx = *it; + it++; + size_t tmp = strchrspn(it, '\1'); + tmp++; + if (cur_idx == idx) { + len = tmp; + entry_p = it; + break; + } + it += tmp; + } while (it < web_log + WEB_LOG_SIZE && *it != '\0'); + } + *entry_pp = entry_p; + *len_p = len; +} +#endif + +void Syslog(void) +{ + + + uint32_t current_hash = GetHash(SettingsText(SET_SYSLOG_HOST), strlen(SettingsText(SET_SYSLOG_HOST))); + if (syslog_host_hash != current_hash) { + syslog_host_hash = current_hash; + WiFi.hostByName(SettingsText(SET_SYSLOG_HOST), syslog_host_addr); + } + if (PortUdp.beginPacket(syslog_host_addr, Settings.syslog_port)) { + char syslog_preamble[64]; + snprintf_P(syslog_preamble, sizeof(syslog_preamble), PSTR("%s ESP-"), my_hostname); + memmove(log_data + strlen(syslog_preamble), log_data, sizeof(log_data) - strlen(syslog_preamble)); + log_data[sizeof(log_data) -1] = '\0'; + memcpy(log_data, syslog_preamble, strlen(syslog_preamble)); + PortUdp.write(log_data, strlen(log_data)); + PortUdp.endPacket(); + delay(1); + } else { + syslog_level = 0; + syslog_timer = SYSLOG_TIMER; + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_HOST_NOT_FOUND ". " D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER); + } +} + +void AddLog(uint32_t loglevel) +{ + char mxtime[10]; + + snprintf_P(mxtime, sizeof(mxtime), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d "), RtcTime.hour, RtcTime.minute, RtcTime.second); + + if (loglevel <= seriallog_level) { + Serial.printf("%s%s\r\n", mxtime, log_data); + } +#ifdef USE_WEBSERVER + if (Settings.webserver && (loglevel <= Settings.weblog_level)) { + + + web_log_index &= 0xFF; + if (!web_log_index) web_log_index++; + while (web_log_index == web_log[0] || + strlen(web_log) + strlen(log_data) + 13 > WEB_LOG_SIZE) + { + char* it = web_log; + it++; + it += strchrspn(it, '\1'); + it++; + memmove(web_log, it, WEB_LOG_SIZE -(it-web_log)); + } + snprintf_P(web_log, sizeof(web_log), PSTR("%s%c%s%s\1"), web_log, web_log_index++, mxtime, log_data); + web_log_index &= 0xFF; + if (!web_log_index) web_log_index++; + } +#endif + if (Settings.flag.mqtt_enabled && + !global_state.mqtt_down && + (loglevel <= Settings.mqttlog_level)) { MqttPublishLogging(mxtime); } + + if (!global_state.wifi_down && + (loglevel <= syslog_level)) { Syslog(); } +} + +void AddLog_P(uint32_t loglevel, const char *formatP) +{ + snprintf_P(log_data, sizeof(log_data), formatP); + AddLog(loglevel); +} + +void AddLog_P(uint32_t loglevel, const char *formatP, const char *formatP2) +{ + char message[sizeof(log_data)]; + + snprintf_P(log_data, sizeof(log_data), formatP); + snprintf_P(message, sizeof(message), formatP2); + strncat(log_data, message, sizeof(log_data) - strlen(log_data) -1); + AddLog(loglevel); +} + +void PrepLog_P2(uint32_t loglevel, PGM_P formatP, ...) +{ + va_list arg; + va_start(arg, formatP); + vsnprintf_P(log_data, sizeof(log_data), formatP, arg); + va_end(arg); + + prepped_loglevel = loglevel; +} + +void AddLog_P2(uint32_t loglevel, PGM_P formatP, ...) +{ + va_list arg; + va_start(arg, formatP); + vsnprintf_P(log_data, sizeof(log_data), formatP, arg); + va_end(arg); + + AddLog(loglevel); +} + +void AddLog_Debug(PGM_P formatP, ...) +{ + va_list arg; + va_start(arg, formatP); + vsnprintf_P(log_data, sizeof(log_data), formatP, arg); + va_end(arg); + + AddLog(LOG_LEVEL_DEBUG); +} + +void AddLogBuffer(uint32_t loglevel, uint8_t *buffer, uint32_t count) +{ +# 1721 "C:/shared/sonoff/Git/Tasmota/tasmota/support.ino" + char hex_char[(count * 3) + 2]; + AddLog_P2(loglevel, PSTR("DMP: %s"), ToHex_P(buffer, count, hex_char, sizeof(hex_char), ' ')); +} + +void AddLogSerial(uint32_t loglevel) +{ + AddLogBuffer(loglevel, (uint8_t*)serial_in_buffer, serial_in_byte_counter); +} + +void AddLogMissed(char *sensor, uint32_t misses) +{ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses); +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_button.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_button.ino" +#define BUTTON_V1 +#ifdef BUTTON_V1 + + + + +#define MAX_BUTTON_COMMANDS 5 +const char kCommands[] PROGMEM = + D_CMND_WIFICONFIG " 2|" D_CMND_WIFICONFIG " 2|" D_CMND_WIFICONFIG " 2|" D_CMND_RESTART " 1|" D_CMND_UPGRADE " 1"; + +struct BUTTON { + unsigned long debounce = 0; + uint16_t hold_timer[MAX_KEYS] = { 0 }; + uint16_t dual_code = 0; + + uint8_t last_state[MAX_KEYS] = { NOT_PRESSED, NOT_PRESSED, NOT_PRESSED, NOT_PRESSED }; + uint8_t window_timer[MAX_KEYS] = { 0 }; + uint8_t press_counter[MAX_KEYS] = { 0 }; + + uint8_t dual_receive_count = 0; + uint8_t no_pullup_mask = 0; + uint8_t inverted_mask = 0; + uint8_t present = 0; + uint8_t adc = 99; +} Button; + + + +void ButtonPullupFlag(uint8 button_bit) +{ + bitSet(Button.no_pullup_mask, button_bit); +} + +void ButtonInvertFlag(uint8 button_bit) +{ + bitSet(Button.inverted_mask, button_bit); +} + +void ButtonInit(void) +{ + Button.present = 0; + for (uint32_t i = 0; i < MAX_KEYS; i++) { + if (pin[GPIO_KEY1 +i] < 99) { + Button.present++; + pinMode(pin[GPIO_KEY1 +i], bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); + } +#ifndef USE_ADC_VCC + else if ((99 == Button.adc) && ((ADC0_BUTTON == my_adc0) || (ADC0_BUTTON_INV == my_adc0))) { + Button.present++; + Button.adc = i; + } +#endif + } +} + +uint8_t ButtonSerial(uint8_t serial_in_byte) +{ + if (Button.dual_receive_count) { + Button.dual_receive_count--; + if (Button.dual_receive_count) { + Button.dual_code = (Button.dual_code << 8) | serial_in_byte; + serial_in_byte = 0; + } else { + if (serial_in_byte != 0xA1) { + Button.dual_code = 0; + } + } + } + if (0xA0 == serial_in_byte) { + serial_in_byte = 0; + Button.dual_code = 0; + Button.dual_receive_count = 3; + } + + return serial_in_byte; +} +# 108 "C:/shared/sonoff/Git/Tasmota/tasmota/support_button.ino" +void ButtonHandler(void) +{ + if (uptime < 4) { return; } + + uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; + uint16_t loops_per_second = 1000 / Settings.button_debounce; + char scmnd[20]; + + + + for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { + uint8_t button = NOT_PRESSED; + uint8_t button_present = 0; + + if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) { + button_present = 1; + if (Button.dual_code) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), Button.dual_code); + button = PRESSED; + if (0xF500 == Button.dual_code) { + Button.hold_timer[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; + hold_time_extent = 1; + } + Button.dual_code = 0; + } + } + else if (pin[GPIO_KEY1 +button_index] < 99) { + button_present = 1; + button = (digitalRead(pin[GPIO_KEY1 +button_index]) != bitRead(Button.inverted_mask, button_index)); + } +#ifndef USE_ADC_VCC + if (Button.adc == button_index) { + button_present = 1; + if (ADC0_BUTTON_INV == my_adc0) { + button = (AdcRead(1) < 128); + } + else if (ADC0_BUTTON == my_adc0) { + button = (AdcRead(1) > 128); + } + } +#endif + + if (button_present) { + XdrvMailbox.index = button_index; + XdrvMailbox.payload = button; + if (XdrvCall(FUNC_BUTTON_PRESSED)) { + + } + else if (SONOFF_4CHPRO == my_module_type) { + if (Button.hold_timer[button_index]) { Button.hold_timer[button_index]--; } + + bool button_pressed = false; + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1); + Button.hold_timer[button_index] = loops_per_second; + button_pressed = true; + } + if ((NOT_PRESSED == button) && (PRESSED == Button.last_state[button_index])) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1); + if (!Button.hold_timer[button_index]) { button_pressed = true; } + } + if (button_pressed) { + if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { + ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); + } + } + } + else { + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { + if (Settings.flag.button_single) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1); + if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { + ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); + } + } else { + Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, Button.press_counter[button_index]); + Button.window_timer[button_index] = loops_per_second / 2; + } + blinks = 201; + } + + if (NOT_PRESSED == button) { + Button.hold_timer[button_index] = 0; + } else { + Button.hold_timer[button_index]++; + if (Settings.flag.button_single) { + if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { + + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } else { + if (Settings.flag.button_restrict) { + if (Settings.param[P_HOLD_IGNORE] > 0) { + if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) { + Button.hold_timer[button_index] = 0; + Button.press_counter[button_index] = 0; + DEBUG_CORE_LOG(PSTR("BTN: " D_BUTTON "%d cancel by " D_CMND_SETOPTION "40 %d"), button_index +1, Settings.param[P_HOLD_IGNORE]); + } + } + if (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { + Button.press_counter[button_index] = 0; + SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); + } + } else { + if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { + Button.press_counter[button_index] = 0; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } + } + } + + if (!Settings.flag.button_single) { + if (Button.window_timer[button_index]) { + Button.window_timer[button_index]--; + } else { + if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0) && (Button.press_counter[button_index] < MAX_BUTTON_COMMANDS +3)) { + bool single_press = false; + if (Button.press_counter[button_index] < 3) { + if ((SONOFF_DUAL_R2 == my_module_type) || (SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { + single_press = true; + } else { + single_press = (Settings.flag.button_swap +1 == Button.press_counter[button_index]); + if ((1 == Button.present) && (2 == devices_present)) { + if (Settings.flag.button_swap) { + Button.press_counter[button_index] = (single_press) ? 1 : 2; + } + } else { + Button.press_counter[button_index] = 1; + } + } + } +#if defined(USE_LIGHT) && defined(ROTARY_V1) + if (!((0 == button_index) && RotaryButtonPressed())) { +#endif + if (single_press && SendKey(KEY_BUTTON, button_index + Button.press_counter[button_index], POWER_TOGGLE)) { + + } else { + if (Button.press_counter[button_index] < 3) { + if (WifiState() > WIFI_RESTART) { + restart_flag = 1; + } else { + ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); + } + } else { + if (!Settings.flag.button_restrict) { + GetTextIndexed(scmnd, sizeof(scmnd), Button.press_counter[button_index] -3, kCommands); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } + } +#if defined(USE_LIGHT) && defined(ROTARY_V1) + } +#endif + Button.press_counter[button_index] = 0; + } + } + } + } + } + Button.last_state[button_index] = button; + } +} + +void ButtonLoop(void) +{ + if (Button.present) { + if (TimeReached(Button.debounce)) { + SetNextTimeInterval(Button.debounce, Settings.button_debounce); + ButtonHandler(); + } + } +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_command.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_command.ino" +const char kTasmotaCommands[] PROGMEM = "|" + D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" + D_CMND_SERIALLOG "|" D_CMND_RESTART "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SAVEDATA "|" + D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" + D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|" + D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" + D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|" + D_CMND_SERIALDELIMITER "|" 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_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" + D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" +#ifdef USE_I2C + D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|" +#endif + D_CMND_SENSOR "|" D_CMND_DRIVER; + +void (* const TasmotaCommand[])(void) PROGMEM = { + &CmndBacklog, &CmndDelay, &CmndPower, &CmndStatus, &CmndState, &CmndSleep, &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, + &CmndSeriallog, &CmndRestart, &CmndPowerOnState, &CmndPulsetime, &CmndBlinktime, &CmndBlinkcount, &CmndSavedata, + &CmndSetoption, &CmndTemperatureResolution, &CmndHumidityResolution, &CmndPressureResolution, &CmndPowerResolution, + &CmndVoltageResolution, &CmndFrequencyResolution, &CmndCurrentResolution, &CmndEnergyResolution, &CmndWeightResolution, + &CmndModule, &CmndModules, &CmndGpio, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange, + &CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig, + &CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, + &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd, + &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset, +#ifdef USE_I2C + &CmndI2cScan, CmndI2cDriver, +#endif + &CmndSensor, &CmndDriver }; + +const char kWifiConfig[] PROGMEM = + D_WCFG_0_RESTART "||" D_WCFG_2_WIFIMANAGER "||" D_WCFG_4_RETRY "|" D_WCFG_5_WAIT "|" D_WCFG_6_SERIAL "|" D_WCFG_7_WIFIMANAGER_RESET_ONLY; + + + +void ResponseCmndNumber(int value) +{ + Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, value); +} + +void ResponseCmndFloat(float value, uint32_t decimals) +{ + char stemp1[TOPSZ]; + dtostrfd(value, decimals, stemp1); + Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, stemp1); +} + +void ResponseCmndIdxNumber(int value) +{ + Response_P(S_JSON_COMMAND_INDEX_NVALUE, XdrvMailbox.command, XdrvMailbox.index, value); +} + +void ResponseCmndChar(const char* value) +{ + Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, value); +} + +void ResponseCmndStateText(uint32_t value) +{ + ResponseCmndChar(GetStateText(value)); +} + +void ResponseCmndDone(void) +{ + ResponseCmndChar(D_JSON_DONE); +} + +void ResponseCmndIdxChar(const char* value) +{ + Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, XdrvMailbox.index, value); +} + +void ResponseCmndAll(uint32_t text_index, uint32_t count) +{ + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < count; i++) { + ResponseAppend_P(PSTR("%c\"%s%d\":\"%s\""), (i) ? ',' : '{', XdrvMailbox.command, i +1, SettingsText(text_index +i)); + } + ResponseJsonEnd(); +} + + + +void ExecuteCommand(const char *cmnd, uint32_t source) +{ + + + +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("ExecuteCommand")); +#endif + ShowSource(source); + + const char *pos = cmnd; + while (*pos && isspace(*pos)) { + pos++; + } + + const char *start = pos; + + while (*pos && (isalpha(*pos) || isdigit(*pos) || '_' == *pos || '/' == *pos)) { + if ('/' == *pos) { + start = pos + 1; + } + pos++; + } + if ('\0' == *start || pos <= start) { + return; + } + + uint32_t size = pos - start; + char stopic[size + 2]; + stopic[0] = '/'; + memcpy(stopic+1, start, size); + stopic[size+1] = '\0'; + + char svalue[strlen(pos) +1]; + strlcpy(svalue, pos, sizeof(svalue)); + CommandHandler(stopic, svalue, strlen(svalue)); +} +# 148 "C:/shared/sonoff/Git/Tasmota/tasmota/support_command.ino" +void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) +{ +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("CommandHandler")); +#endif + + while (*dataBuf && isspace(*dataBuf)) { + dataBuf++; + data_len--; + } + + bool grpflg = (strstr(topicBuf, SettingsText(SET_MQTT_GRP_TOPIC)) != nullptr); + + char stemp1[TOPSZ]; + GetFallbackTopic_P(stemp1, ""); + fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1))); + + char *type = strrchr(topicBuf, '/'); + + uint32_t index = 1; + bool user_index = false; + if (type != nullptr) { + type++; + uint32_t i; + for (i = 0; i < strlen(type); i++) { + type[i] = toupper(type[i]); + } + while (isdigit(type[i-1])) { + i--; + } + if (i < strlen(type)) { + index = atoi(type +i); + user_index = true; + } + type[i] = '\0'; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CMD: " D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " \"%s\", " D_DATA " \"%s\""), grpflg, index, type, dataBuf); + + if (type != nullptr) { + Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}")); + + if (Settings.ledstate &0x02) { blinks++; } + + if (!strcmp(dataBuf,"?")) { data_len = 0; } + + char *p; + int32_t payload = strtol(dataBuf, &p, 0); + if (p == dataBuf) { payload = -99; } + int temp_payload = GetStateNumber(dataBuf); + if (temp_payload > -1) { payload = temp_payload; } + + DEBUG_CORE_LOG(PSTR("CMD: Payload %d"), payload); + + + backlog_delay = millis() + Settings.param[P_BACKLOG_DELAY]; + + char command[CMDSZ] = { 0 }; + XdrvMailbox.command = command; + XdrvMailbox.index = index; + XdrvMailbox.data_len = data_len; + XdrvMailbox.payload = payload; + XdrvMailbox.grpflg = grpflg; + XdrvMailbox.usridx = user_index; + XdrvMailbox.topic = type; + XdrvMailbox.data = dataBuf; + +#ifdef USE_SCRIPT_SUB_COMMAND + + if (!Script_SubCmd()) { + if (!DecodeCommand(kTasmotaCommands, TasmotaCommand)) { + if (!XdrvCall(FUNC_COMMAND)) { + if (!XsnsCall(FUNC_COMMAND)) { + type = nullptr; + } + } + } + } +#else + if (!DecodeCommand(kTasmotaCommands, TasmotaCommand)) { + if (!XdrvCall(FUNC_COMMAND)) { + if (!XsnsCall(FUNC_COMMAND)) { + type = nullptr; + } + } + } +#endif + + } + + if (type == nullptr) { + blinks = 201; + snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_COMMAND)); + Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}")); + type = (char*)stemp1; + } + + if (mqtt_data[0] != '\0') { + MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); + XdrvRulesProcess(); + } + fallback_topic_flag = false; +} + + + +void CmndBacklog(void) +{ + if (XdrvMailbox.data_len) { + +#ifdef SUPPORT_IF_STATEMENT + char *blcommand = strtok(XdrvMailbox.data, ";"); + while ((blcommand != nullptr) && (backlog.size() < MAX_BACKLOG)) +#else + uint32_t bl_pointer = (!backlog_pointer) ? MAX_BACKLOG -1 : backlog_pointer; + bl_pointer--; + char *blcommand = strtok(XdrvMailbox.data, ";"); + while ((blcommand != nullptr) && (backlog_index != bl_pointer)) +#endif + { + while(true) { + blcommand = Trim(blcommand); + if (!strncasecmp_P(blcommand, PSTR(D_CMND_BACKLOG), strlen(D_CMND_BACKLOG))) { + blcommand += strlen(D_CMND_BACKLOG); + } else { + break; + } + } + if (*blcommand != '\0') { +#ifdef SUPPORT_IF_STATEMENT + if (backlog.size() < MAX_BACKLOG) { + backlog.add(blcommand); + } +#else + backlog[backlog_index] = String(blcommand); + backlog_index++; + if (backlog_index >= MAX_BACKLOG) backlog_index = 0; +#endif + } + blcommand = strtok(nullptr, ";"); + } + + mqtt_data[0] = '\0'; + } else { + bool blflag = BACKLOG_EMPTY; +#ifdef SUPPORT_IF_STATEMENT + backlog.clear(); +#else + backlog_pointer = backlog_index; +#endif + ResponseCmndChar(blflag ? D_JSON_EMPTY : D_JSON_ABORTED); + } +} + +void CmndDelay(void) +{ + if ((XdrvMailbox.payload >= (MIN_BACKLOG_DELAY / 100)) && (XdrvMailbox.payload <= 3600)) { + backlog_delay = millis() + (100 * XdrvMailbox.payload); + } + uint32_t bl_delay = 0; + long bl_delta = TimePassedSince(backlog_delay); + if (bl_delta < 0) { bl_delay = (bl_delta *-1) / 100; } + ResponseCmndNumber(bl_delay); +} + +void CmndPower(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= devices_present)) { + if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_BLINK_STOP)) { + XdrvMailbox.payload = POWER_SHOW_STATE; + } + + ExecuteCommandPower(XdrvMailbox.index, XdrvMailbox.payload, SRC_IGNORE); + mqtt_data[0] = '\0'; + } + else if (0 == XdrvMailbox.index) { + if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_TOGGLE)) { + XdrvMailbox.payload = POWER_SHOW_STATE; + } + SetAllPower(XdrvMailbox.payload, SRC_IGNORE); + mqtt_data[0] = '\0'; + } +} + +void CmndStatus(void) +{ + uint32_t payload = ((XdrvMailbox.payload < 0) || (XdrvMailbox.payload > MAX_STATUS)) ? 99 : XdrvMailbox.payload; + + uint32_t option = STAT; + char stemp[200]; + char stemp2[TOPSZ]; + + + + + + if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { payload = 99; } + if (!energy_flg && (9 == payload)) { payload = 99; } + if (!CrashFlag() && (12 == payload)) { payload = 99; } + + if ((0 == payload) || (99 == payload)) { + uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { maxfn = 1; } +#endif + stemp[0] = '\0'; + for (uint32_t i = 0; i < maxfn; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), SettingsText(SET_FRIENDLYNAME1 +i)); + } + stemp2[0] = '\0'; + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]); + } + Response_P(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_LEDMASK "\":\"%04X\",\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" + D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), + ModuleNr(), stemp, mqtt_topic, + SettingsText(SET_MQTT_BUTTON_TOPIC), power, Settings.poweronstate, Settings.ledstate, + Settings.ledmask, Settings.save_data, + Settings.flag.save_state, + SettingsText(SET_MQTT_SWITCH_TOPIC), + stemp2, + Settings.flag.mqtt_button_retain, + Settings.flag.mqtt_switch_retain, + Settings.flag.mqtt_sensor_retain, + Settings.flag.mqtt_power_retain); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS)); + } + + if ((0 == payload) || (1 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_SERIALCONFIG "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" + D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" + D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"BCResetTime\":\"%s\",\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"), + Settings.baudrate * 300, GetSerialConfig().c_str(), SettingsText(SET_MQTT_GRP_TOPIC), SettingsText(SET_OTAURL), + GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep, + Settings.cfg_holder, Settings.bootcount, GetDateAndTime(DT_BOOTCOUNT).c_str(), Settings.save_flag, GetSettingsAddress()); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "1")); + } + + if ((0 == payload) || (2 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" + D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"," + "\"Hardware\":\"%s\"" + "%s}}"), + my_version, my_image, GetBuildDateAndTime().c_str(), + ESP.getBootVersion(), ESP.getSdkVersion(), + GetDeviceHardware().c_str(), + GetStatistics().c_str()); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2")); + } + + if ((0 == payload) || (3 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_MQTTLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" + D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" + D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\",\"%08X\"]}}"), + Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, + SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), Settings.tele_period, + Settings.flag2.data, Settings.flag.data, ToHex_P((unsigned char*)Settings.param, PARAM8_SIZE, stemp2, sizeof(stemp2)), + Settings.flag3.data, Settings.flag4.data); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "3")); + } + + if ((0 == payload) || (4 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" + D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHCHIPID "\":\"%06X\",\"" D_JSON_FLASHMODE "\":%d,\"" + D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]"), + ESP.getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024, + ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024, ESP.getFlashChipId(), ESP.getFlashChipMode(), + LANGUAGE_LCID, feature_drv1, feature_drv2, feature_sns1, feature_sns2, feature5, feature6); + XsnsDriverState(); + ResponseAppend_P(PSTR(",\"Sensors\":")); + XsnsSensorState(); + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "4")); + } + + if ((0 == payload) || (5 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_GATEWAY "\":\"%s\",\"" + D_JSON_SUBNETMASK "\":\"%s\",\"" D_JSON_DNSSERVER "\":\"%s\",\"" D_JSON_MAC "\":\"%s\",\"" + D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d,\"" D_CMND_WIFIPOWER "\":%s}}"), + my_hostname, WiFi.localIP().toString().c_str(), IPAddress(Settings.ip_address[1]).toString().c_str(), + IPAddress(Settings.ip_address[2]).toString().c_str(), IPAddress(Settings.ip_address[3]).toString().c_str(), WiFi.macAddress().c_str(), + Settings.webserver, Settings.sta_config, WifiGetOutputPower().c_str()); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "5")); + } + + if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" + D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), + SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), + mqtt_client, SettingsText(SET_MQTT_USER), MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6")); + } + + if ((0 == payload) || (7 == payload)) { + if (99 == Settings.timezone) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d" ), Settings.timezone); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"%s\"" ), GetTimeZone().c_str()); + } +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" + D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s,\"" D_JSON_SUNRISE "\":\"%s\",\"" D_JSON_SUNSET "\":\"%s\"}}"), + GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(), + GetTime(3).c_str(), stemp, GetSun(0).c_str(), GetSun(1).c_str()); +#else + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" + D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s}}"), + GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(), + GetTime(3).c_str(), stemp); +#endif + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "7")); + } + +#if defined(USE_ENERGY_SENSOR) && defined(USE_ENERGY_MARGIN_DETECTION) + if (energy_flg) { + if ((0 == payload) || (9 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":%d,\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\"" + D_CMND_VOLTAGELOW "\":%d,\"" D_CMND_VOLTAGEHIGH "\":%d,\"" D_CMND_CURRENTLOW "\":%d,\"" D_CMND_CURRENTHIGH "\":%d}}"), + Settings.energy_power_delta, Settings.energy_min_power, Settings.energy_max_power, + Settings.energy_min_voltage, Settings.energy_max_voltage, Settings.energy_min_current, Settings.energy_max_current); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "9")); + } + } +#endif + + if ((0 == payload) || (8 == payload) || (10 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS10_SENSOR "\":")); + MqttShowSensor(); + ResponseJsonEnd(); + if (8 == payload) { + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "8")); + } else { + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "10")); + } + } + + if ((0 == payload) || (11 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS11_STATUS "\":")); + MqttShowState(); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11")); + } + + if (CrashFlag()) { + if ((0 == payload) || (12 == payload)) { + Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":")); + CrashDump(); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "12")); + } + } + +#ifdef USE_SCRIPT_STATUS + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data); +#endif + mqtt_data[0] = '\0'; +} + +void CmndState(void) +{ + mqtt_data[0] = '\0'; + MqttShowState(); + if (Settings.flag3.hass_tele_on_power) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); + } +#ifdef USE_HOME_ASSISTANT + if (Settings.flag.hass_discovery) { + HAssPublishStatus(); + } +#endif +} + +void CmndTempOffset(void) +{ + if (XdrvMailbox.data_len > 0) { + int value = (int)(CharToFloat(XdrvMailbox.data) * 10); + if ((value > -127) && (value < 127)) { + Settings.temp_comp = value; + } + } + ResponseCmndFloat((float)(Settings.temp_comp) / 10, 1); +} + +void CmndSleep(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 251)) { + Settings.sleep = XdrvMailbox.payload; + sleep = XdrvMailbox.payload; + WiFiSetSleepMode(); + } + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.sleep, sleep); + +} + +void CmndUpgrade(void) +{ + + + + + if (((1 == XdrvMailbox.data_len) && (1 == XdrvMailbox.payload)) || ((XdrvMailbox.data_len >= 3) && NewerVersion(XdrvMailbox.data))) { + ota_state_flag = 3; + char stemp1[TOPSZ]; + Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), XdrvMailbox.command, my_version, GetOtaUrl(stemp1, sizeof(stemp1))); + } else { + Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), XdrvMailbox.command, my_version); + } +} + +void CmndOtaUrl(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_OTAURL, (SC_DEFAULT == Shortcut()) ? OTA_URL : XdrvMailbox.data); + } + ResponseCmndChar(SettingsText(SET_OTAURL)); +} + +void CmndSeriallog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { + Settings.flag.mqtt_serial = 0; + SetSeriallog(XdrvMailbox.payload); + } + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.seriallog_level, seriallog_level); +} + +void CmndRestart(void) +{ + switch (XdrvMailbox.payload) { + case 1: + restart_flag = 2; + ResponseCmndChar(D_JSON_RESTARTING); + break; + case -1: + CmndCrash(); + break; + case -2: + CmndWDT(); + break; + case -3: + CmndBlockedLoop(); + break; + case 99: + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); + EspRestart(); + break; + default: + ResponseCmndChar(D_JSON_ONE_TO_RESTART); + } +} + +void CmndPowerOnState(void) +{ + if (my_module_type != MOTOR) { + + + + + + + + if ((XdrvMailbox.payload >= POWER_ALL_OFF) && (XdrvMailbox.payload <= POWER_ALL_OFF_PULSETIME_ON)) { + Settings.poweronstate = XdrvMailbox.payload; + if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { + for (uint32_t i = 1; i <= devices_present; i++) { + ExecuteCommandPower(i, POWER_ON, SRC_IGNORE); + } + } + } + ResponseCmndNumber(Settings.poweronstate); + } +} + +void CmndPulsetime(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PULSETIMERS)) { + uint32_t items = 1; + if (!XdrvMailbox.usridx && !XdrvMailbox.data_len) { + items = MAX_PULSETIMERS; + } else { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) { + Settings.pulse_timer[XdrvMailbox.index -1] = XdrvMailbox.payload; + SetPulseTimer(XdrvMailbox.index -1, XdrvMailbox.payload); + } + } + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < items; i++) { + uint32_t index = (1 == items) ? XdrvMailbox.index : i +1; + ResponseAppend_P(PSTR("%c\"%s%d\":{\"" D_JSON_SET "\":%d,\"" D_JSON_REMAINING "\":%d}"), + (i) ? ',' : '{', + XdrvMailbox.command, index, + Settings.pulse_timer[index -1], GetPulseTimer(index -1)); + } + ResponseJsonEnd(); + } +} + +void CmndBlinktime(void) +{ + if ((XdrvMailbox.payload > 1) && (XdrvMailbox.payload <= 3600)) { + Settings.blinktime = XdrvMailbox.payload; + if (blink_timer > 0) { blink_timer = millis() + (100 * XdrvMailbox.payload); } + } + ResponseCmndNumber(Settings.blinktime); +} + +void CmndBlinkcount(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) { + Settings.blinkcount = XdrvMailbox.payload; + if (blink_counter) { blink_counter = Settings.blinkcount *2; } + } + ResponseCmndNumber(Settings.blinkcount); +} + +void CmndSavedata(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3600)) { + Settings.save_data = XdrvMailbox.payload; + save_data_counter = Settings.save_data; + } + SettingsSaveAll(); + char stemp1[TOPSZ]; + if (Settings.save_data > 1) { + snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_EVERY " %d " D_UNIT_SECOND), Settings.save_data); + } + ResponseCmndChar((Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data)); +} + +void CmndSetoption(void) +{ + if (XdrvMailbox.index < 114) { + uint32_t ptype; + uint32_t pindex; + if (XdrvMailbox.index <= 31) { + ptype = 2; + pindex = XdrvMailbox.index; + } + else if (XdrvMailbox.index <= 49) { + ptype = 1; + pindex = XdrvMailbox.index -32; + } + else if (XdrvMailbox.index <= 81) { + ptype = 3; + pindex = XdrvMailbox.index -50; + } + else { + ptype = 4; + pindex = XdrvMailbox.index -82; + } + + if (XdrvMailbox.payload >= 0) { + if (1 == ptype) { + uint32_t param_low = 0; + uint32_t param_high = 255; + switch (pindex) { + case P_HOLD_TIME: + case P_MAX_POWER_RETRY: + param_low = 1; + param_high = 250; + break; + } + if ((XdrvMailbox.payload >= param_low) && (XdrvMailbox.payload <= param_high)) { + Settings.param[pindex] = XdrvMailbox.payload; +#ifdef USE_LIGHT + if (P_RGB_REMAP == pindex) { + LightUpdateColorMapping(); + restart_flag = 2; + } +#endif +#if (defined(USE_IR_REMOTE) && defined(USE_IR_RECEIVE)) || defined(USE_IR_REMOTE_FULL) + if (P_IR_UNKNOW_THRESHOLD == pindex) { + IrReceiveUpdateThreshold(); + } +#endif + } else { + ptype = 99; + } + } else { + if (XdrvMailbox.payload <= 1) { + if (2 == ptype) { + switch (pindex) { + case 5: + case 6: + case 7: + case 9: + case 14: + case 22: + case 23: + case 25: + case 27: + ptype = 99; + break; + case 3: + case 15: + restart_flag = 2; + default: + bitWrite(Settings.flag.data, pindex, XdrvMailbox.payload); + } + if (12 == pindex) { + stop_flash_rotate = XdrvMailbox.payload; + SettingsSave(2); + } + #ifdef USE_HOME_ASSISTANT + if ((19 == pindex) || (30 == pindex)) { + HAssDiscover(); + } + #endif + } + else if (3 == ptype) { + bitWrite(Settings.flag3.data, pindex, XdrvMailbox.payload); + switch (pindex) { + case 5: + if (0 == XdrvMailbox.payload) { + restart_flag = 2; + } + break; + case 10: + WiFiSetSleepMode(); + break; + case 18: + case 25: + restart_flag = 2; + break; + } + } + else if (4 == ptype) { + bitWrite(Settings.flag4.data, pindex, XdrvMailbox.payload); + } + } else { + ptype = 99; + } + } + } + + if (ptype < 99) { + if (1 == ptype) { + ResponseCmndIdxNumber(Settings.param[pindex]); + } else { + uint32_t flag = Settings.flag.data; + if (3 == ptype) { + flag = Settings.flag3.data; + } + else if (4 == ptype) { + flag = Settings.flag4.data; + } + ResponseCmndIdxChar(GetStateText(bitRead(flag, pindex))); + } + } + } +} + +void CmndTemperatureResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.temperature_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.temperature_resolution); +} + +void CmndHumidityResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.humidity_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.humidity_resolution); +} + +void CmndPressureResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.pressure_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.pressure_resolution); +} + +void CmndPowerResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.wattage_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.wattage_resolution); +} + +void CmndVoltageResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.voltage_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.voltage_resolution); +} + +void CmndFrequencyResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.frequency_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.frequency_resolution); +} + +void CmndCurrentResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.current_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.current_resolution); +} + +void CmndEnergyResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { + Settings.flag2.energy_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.energy_resolution); +} + +void CmndWeightResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + Settings.flag2.weight_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.weight_resolution); +} + +void CmndModule(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAXMODULE)) { + bool present = false; + if (0 == XdrvMailbox.payload) { + XdrvMailbox.payload = USER_MODULE; + present = true; + } else { + XdrvMailbox.payload--; + present = ValidTemplateModule(XdrvMailbox.payload); + } + if (present) { + Settings.last_module = Settings.module; + Settings.module = XdrvMailbox.payload; + SetModuleType(); + if (Settings.last_module != XdrvMailbox.payload) { + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + Settings.my_gp.io[i] = GPIO_NONE; + } + } + restart_flag = 2; + } + } + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, ModuleNr(), ModuleName().c_str()); +} + +void CmndModules(void) +{ + uint32_t midx = USER_MODULE; + uint32_t lines = 1; + bool jsflg = false; + for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { + if (i > 0) { midx = pgm_read_byte(kModuleNiceList + i -1); } + if (!jsflg) { + Response_P(PSTR("{\"" D_CMND_MODULES "%d\":{"), lines); + } else { + ResponseAppend_P(PSTR(",")); + } + jsflg = true; + uint32_t j = i ? midx +1 : 0; + if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), j, AnyModuleName(midx).c_str()) > (LOGSZ - TOPSZ)) || (i == sizeof(kModuleNiceList))) { + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); + jsflg = false; + lines++; + } + } + mqtt_data[0] = '\0'; +} + +void CmndGpio(void) +{ + if (XdrvMailbox.index < sizeof(Settings.my_gp)) { + myio cmodule; + ModuleGpios(&cmodule); + if (ValidGPIO(XdrvMailbox.index, cmodule.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < GPIO_SENSOR_END)) { + bool present = false; + for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + uint32_t midx = pgm_read_byte(kGpioNiceList + i); + if (midx == XdrvMailbox.payload) { present = true; } + } + if (present) { + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + if (ValidGPIO(i, cmodule.io[i]) && (Settings.my_gp.io[i] == XdrvMailbox.payload)) { + Settings.my_gp.io[i] = GPIO_NONE; + } + } + Settings.my_gp.io[XdrvMailbox.index] = XdrvMailbox.payload; + restart_flag = 2; + } + } + Response_P(PSTR("{")); + bool jsflg = false; + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + if (ValidGPIO(i, cmodule.io[i]) || ((GPIO_USER == XdrvMailbox.payload) && !FlashPin(i))) { + if (jsflg) { ResponseAppend_P(PSTR(",")); } + jsflg = true; + uint8_t sensor_type = Settings.my_gp.io[i]; + if (!ValidGPIO(i, cmodule.io[i])) { + sensor_type = cmodule.io[i]; + if (GPIO_USER == sensor_type) { + sensor_type = GPIO_NONE; + } + } + uint8_t sensor_name_idx = sensor_type; + const char *sensor_names = kSensorNames; + if (sensor_type > GPIO_FIX_START) { + sensor_name_idx = sensor_type - GPIO_FIX_START -1; + sensor_names = kSensorNamesFixed; + } + char stemp1[TOPSZ]; + ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s\"}"), + i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names)); + } + } + if (jsflg) { + ResponseJsonEnd(); + } else { + ResponseCmndChar(D_JSON_NOT_SUPPORTED); + } + } +} + +void CmndGpios(void) +{ + myio cmodule; + ModuleGpios(&cmodule); + uint32_t lines = 1; + bool jsflg = false; + for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + uint32_t midx = pgm_read_byte(kGpioNiceList + i); + if ((XdrvMailbox.payload != 255) && GetUsedInModule(midx, cmodule.io)) { continue; } + if (!jsflg) { + Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":{"), lines); + } else { + ResponseAppend_P(PSTR(",")); + } + jsflg = true; + char stemp1[TOPSZ]; + if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); + jsflg = false; + lines++; + } + } + mqtt_data[0] = '\0'; +} + +void CmndTemplate(void) +{ + + bool error = false; + + if (strstr(XdrvMailbox.data, "{") == nullptr) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= MAXMODULE)) { + XdrvMailbox.payload--; + if (ValidTemplateModule(XdrvMailbox.payload)) { + ModuleDefault(XdrvMailbox.payload); + if (USER_MODULE == Settings.module) { restart_flag = 2; } + } + } + else if (0 == XdrvMailbox.payload) { + if (Settings.module != USER_MODULE) { + ModuleDefault(Settings.module); + } + } + else if (255 == XdrvMailbox.payload) { + if (Settings.module != USER_MODULE) { + ModuleDefault(Settings.module); + } + snprintf_P(Settings.user_template.name, sizeof(Settings.user_template.name), PSTR("Merged")); + uint32_t j = 0; + for (uint32_t i = 0; i < sizeof(mycfgio); i++) { + if (6 == i) { j = 9; } + if (8 == i) { j = 12; } + if (my_module.io[j] > GPIO_NONE) { + Settings.user_template.gp.io[i] = my_module.io[j]; + } + j++; + } + } + } + else { + if (JsonTemplate(XdrvMailbox.data)) { + if (USER_MODULE == Settings.module) { restart_flag = 2; } + } else { + ResponseCmndChar(D_JSON_INVALID_JSON); + error = true; + } + } + if (!error) { TemplateJson(); } +} + +void CmndPwm(void) +{ + if (pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings.pwm_range) && (pin[GPIO_PWM1 + XdrvMailbox.index -1] < 99)) { + Settings.pwm_value[XdrvMailbox.index -1] = XdrvMailbox.payload; + analogWrite(pin[GPIO_PWM1 + XdrvMailbox.index -1], bitRead(pwm_inverted, XdrvMailbox.index -1) ? Settings.pwm_range - XdrvMailbox.payload : XdrvMailbox.payload); + } + Response_P(PSTR("{")); + MqttShowPWMState(); + ResponseJsonEnd(); + } +} + +void CmndPwmfrequency(void) +{ + if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) { + Settings.pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload; + analogWriteFreq(Settings.pwm_frequency); + } + ResponseCmndNumber(Settings.pwm_frequency); +} + +void CmndPwmrange(void) +{ + if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload > 254) && (XdrvMailbox.payload < 1024))) { + Settings.pwm_range = (1 == XdrvMailbox.payload) ? PWM_RANGE : XdrvMailbox.payload; + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (Settings.pwm_value[i] > Settings.pwm_range) { + Settings.pwm_value[i] = Settings.pwm_range; + } + } + analogWriteRange(Settings.pwm_range); + } + ResponseCmndNumber(Settings.pwm_range); +} + +void CmndButtonDebounce(void) +{ + if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) { + Settings.button_debounce = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.button_debounce); +} + +void CmndSwitchDebounce(void) +{ + if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) { + Settings.switch_debounce = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.switch_debounce); +} + +void CmndBaudrate(void) +{ + if (XdrvMailbox.payload >= 300) { + XdrvMailbox.payload /= 300; + uint32_t baudrate = (XdrvMailbox.payload & 0xFFFF) * 300; + SetSerialBaudrate(baudrate); + } + ResponseCmndNumber(Settings.baudrate * 300); +} + +void CmndSerialConfig(void) +{ + + + + + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.data_len < 3) { + if ((XdrvMailbox.payload >= TS_SERIAL_5N1) && (XdrvMailbox.payload <= TS_SERIAL_8O2)) { + SetSerialConfig(XdrvMailbox.payload); + } + } + else if ((XdrvMailbox.payload >= 5) && (XdrvMailbox.payload <= 8)) { + uint8_t serial_config = XdrvMailbox.payload -5; + + bool valid = true; + char parity = (XdrvMailbox.data[1] & 0xdf); + if ('E' == parity) { + serial_config += 0x08; + } + else if ('O' == parity) { + serial_config += 0x10; + } + else if ('N' != parity) { + valid = false; + } + + if ('2' == XdrvMailbox.data[2]) { + serial_config += 0x04; + } + else if ('1' != XdrvMailbox.data[2]) { + valid = false; + } + + if (valid) { + SetSerialConfig(serial_config); + } + } + } + ResponseCmndChar(GetSerialConfig().c_str()); +} + +void CmndSerialSend(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { + SetSeriallog(LOG_LEVEL_NONE); + Settings.flag.mqtt_serial = 1; + Settings.flag.mqtt_serial_raw = (XdrvMailbox.index > 3) ? 1 : 0; + if (XdrvMailbox.data_len > 0) { + if (1 == XdrvMailbox.index) { + Serial.printf("%s\n", XdrvMailbox.data); + } + else if (2 == XdrvMailbox.index || 4 == XdrvMailbox.index) { + for (uint32_t i = 0; i < XdrvMailbox.data_len; i++) { + Serial.write(XdrvMailbox.data[i]); + } + } + else if (3 == XdrvMailbox.index) { + uint32_t dat_len = XdrvMailbox.data_len; + Serial.printf("%s", Unescape(XdrvMailbox.data, &dat_len)); + } + else if (5 == XdrvMailbox.index) { + SerialSendRaw(RemoveSpace(XdrvMailbox.data)); + } + ResponseCmndDone(); + } + } +} + +void CmndSerialDelimiter(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.payload < 256)) { + if (XdrvMailbox.payload > 0) { + Settings.serial_delimiter = XdrvMailbox.payload; + } else { + uint32_t dat_len = XdrvMailbox.data_len; + Unescape(XdrvMailbox.data, &dat_len); + Settings.serial_delimiter = XdrvMailbox.data[0]; + } + } + ResponseCmndNumber(Settings.serial_delimiter); +} + +void CmndSyslog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { + SetSyslog(XdrvMailbox.payload); + } + Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.syslog_level, syslog_level); +} + +void CmndLoghost(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_SYSLOG_HOST, (SC_DEFAULT == Shortcut()) ? SYS_LOG_HOST : XdrvMailbox.data); + } + ResponseCmndChar(SettingsText(SET_SYSLOG_HOST)); +} + +void CmndLogport(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { + Settings.syslog_port = (1 == XdrvMailbox.payload) ? SYS_LOG_PORT : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.syslog_port); +} + +void CmndIpAddress(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { + uint32_t address; + if (ParseIp(&address, XdrvMailbox.data)) { + Settings.ip_address[XdrvMailbox.index -1] = address; + + } + char stemp1[TOPSZ]; + snprintf_P(stemp1, sizeof(stemp1), PSTR(" (%s)"), WiFi.localIP().toString().c_str()); + Response_P(S_JSON_COMMAND_INDEX_SVALUE_SVALUE, XdrvMailbox.command, XdrvMailbox.index, IPAddress(Settings.ip_address[XdrvMailbox.index -1]).toString().c_str(), (1 == XdrvMailbox.index) ? stemp1:""); + } +} + +void CmndNtpServer(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_NTP_SERVERS)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_NTPSERVER1, MAX_NTP_SERVERS); + } else { + uint32_t ntp_server = SET_NTPSERVER1 + XdrvMailbox.index -1; + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(ntp_server, + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? NTP_SERVER1 : (2 == XdrvMailbox.index) ? NTP_SERVER2 : NTP_SERVER3 : XdrvMailbox.data); + SettingsUpdateText(ntp_server, ReplaceCommaWithDot(SettingsText(ntp_server))); + + ntp_force_sync = true; + } + ResponseCmndIdxChar(SettingsText(ntp_server)); + } + } +} + +void CmndAp(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + switch (XdrvMailbox.payload) { + case 0: + Settings.sta_active ^= 1; + break; + case 1: + case 2: + Settings.sta_active = XdrvMailbox.payload -1; + } + restart_flag = 2; + } + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active)); +} + +void CmndSsid(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SSIDS)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_STASSID1, MAX_SSIDS); + } else { + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_STASSID1 + XdrvMailbox.index -1, + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_SSID1 : STA_SSID2 : XdrvMailbox.data); + Settings.sta_active = XdrvMailbox.index -1; + restart_flag = 2; + } + ResponseCmndIdxChar(SettingsText(SET_STASSID1 + XdrvMailbox.index -1)); + } + } +} + +void CmndPassword(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + if ((XdrvMailbox.data_len > 4) || (SC_CLEAR == Shortcut()) || (SC_DEFAULT == Shortcut())) { + SettingsUpdateText(SET_STAPWD1 + XdrvMailbox.index -1, + (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_PASS1 : STA_PASS2 : XdrvMailbox.data); + Settings.sta_active = XdrvMailbox.index -1; + restart_flag = 2; + ResponseCmndIdxChar(SettingsText(SET_STAPWD1 + XdrvMailbox.index -1)); + } else { + Response_P(S_JSON_COMMAND_INDEX_ASTERISK, XdrvMailbox.command, XdrvMailbox.index); + } + } +} + +void CmndHostname(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + SettingsUpdateText(SET_HOSTNAME, (SC_DEFAULT == Shortcut()) ? WIFI_HOSTNAME : XdrvMailbox.data); + if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); + } + restart_flag = 2; + } + ResponseCmndChar(SettingsText(SET_HOSTNAME)); +} + +void CmndWifiConfig(void) +{ + if ((XdrvMailbox.payload >= WIFI_RESTART) && (XdrvMailbox.payload < MAX_WIFI_OPTION)) { + if ((EX_WIFI_SMARTCONFIG == XdrvMailbox.payload) || (EX_WIFI_WPSCONFIG == XdrvMailbox.payload)) { + XdrvMailbox.payload = WIFI_MANAGER; + } + Settings.sta_config = XdrvMailbox.payload; + wifi_state_flag = Settings.sta_config; + if (WifiState() > WIFI_RESTART) { + restart_flag = 2; + } + } + char stemp1[TOPSZ]; + Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_config, GetTextIndexed(stemp1, sizeof(stemp1), Settings.sta_config, kWifiConfig)); +} + +void CmndFriendlyname(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_FRIENDLYNAMES)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_FRIENDLYNAME1, MAX_FRIENDLYNAMES); + } else { + if (XdrvMailbox.data_len > 0) { + char stemp1[TOPSZ]; + if (1 == XdrvMailbox.index) { + snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME)); + } else { + snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME "%d"), XdrvMailbox.index); + } + SettingsUpdateText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : (SC_DEFAULT == Shortcut()) ? stemp1 : XdrvMailbox.data); + } + ResponseCmndIdxChar(SettingsText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1)); + } + } +} + +void CmndSwitchMode(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SWITCHES)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_SWITCH_OPTION)) { + Settings.switchmode[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.switchmode[XdrvMailbox.index-1]); + } +} + +void CmndInterlock(void) +{ + + uint32_t max_relays = devices_present; + if (light_type) { max_relays--; } + if (max_relays > sizeof(Settings.interlock[0]) * 8) { max_relays = sizeof(Settings.interlock[0]) * 8; } + if (max_relays > 1) { + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } + char *group; + char *q; + uint32_t group_index = 0; + power_t relay_mask = 0; + for (group = strtok_r(XdrvMailbox.data, " ", &q); group && group_index < MAX_INTERLOCKS; group = strtok_r(nullptr, " ", &q)) { + char *str; + char *p; + for (str = strtok_r(group, ",", &p); str; str = strtok_r(nullptr, ",", &p)) { + int pbit = atoi(str); + if ((pbit > 0) && (pbit <= max_relays)) { + pbit--; + if (!bitRead(relay_mask, pbit)) { + bitSet(relay_mask, pbit); + bitSet(Settings.interlock[group_index], pbit); + } + } + } + group_index++; + } + for (uint32_t i = 0; i < group_index; i++) { + uint32_t minimal_bits = 0; + for (uint32_t j = 0; j < max_relays; j++) { + if (bitRead(Settings.interlock[i], j)) { minimal_bits++; } + } + if (minimal_bits < 2) { Settings.interlock[i] = 0; } + } + } else { + Settings.flag.interlock = XdrvMailbox.payload &1; + if (Settings.flag.interlock) { + SetDevicePower(power, SRC_IGNORE); + } + } +#ifdef USE_SHUTTER + if (Settings.flag3.shutter_mode) { + ShutterInit(); + } +#endif + } + Response_P(PSTR("{\"" D_CMND_INTERLOCK "\":\"%s\",\"" D_JSON_GROUPS "\":\""), GetStateText(Settings.flag.interlock)); + uint32_t anygroup = 0; + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { + if (Settings.interlock[i]) { + anygroup++; + ResponseAppend_P(PSTR("%s"), (anygroup > 1) ? " " : ""); + uint32_t anybit = 0; + power_t mask = 1; + for (uint32_t j = 0; j < max_relays; j++) { + if (Settings.interlock[i] & mask) { + anybit++; + ResponseAppend_P(PSTR("%s%d"), (anybit > 1) ? "," : "", j +1); + } + mask <<= 1; + } + } + } + if (!anygroup) { + for (uint32_t j = 1; j <= max_relays; j++) { + ResponseAppend_P(PSTR("%s%d"), (j > 1) ? "," : "", j); + } + } + ResponseAppend_P(PSTR("\"}")); + } else { + Settings.flag.interlock = 0; + ResponseCmndStateText(Settings.flag.interlock); + } +} + +void CmndTeleperiod(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.tele_period = (1 == XdrvMailbox.payload) ? TELE_PERIOD : XdrvMailbox.payload; + if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) Settings.tele_period = 10; + tele_period = Settings.tele_period; + } + ResponseCmndNumber(Settings.tele_period); +} + +void CmndReset(void) +{ + switch (XdrvMailbox.payload) { + case 1: + restart_flag = 211; + ResponseCmndChar(D_JSON_RESET_AND_RESTARTING); + break; + case 2 ... 6: + restart_flag = 210 + XdrvMailbox.payload; + Response_P(PSTR("{\"" D_CMND_RESET "\":\"" D_JSON_ERASE ", " D_JSON_RESET_AND_RESTARTING "\"}")); + break; + case 99: + Settings.bootcount = 0; + Settings.bootcount_reset_time = 0; + ResponseCmndDone(); + break; + default: + ResponseCmndChar(D_JSON_ONE_TO_RESET); + } +} + +void CmndTime(void) +{ + + + + + + + + uint32_t format = Settings.flag2.time_format; + if (XdrvMailbox.data_len > 0) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4)) { + Settings.flag2.time_format = XdrvMailbox.payload -1; + format = Settings.flag2.time_format; + } else { + format = 1; + RtcSetTime(XdrvMailbox.payload); + } + } + mqtt_data[0] = '\0'; + ResponseAppendTimeFormat(format); + ResponseJsonEnd(); +} + +void CmndTimezone(void) +{ + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.payload >= -13)) { + Settings.timezone = XdrvMailbox.payload; + Settings.timezone_minutes = 0; + if (XdrvMailbox.payload < 15) { + char *p = strtok (XdrvMailbox.data, ":"); + if (p) { + p = strtok (nullptr, ":"); + if (p) { + Settings.timezone_minutes = strtol(p, nullptr, 10); + if (Settings.timezone_minutes > 59) { Settings.timezone_minutes = 59; } + } + } + } else { + Settings.timezone = 99; + } + ntp_force_sync = true; + } + if (99 == Settings.timezone) { + ResponseCmndNumber(Settings.timezone); + } else { + char stemp1[TOPSZ]; + snprintf_P(stemp1, sizeof(stemp1), PSTR("%+03d:%02d"), Settings.timezone, Settings.timezone_minutes); + ResponseCmndChar(stemp1); + } +} + +void CmndTimeStdDst(uint32_t ts) +{ + + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + uint32_t tpos = 0; + int value = 0; + char *p = XdrvMailbox.data; + char *q = p; + while (p && (tpos < 7)) { + if (p > q) { + if (1 == tpos) { Settings.tflag[ts].hemis = value &1; } + if (2 == tpos) { Settings.tflag[ts].week = (value < 0) ? 0 : (value > 4) ? 4 : value; } + if (3 == tpos) { Settings.tflag[ts].month = (value < 1) ? 1 : (value > 12) ? 12 : value; } + if (4 == tpos) { Settings.tflag[ts].dow = (value < 1) ? 1 : (value > 7) ? 7 : value; } + if (5 == tpos) { Settings.tflag[ts].hour = (value < 0) ? 0 : (value > 23) ? 23 : value; } + if (6 == tpos) { Settings.toffset[ts] = (value < -900) ? -900 : (value > 900) ? 900 : value; } + } + p = Trim(p); + if (tpos && (*p == ',')) { p++; } + p = Trim(p); + q = p; + value = strtol(p, &p, 10); + tpos++; + } + ntp_force_sync = true; + } else { + if (0 == XdrvMailbox.payload) { + if (0 == ts) { + SettingsResetStd(); + } else { + SettingsResetDst(); + } + } + ntp_force_sync = true; + } + } + Response_P(PSTR("{\"%s\":{\"Hemisphere\":%d,\"Week\":%d,\"Month\":%d,\"Day\":%d,\"Hour\":%d,\"Offset\":%d}}"), + XdrvMailbox.command, Settings.tflag[ts].hemis, Settings.tflag[ts].week, Settings.tflag[ts].month, Settings.tflag[ts].dow, Settings.tflag[ts].hour, Settings.toffset[ts]); +} + +void CmndTimeStd(void) +{ + CmndTimeStdDst(0); +} + +void CmndTimeDst(void) +{ + CmndTimeStdDst(1); +} + +void CmndAltitude(void) +{ + if ((XdrvMailbox.data_len > 0) && ((XdrvMailbox.payload >= -30000) && (XdrvMailbox.payload <= 30000))) { + Settings.altitude = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.altitude); +} + +void CmndLedPower(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) { + if (99 == pin[GPIO_LEDLNK]) { XdrvMailbox.index = 1; } + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + Settings.ledstate &= 8; + uint32_t mask = 1 << (XdrvMailbox.index -1); + switch (XdrvMailbox.payload) { + case 0: + led_power &= (0xFF ^ mask); + Settings.ledstate = 0; + break; + case 1: + led_power |= mask; + Settings.ledstate = 8; + break; + case 2: + led_power ^= mask; + Settings.ledstate ^= 8; + break; + } + blinks = 0; + if (99 == pin[GPIO_LEDLNK]) { + SetLedPower(Settings.ledstate &8); + } else { + SetLedPowerIdx(XdrvMailbox.index -1, (led_power & mask)); + } + } + bool state = bitRead(led_power, XdrvMailbox.index -1); + if (99 == pin[GPIO_LEDLNK]) { + state = bitRead(Settings.ledstate, 3); + } + ResponseCmndIdxChar(GetStateText(state)); + } +} + +void CmndLedState(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_LED_OPTION)) { + Settings.ledstate = XdrvMailbox.payload; + if (!Settings.ledstate) { + SetLedPowerAll(0); + SetLedLink(0); + } + } + ResponseCmndNumber(Settings.ledstate); +} + +void CmndLedMask(void) +{ + if (XdrvMailbox.data_len > 0) { + Settings.ledmask = XdrvMailbox.payload; + } + char stemp1[TOPSZ]; + snprintf_P(stemp1, sizeof(stemp1), PSTR("%d (0x%04X)"), Settings.ledmask, Settings.ledmask); + ResponseCmndChar(stemp1); +} + +void CmndWifiPower(void) +{ + if (XdrvMailbox.data_len > 0) { + Settings.wifi_output_power = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); + if (Settings.wifi_output_power > 205) { + Settings.wifi_output_power = 205; + } + WifiSetOutputPower(); + } + ResponseCmndChar(WifiGetOutputPower().c_str()); +} + +#ifdef USE_I2C +void CmndI2cScan(void) +{ + if (i2c_flg) { + I2cScan(mqtt_data, sizeof(mqtt_data)); + } +} + +void CmndI2cDriver(void) +{ + if (XdrvMailbox.index < MAX_I2C_DRIVERS) { + if (XdrvMailbox.payload >= 0) { + bitWrite(Settings.i2c_drivers[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); + restart_flag = 2; + } + } + Response_P(PSTR("{\"" D_CMND_I2CDRIVER "\":")); + I2cDriverState(); + ResponseJsonEnd(); +} +#endif + +void CmndSensor(void) +{ + XsnsCall(FUNC_COMMAND_SENSOR); +} + +void CmndDriver(void) +{ + XdrvCall(FUNC_COMMAND_DRIVER); +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_crash_recorder.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_crash_recorder.ino" +const uint32_t crash_magic = 0x53415400; +const uint32_t crash_rtc_offset = 32; +const uint32_t crash_dump_max_len = 31; + + + + + + +extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) +{ + uint32_t addr_written = 0; + uint32_t value; + + for (uint32_t i = stack; i < stack_end; i += 4) { + value = *((uint32_t*) i); + if ((value >= 0x40000000) && (value < 0x40300000)) { + ESP.rtcUserMemoryWrite(crash_rtc_offset + addr_written, (uint32_t*)&value, sizeof(value)); + addr_written++; + if (addr_written >= crash_dump_max_len) { break; } + } + } + value = crash_magic + addr_written; + ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); +} + + +void CmndCrash(void) +{ + volatile uint32_t dummy; + dummy = *((uint32_t*) 0x00000000); +} + + +void CmndWDT(void) +{ + volatile uint32_t dummy = 0; + while (1) { + dummy++; + } +} + + +void CmndBlockedLoop(void) +{ + while (1) { + delay(1000); + } +} + + +void CrashDumpClear(void) +{ + uint32_t value = 0; + ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); +} + + + + + +bool CrashFlag(void) +{ + return ((ResetReason() == REASON_EXCEPTION_RST) || (ResetReason() == REASON_SOFT_WDT_RST) || oswatch_blocked_loop); +} + +void CrashDump(void) +{ + ResponseAppend_P(PSTR("{\"Exception\":%d,\"Reason\":\"%s\",\"EPC\":[\"%08x\",\"%08x\",\"%08x\"],\"EXCVADDR\":\"%08x\",\"DEPC\":\"%08x\""), + resetInfo.exccause, + GetResetReason().c_str(), + resetInfo.epc1, + resetInfo.epc2, + resetInfo.epc3, + resetInfo.excvaddr, + resetInfo.depc); + + uint32_t value; + ESP.rtcUserMemoryRead(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); + if (crash_magic == (value & 0xFFFFFF00)) { + ResponseAppend_P(PSTR(",\"CallChain\":[")); + uint32_t count = value & 0x3F; + for (uint32_t i = 0; i < count; i++) { + ESP.rtcUserMemoryRead(crash_rtc_offset +i, (uint32_t*)&value, sizeof(value)); + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"%08x\""), value); + } + ResponseAppend_P(PSTR("]")); + } + + ResponseJsonEnd(); +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_esptool.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_esptool.ino" +#define USE_ESPTOOL +#ifdef USE_ESPTOOL + + + + + + + +#define READ_REG(REG) (*((volatile uint32_t *)(REG))) +#define WRITE_REG(REG,VAL) *((volatile uint32_t *)(REG)) = (VAL) +#define REG_SET_MASK(reg,mask) WRITE_REG((reg), (READ_REG(reg)|(mask))) + +#define SPI_BASE_REG 0x60000200 + +#define SPI_CMD_REG (SPI_BASE_REG + 0x00) +#define SPI_FLASH_RDSR (1<<27) +#define SPI_FLASH_SE (1<<24) +#define SPI_FLASH_BE (1<<23) +#define SPI_FLASH_WREN (1<<30) + +#define SPI_ADDR_REG (SPI_BASE_REG + 0x04) +#define SPI_CTRL_REG (SPI_BASE_REG + 0x08) +#define SPI_RD_STATUS_REG (SPI_BASE_REG + 0x10) +#define SPI_W0_REG (SPI_BASE_REG + 0x40) +#define SPI_EXT2_REG (SPI_BASE_REG + 0xF8) + +#define SPI_ST 0x7 + + +#define SECTORS_PER_BLOCK (FLASH_BLOCK_SIZE / SPI_FLASH_SEC_SIZE) + + +static const uint32_t STATUS_WIP_BIT = (1 << 0); + + +inline static void spi_wait_ready(void) +{ + while((READ_REG(SPI_EXT2_REG) & SPI_ST)) { } +} + + + +static bool spiflash_is_ready(void) +{ + spi_wait_ready(); + WRITE_REG(SPI_RD_STATUS_REG, 0); + WRITE_REG(SPI_CMD_REG, SPI_FLASH_RDSR); + while(READ_REG(SPI_CMD_REG) != 0) { } + uint32_t status_value = READ_REG(SPI_RD_STATUS_REG); + return (status_value & STATUS_WIP_BIT) == 0; +} + +static void spi_write_enable(void) +{ + while(!spiflash_is_ready()) { } + WRITE_REG(SPI_CMD_REG, SPI_FLASH_WREN); + while(READ_REG(SPI_CMD_REG) != 0) { } +} + +bool EsptoolEraseSector(uint32_t sector) +{ + spi_write_enable(); + spi_wait_ready(); + + WRITE_REG(SPI_ADDR_REG, (sector * SPI_FLASH_SEC_SIZE) & 0xffffff); + WRITE_REG(SPI_CMD_REG, SPI_FLASH_SE); + while(READ_REG(SPI_CMD_REG) != 0) { } + while(!spiflash_is_ready()) { } + + return true; +} + +void EsptoolErase(uint32_t start_sector, uint32_t end_sector) +{ + int next_erase_sector = start_sector; + int remaining_erase_sector = end_sector - start_sector; + + while (remaining_erase_sector > 0) { + spi_write_enable(); + + uint32_t command = SPI_FLASH_SE; + uint32_t sectors_to_erase = 1; + if (remaining_erase_sector >= SECTORS_PER_BLOCK && + next_erase_sector % SECTORS_PER_BLOCK == 0) { + command = SPI_FLASH_BE; + sectors_to_erase = SECTORS_PER_BLOCK; + } + uint32_t addr = next_erase_sector * SPI_FLASH_SEC_SIZE; + + spi_wait_ready(); + WRITE_REG(SPI_ADDR_REG, addr & 0xffffff); + WRITE_REG(SPI_CMD_REG, command); + while(READ_REG(SPI_CMD_REG) != 0) { } + remaining_erase_sector -= sectors_to_erase; + next_erase_sector += sectors_to_erase; + + while (!spiflash_is_ready()) { } + yield(); + OsWatchLoop(); + } +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_features.ino" +# 24 "C:/shared/sonoff/Git/Tasmota/tasmota/support_features.ino" +void GetFeatures(void) +{ + feature_drv1 = 0x00000000; + +#ifdef USE_ENERGY_MARGIN_DETECTION + feature_drv1 |= 0x00000001; +#endif +#ifdef USE_LIGHT + feature_drv1 |= 0x00000002; +#endif +#ifdef USE_I2C + feature_drv1 |= 0x00000004; +#endif +#ifdef USE_SPI + feature_drv1 |= 0x00000008; +#endif +#ifdef USE_DISCOVERY + feature_drv1 |= 0x00000010; +#endif +#ifdef USE_ARDUINO_OTA + feature_drv1 |= 0x00000020; +#endif +#ifdef USE_MQTT_TLS + feature_drv1 |= 0x00000040; +#endif +#ifdef USE_WEBSERVER + feature_drv1 |= 0x00000080; +#endif +#ifdef WEBSERVER_ADVERTISE + feature_drv1 |= 0x00000100; +#endif +#ifdef USE_EMULATION_HUE + feature_drv1 |= 0x00000200; +#endif +#if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT) + feature_drv1 |= 0x00000400; +#endif +#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) + +#endif +#if (MQTT_LIBRARY_TYPE == MQTT_ESPMQTTARDUINO) + +#endif +#ifdef MQTT_HOST_DISCOVERY + feature_drv1 |= 0x00002000; +#endif +#ifdef USE_ARILUX_RF + feature_drv1 |= 0x00004000; +#endif +#if defined(USE_LIGHT) && defined(USE_WS2812) + feature_drv1 |= 0x00008000; +#endif +#ifdef USE_WS2812_DMA + feature_drv1 |= 0x00010000; +#endif +#if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL) + feature_drv1 |= 0x00020000; +#endif +#ifdef USE_IR_HVAC + feature_drv1 |= 0x00040000; +#endif +#ifdef USE_IR_RECEIVE + feature_drv1 |= 0x00080000; +#endif +#ifdef USE_DOMOTICZ + feature_drv1 |= 0x00100000; +#endif +#ifdef USE_DISPLAY + feature_drv1 |= 0x00200000; +#endif +#ifdef USE_HOME_ASSISTANT + feature_drv1 |= 0x00400000; +#endif +#ifdef USE_SERIAL_BRIDGE + feature_drv1 |= 0x00800000; +#endif +#ifdef USE_TIMERS + feature_drv1 |= 0x01000000; +#endif +#ifdef USE_SUNRISE + feature_drv1 |= 0x02000000; +#endif +#ifdef USE_TIMERS_WEB + feature_drv1 |= 0x04000000; +#endif +#ifdef USE_RULES + feature_drv1 |= 0x08000000; +#endif +#ifdef USE_KNX + feature_drv1 |= 0x10000000; +#endif +#ifdef USE_WPS + feature_drv1 |= 0x20000000; +#endif +#ifdef USE_SMARTCONFIG + feature_drv1 |= 0x40000000; +#endif +#ifdef USE_ENERGY_POWER_LIMIT + feature_drv1 |= 0x80000000; +#endif + + + + feature_drv2 = 0x00000000; + +#ifdef USE_CONFIG_OVERRIDE + feature_drv2 |= 0x00000001; +#endif +#ifdef FIRMWARE_MINIMAL + feature_drv2 |= 0x00000002; +#endif +#ifdef FIRMWARE_SENSORS + feature_drv2 |= 0x00000004; +#endif +#ifdef FIRMWARE_CLASSIC + feature_drv2 |= 0x00000008; +#endif +#ifdef FIRMWARE_KNX_NO_EMULATION + feature_drv2 |= 0x00000010; +#endif +#ifdef USE_DISPLAY_MODES1TO5 + feature_drv2 |= 0x00000020; +#endif +#ifdef USE_DISPLAY_GRAPH + feature_drv2 |= 0x00000040; +#endif +#ifdef USE_DISPLAY_LCD + feature_drv2 |= 0x00000080; +#endif +#ifdef USE_DISPLAY_SSD1306 + feature_drv2 |= 0x00000100; +#endif +#ifdef USE_DISPLAY_MATRIX + feature_drv2 |= 0x00000200; +#endif +#ifdef USE_DISPLAY_ILI9341 + feature_drv2 |= 0x00000400; +#endif +#ifdef USE_DISPLAY_EPAPER_29 + feature_drv2 |= 0x00000800; +#endif +#ifdef USE_DISPLAY_SH1106 + feature_drv2 |= 0x00001000; +#endif +#ifdef USE_MP3_PLAYER + feature_drv2 |= 0x00002000; +#endif +#ifdef USE_PCA9685 + feature_drv2 |= 0x00004000; +#endif +#if defined(USE_LIGHT) && defined(USE_TUYA_MCU) + feature_drv2 |= 0x00008000; +#endif +#ifdef USE_RC_SWITCH + feature_drv2 |= 0x00010000; +#endif +#if defined(USE_LIGHT) && defined(USE_ARMTRONIX_DIMMERS) + feature_drv2 |= 0x00020000; +#endif +#if defined(USE_LIGHT) && defined(USE_SM16716) + feature_drv2 |= 0x00040000; +#endif +#ifdef USE_SCRIPT + feature_drv2 |= 0x00080000; +#endif +#ifdef USE_EMULATION_WEMO + feature_drv2 |= 0x00100000; +#endif +#ifdef USE_SONOFF_IFAN + feature_drv2 |= 0x00200000; +#endif +#ifdef USE_ZIGBEE + feature_drv2 |= 0x00400000; +#endif +#ifdef NO_EXTRA_4K_HEAP + feature_drv2 |= 0x00800000; +#endif +#ifdef VTABLES_IN_IRAM + feature_drv2 |= 0x01000000; +#endif +#ifdef VTABLES_IN_DRAM + feature_drv2 |= 0x02000000; +#endif +#ifdef VTABLES_IN_FLASH + feature_drv2 |= 0x04000000; +#endif +#ifdef PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH + feature_drv2 |= 0x08000000; +#endif +#ifdef PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY + feature_drv2 |= 0x10000000; +#endif +#ifdef PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH + feature_drv2 |= 0x20000000; +#endif +#ifdef DEBUG_THEO + feature_drv2 |= 0x40000000; +#endif +#ifdef USE_DEBUG_DRIVER + feature_drv2 |= 0x80000000; +#endif + + + + feature_sns1 = 0x00000000; + +#ifdef USE_COUNTER + feature_sns1 |= 0x00000001; +#endif +#ifdef USE_ADC_VCC + feature_sns1 |= 0x00000002; +#endif +#ifdef USE_ENERGY_SENSOR + feature_sns1 |= 0x00000004; +#endif +#ifdef USE_PZEM004T + feature_sns1 |= 0x00000008; +#endif +#ifdef USE_DS18B20 + feature_sns1 |= 0x00000010; +#endif +#ifdef USE_DS18x20_LEGACY + feature_sns1 |= 0x00000020; +#endif +#ifdef USE_DS18x20 + feature_sns1 |= 0x00000040; +#endif +#ifdef USE_DHT + feature_sns1 |= 0x00000080; +#endif +#ifdef USE_SHT + feature_sns1 |= 0x00000100; +#endif +#ifdef USE_HTU + feature_sns1 |= 0x00000200; +#endif +#ifdef USE_BMP + feature_sns1 |= 0x00000400; +#endif +#ifdef USE_BME680 + feature_sns1 |= 0x00000800; +#endif +#ifdef USE_BH1750 + feature_sns1 |= 0x00001000; +#endif +#ifdef USE_VEML6070 + feature_sns1 |= 0x00002000; +#endif +#ifdef USE_ADS1115_I2CDEV + feature_sns1 |= 0x00004000; +#endif +#ifdef USE_ADS1115 + feature_sns1 |= 0x00008000; +#endif +#ifdef USE_INA219 + feature_sns1 |= 0x00010000; +#endif +#ifdef USE_SHT3X + feature_sns1 |= 0x00020000; +#endif +#ifdef USE_MHZ19 + feature_sns1 |= 0x00040000; +#endif +#ifdef USE_TSL2561 + feature_sns1 |= 0x00080000; +#endif +#ifdef USE_SENSEAIR + feature_sns1 |= 0x00100000; +#endif +#ifdef USE_PMS5003 + feature_sns1 |= 0x00200000; +#endif +#ifdef USE_MGS + feature_sns1 |= 0x00400000; +#endif +#ifdef USE_NOVA_SDS + feature_sns1 |= 0x00800000; +#endif +#ifdef USE_SGP30 + feature_sns1 |= 0x01000000; +#endif +#ifdef USE_SR04 + feature_sns1 |= 0x02000000; +#endif +#ifdef USE_SDM120 + feature_sns1 |= 0x04000000; +#endif +#ifdef USE_SI1145 + feature_sns1 |= 0x08000000; +#endif +#ifdef USE_SDM630 + feature_sns1 |= 0x10000000; +#endif +#ifdef USE_LM75AD + feature_sns1 |= 0x20000000; +#endif +#ifdef USE_APDS9960 + feature_sns1 |= 0x40000000; +#endif +#ifdef USE_TM1638 + feature_sns1 |= 0x80000000; +#endif + + + + feature_sns2 = 0x00000000; + +#ifdef USE_MCP230xx + feature_sns2 |= 0x00000001; +#endif +#ifdef USE_MPR121 + feature_sns2 |= 0x00000002; +#endif +#ifdef USE_CCS811 + feature_sns2 |= 0x00000004; +#endif +#ifdef USE_MPU6050 + feature_sns2 |= 0x00000008; +#endif +#ifdef USE_MCP230xx_OUTPUT + feature_sns2 |= 0x00000010; +#endif +#ifdef USE_MCP230xx_DISPLAYOUTPUT + feature_sns2 |= 0x00000020; +#endif +#ifdef USE_HLW8012 + feature_sns2 |= 0x00000040; +#endif +#ifdef USE_CSE7766 + feature_sns2 |= 0x00000080; +#endif +#ifdef USE_MCP39F501 + feature_sns2 |= 0x00000100; +#endif +#ifdef USE_PZEM_AC + feature_sns2 |= 0x00000200; +#endif +#ifdef USE_DS3231 + feature_sns2 |= 0x00000400; +#endif +#ifdef USE_HX711 + feature_sns2 |= 0x00000800; +#endif +#ifdef USE_PZEM_DC + feature_sns2 |= 0x00001000; +#endif +#ifdef USE_TX20_WIND_SENSOR + feature_sns2 |= 0x00002000; +#endif +#ifdef USE_MGC3130 + feature_sns2 |= 0x00004000; +#endif +#ifdef USE_RF_SENSOR + feature_sns2 |= 0x00008000; +#endif +#ifdef USE_THEO_V2 + feature_sns2 |= 0x00010000; +#endif +#ifdef USE_ALECTO_V2 + feature_sns2 |= 0x00020000; +#endif +#ifdef USE_AZ7798 + feature_sns2 |= 0x00040000; +#endif +#ifdef USE_MAX31855 + feature_sns2 |= 0x00080000; +#endif +#ifdef USE_PN532_HSU + feature_sns2 |= 0x00100000; +#endif +#ifdef USE_MAX44009 + feature_sns2 |= 0x00200000; +#endif +#ifdef USE_SCD30 + feature_sns2 |= 0x00400000; +#endif +#ifdef USE_HRE + feature_sns2 |= 0x00800000; +#endif +#ifdef USE_ADE7953 + feature_sns2 |= 0x01000000; +#endif +#ifdef USE_SPS30 + feature_sns2 |= 0x02000000; +#endif +#ifdef USE_VL53L0X + feature_sns2 |= 0x04000000; +#endif +#ifdef USE_MLX90614 + feature_sns2 |= 0x08000000; +#endif +#ifdef USE_MAX31865 + feature_sns2 |= 0x10000000; +#endif +#ifdef USE_CHIRP + feature_sns2 |= 0x20000000; +#endif +#ifdef USE_SOLAX_X1 + feature_sns2 |= 0x40000000; +#endif +#ifdef USE_PAJ7620 + feature_sns2 |= 0x80000000; +#endif + + + + feature5 = 0x00000000; + +#ifdef USE_BUZZER + feature5 |= 0x00000001; +#endif +#ifdef USE_RDM6300 + feature5 |= 0x00000002; +#endif +#ifdef USE_IBEACON + feature5 |= 0x00000004; +#endif +#ifdef USE_SML_M + feature5 |= 0x00000008; +#endif +#ifdef USE_INA226 + feature5 |= 0x00000010; +#endif +#ifdef USE_A4988_STEPPER + feature5 |= 0x00000020; +#endif +#ifdef USE_DDS2382 + feature5 |= 0x00000040; +#endif +#ifdef USE_SM2135 + feature5 |= 0x00000080; +#endif +#ifdef USE_SHUTTER + feature5 |= 0x00000100; +#endif +#ifdef USE_PCF8574 + feature5 |= 0x00000200; +#endif +#ifdef USE_DDSU666 + feature5 |= 0x00000400; +#endif +#ifdef USE_DEEPSLEEP + feature5 |= 0x00000800; +#endif +#ifdef USE_SONOFF_SC + feature5 |= 0x00001000; +#endif +#ifdef USE_SONOFF_RF + feature5 |= 0x00002000; +#endif +#ifdef USE_SONOFF_L1 + feature5 |= 0x00004000; +#endif +#ifdef USE_EXS_DIMMER + feature5 |= 0x00008000; +#endif +#ifdef USE_ARDUINO_SLAVE + feature5 |= 0x00010000; +#endif +#ifdef USE_HIH6 + feature5 |= 0x00020000; +#endif +#ifdef USE_HPMA + feature5 |= 0x00040000; +#endif +#ifdef USE_TSL2591 + feature5 |= 0x00080000; +#endif +#ifdef USE_DHT12 + feature5 |= 0x00100000; +#endif +#ifdef USE_DS1624 + feature5 |= 0x00200000; +#endif +#ifdef USE_GPS + feature5 |= 0x00400000; +#endif +#ifdef USE_HOTPLUG + feature5 |= 0x00800000; +#endif +#ifdef USE_NRF24 + feature5 |= 0x01000000; +#endif +#ifdef USE_MIBLE + feature5 |= 0x02000000; +#endif +#ifdef USE_HM10 + feature5 |= 0x04000000; +#endif +#ifdef USE_LE01MR + feature5 |= 0x08000000; +#endif +#ifdef USE_AHT10 + feature5 |= 0x10000000; +#endif + + + + + + + + feature6 = 0x00000000; +# 568 "C:/shared/sonoff/Git/Tasmota/tasmota/support_features.ino" +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_flash_log.ino" +# 39 "C:/shared/sonoff/Git/Tasmota/tasmota/support_flash_log.ino" +#ifdef USE_FLOG + +class FLOG + +#define MAGIC_WORD_FL 0x464c + +{ + +struct header_t{ + uint16_t magic_word; + uint16_t padding; + uint32_t physical_start_sector:10; + uint32_t number:10; + uint32_t buf_pointer:12; + }; + +private: +void _readSector(uint8_t one_sector); +void _eraseSector(uint8_t one_sector); +void _writeSector(uint8_t one_sector); +void _clearBuffer(void); +void _searchSaves(void); +void _findFirstErasedSector(void); +void _showBuffer(void); +void _initBuffer(void); +void _saveBufferToSector(void); +header_t _saved_header; + +public: + uint32_t size; + uint32_t start; + uint32_t end; + uint16_t num_sectors; + + uint16_t first_erased_sector; + uint16_t current_sector; + + uint16_t bytes_left; + uint16_t sectors_left; + + uint8_t mode = 0; + bool found_saved_data = false; + bool ready = false; + bool running_download = false; + bool recording = false; + + union sector_t{ + uint32_t dword_buffer[FLASH_SECTOR_SIZE/4]; + uint8_t byte_buffer[FLASH_SECTOR_SIZE]; + header_t header; + } sector; + + void init(void); + void addToBuffer(uint8_t src[], uint32_t size); + void startRecording(bool append); + void stopRecording(void); + + typedef void (*CallbackNoArgs) (); + typedef void (*CallbackWithArgs) (uint8_t *_record); + + void startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter); +}; + +extern "C" uint32_t _SPIFFS_start; +extern "C" uint32_t _FS_start; + + + + +void FLOG::init(void) +{ +DEBUG_SENSOR_LOG(PSTR("FLOG: init ...")); +size = ESP.getSketchSize(); + +start = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) +end = (uint32_t)&_SPIFFS_start - 0x40200000; +#else +end = (uint32_t)&_FS_start - 0x40200000; +#endif +num_sectors = (end - start)/FLASH_SECTOR_SIZE; +DEBUG_SENSOR_LOG(PSTR("FLOG: size: 0x%lx, start: 0x%lx, end: 0x%lx, num_sectors(dec): %lu"), size, start, end, num_sectors ); +_findFirstErasedSector(); +if(first_erased_sector == 0xffff){ + _eraseSector(0); + first_erased_sector = 0; +} +_searchSaves(); +_initBuffer(); +ready = true; +} +# 142 "C:/shared/sonoff/Git/Tasmota/tasmota/support_flash_log.ino" +void FLOG::_readSector(uint8_t one_sector){ + DEBUG_SENSOR_LOG(PSTR("FLOG: read sector number: %u" ), one_sector); + ESP.flashRead(start+(one_sector * FLASH_SECTOR_SIZE),(uint32_t *)§or.dword_buffer, FLASH_SECTOR_SIZE); +} + + + + + +void FLOG::_eraseSector(uint8_t one_sector){ + DEBUG_SENSOR_LOG(PSTR("FLOG: erasing sector number: %u" ), one_sector); + ESP.flashEraseSector((start/FLASH_SECTOR_SIZE)+one_sector); +} + + + + + +void FLOG::_writeSector(uint8_t one_sector){ + DEBUG_SENSOR_LOG(PSTR("FLOG: write buffer to sector number: %u" ), one_sector); + ESP.flashWrite(start+(one_sector * FLASH_SECTOR_SIZE),(uint32_t *)§or.dword_buffer, FLASH_SECTOR_SIZE); +} + + + + +void FLOG::_clearBuffer(){ + for (uint32_t i = sizeof(sector.header)/4; i<(sizeof(sector.dword_buffer)/4); i++){ + sector.dword_buffer[i] = 0; + } + sector.header.buf_pointer = sizeof(sector.header); + +} + + + + +void FLOG::_saveBufferToSector(){ + DEBUG_SENSOR_LOG(PSTR("FLOG: write buffer to current sector: %u" ),current_sector); + _writeSector(current_sector); + if(current_sector == num_sectors){ + current_sector = 0; + } + else{ + current_sector++; + } + _eraseSector(current_sector); + _clearBuffer(); + sector.header.number++; + DEBUG_SENSOR_LOG(PSTR("FLOG: new sector header number: %u" ),sector.header.number); +} + + + + + +void FLOG::_findFirstErasedSector(){ + for (uint32_t i = 0; i3){ + break; + } + } +} + + + + + + + +void FLOG::addToBuffer(uint8_t src[], uint32_t size){ + if(mode == 0){ + if(sector.header.number == num_sectors && !ready){ + return; + } + } + if((FLASH_SECTOR_SIZE-sector.header.buf_pointer-sizeof(sector.header))>size){ + + + + memcpy(sector.byte_buffer + sector.header.buf_pointer, src, size); + sector.header.buf_pointer+=size; + + } + else{ + DEBUG_SENSOR_LOG(PSTR("FLOG: save buffer to flash sector: %u"), current_sector); + DEBUG_SENSOR_LOG(PSTR("FLOG: current buf_pointer: %u"), sector.header.buf_pointer); + _saveBufferToSector(); + sectors_left++; + + if((FLASH_SECTOR_SIZE-sector.header.buf_pointer-sizeof(sector.header))>size){ + memcpy(sector.byte_buffer + sector.header.buf_pointer, src, size); + sector.header.buf_pointer+=size; + } + } +} + + + + + + +void FLOG::startRecording(bool append){ + if(recording){ + DEBUG_SENSOR_LOG(PSTR("FLOG: already recording")); + return; + } + recording = true; + DEBUG_SENSOR_LOG(PSTR("FLOG: start recording")); + _initBuffer(); + if(!found_saved_data) { + append = false; + } + if(append){ + sector.header.number = _saved_header.number+1; + sector.header.physical_start_sector = _saved_header.physical_start_sector; + } + else{ + sector.header.physical_start_sector = (uint16_t)first_erased_sector; + found_saved_data = false; + sectors_left = 0; + } + } + + + + + +void FLOG::stopRecording(void){ + _saveBufferToSector(); + _findFirstErasedSector(); + _searchSaves(); + _initBuffer(); + recording = false; + found_saved_data = true; + } +# 381 "C:/shared/sonoff/Git/Tasmota/tasmota/support_flash_log.ino" + void FLOG::startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter){ + + _readSector(sector.header.physical_start_sector); + uint32_t next_sector = sector.header.physical_start_sector; + bytes_left = sector.header.buf_pointer - sizeof(sector.header); + DEBUG_SENSOR_LOG(PSTR("FLOG: create file for download, will process %u bytes"), bytes_left); + running_download = true; + + sendHeader(); + + while(sectors_left){ + DEBUG_SENSOR_LOG(PSTR("FLOG: next sector: %u"), next_sector); + + uint32_t k = sizeof(sector.header); + while(bytes_left){ + + uint8_t *_record_start = (uint8_t*)§or.byte_buffer[k]; + + sendRecord(_record_start); + if(k%128 == 0){ + + OsWatchLoop(); + delay(sleep); + } + k+=size; + if(bytes_left>7){ + bytes_left-=size; + } + else{ + bytes_left = 0; + DEBUG_SENSOR_LOG(PSTR("FLOG: Flog->bytes_left not dividable by 8 ??????")); + } + } + next_sector++; + if(next_sector>num_sectors){ + next_sector = 0; + } + sectors_left--; + _readSector(next_sector); + bytes_left = sector.header.buf_pointer - sizeof(sector.header); + OsWatchLoop(); + delay(sleep); + } + running_download = false; + + sendFooter(); + + _searchSaves(); + _initBuffer(); + } + + #endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" +# 23 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" +float fmodf(float x, float y) +{ + + union {float f; uint32_t i;} ux = {x}, uy = {y}; + int ex = ux.i>>23 & 0xff; + int ey = uy.i>>23 & 0xff; + uint32_t sx = ux.i & 0x80000000; + uint32_t i; + uint32_t uxi = ux.i; + + if (uy.i<<1 == 0 || isnan(y) || ex == 0xff) + return (x*y)/(x*y); + if (uxi<<1 <= uy.i<<1) { + if (uxi<<1 == uy.i<<1) + return 0*x; + return x; + } + + + if (!ex) { + for (i = uxi<<9; i>>31 == 0; ex--, i <<= 1); + uxi <<= -ex + 1; + } else { + uxi &= -1U >> 9; + uxi |= 1U << 23; + } + if (!ey) { + for (i = uy.i<<9; i>>31 == 0; ey--, i <<= 1); + uy.i <<= -ey + 1; + } else { + uy.i &= -1U >> 9; + uy.i |= 1U << 23; + } + + + for (; ex > ey; ex--) { + i = uxi - uy.i; + if (i >> 31 == 0) { + if (i == 0) + return 0*x; + uxi = i; + } + uxi <<= 1; + } + i = uxi - uy.i; + if (i >> 31 == 0) { + if (i == 0) + return 0*x; + uxi = i; + } + for (; uxi>>23 == 0; uxi <<= 1, ex--); + + + if (ex > 0) { + uxi -= 1U << 23; + uxi |= (uint32_t)ex << 23; + } else { + uxi >>= -ex + 1; + } + uxi |= sx; + ux.i = uxi; + return ux.f; +} + + +double FastPrecisePow(double a, double b) +{ + + + int e = abs((int)b); + union { + double d; + int x[2]; + } u = { a }; + u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447); + u.x[0] = 0; + + + double r = 1.0; + while (e) { + if (e & 1) { + r *= a; + } + a *= a; + e >>= 1; + } + return r * u.d; +} + +float FastPrecisePowf(const float x, const float y) +{ + + return (float)FastPrecisePow(x, y); +} + +double TaylorLog(double x) +{ + + + if (x <= 0.0) { return NAN; } + double z = (x + 1) / (x - 1); + double step = ((x - 1) * (x - 1)) / ((x + 1) * (x + 1)); + double totalValue = 0; + double powe = 1; + for (uint32_t count = 0; count < 10; count++) { + z *= step; + double y = (1 / powe) * z; + totalValue = totalValue + y; + powe = powe + 2; + } + totalValue *= 2; +# 144 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" + return totalValue; +} +# 154 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" +inline float sinf(float x) { return sin_52(x); } +inline float cosf(float x) { return cos_52(x); } +inline float tanf(float x) { return tan_56(x); } +inline float atanf(float x) { return atan_66(x); } +inline float asinf(float x) { return asinf1(x); } +inline float acosf(float x) { return acosf1(x); } +inline float sqrtf(float x) { return sqrt1(x); } +inline float powf(float x, float y) { return FastPrecisePow(x, y); } + + +double const f_pi = 3.1415926535897932384626433; +double const f_twopi = 2.0 * f_pi; +double const f_two_over_pi = 2.0 / f_pi; +double const f_halfpi = f_pi / 2.0; +double const f_threehalfpi = 3.0 * f_pi / 2.0; +double const f_four_over_pi = 4.0 / f_pi; +double const f_qtrpi = f_pi / 4.0; +double const f_sixthpi = f_pi / 6.0; +double const f_tansixthpi = tan(f_sixthpi); +double const f_twelfthpi = f_pi / 12.0; +double const f_tantwelfthpi = tan(f_twelfthpi); +float const f_180pi = 180 / f_pi; +# 194 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" +float cos_52s(float x) +{ + const float c1 = 0.9999932946; + const float c2 = -0.4999124376; + const float c3 = 0.0414877472; + const float c4 = -0.0012712095; + + float x2 = x * x; + return (c1 + x2 * (c2 + x2 * (c3 + c4 * x2))); +} + + + + + + +float cos_52(float x) +{ + x = fmodf(x, f_twopi); + if (x < 0) { x = -x; } + int quad = int(x * (float)f_two_over_pi); + switch (quad) { + case 0: return cos_52s(x); + case 1: return -cos_52s((float)f_pi - x); + case 2: return -cos_52s(x-(float)f_pi); + case 3: return cos_52s((float)f_twopi - x); + } +} + + + + +float sin_52(float x) +{ + return cos_52((float)f_halfpi - x); +} +# 247 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" +float tan_56s(float x) +{ + const float c1 = -3.16783027; + const float c2 = 0.134516124; + const float c3 = -4.033321984; + + float x2 = x * x; + return (x * (c1 + c2 * x2) / (c3 + x2)); +} +# 267 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" +float tan_56(float x) +{ + x = fmodf(x, (float)f_twopi); + int octant = int(x * (float)f_four_over_pi); + switch (octant){ + case 0: return tan_56s(x * (float)f_four_over_pi); + case 1: return 1.0f / tan_56s(((float)f_halfpi - x) * (float)f_four_over_pi); + case 2: return -1.0f / tan_56s((x-(float)f_halfpi) * (float)f_four_over_pi); + case 3: return - tan_56s(((float)f_pi - x) * (float)f_four_over_pi); + case 4: return tan_56s((x-(float)f_pi) * (float)f_four_over_pi); + case 5: return 1.0f / tan_56s(((float)f_threehalfpi - x) * (float)f_four_over_pi); + case 6: return -1.0f / tan_56s((x-(float)f_threehalfpi) * (float)f_four_over_pi); + case 7: return - tan_56s(((float)f_twopi - x) * (float)f_four_over_pi); + } +} +# 296 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" +float atan_66s(float x) +{ + const float c1 = 1.6867629106; + const float c2 = 0.4378497304; + const float c3 = 1.6867633134; + + float x2 = x * x; + return (x * (c1 + x2 * c2) / (c3 + x2)); +} + + + + + +float atan_66(float x) +{ + float y; + bool complement= false; + bool region= false; + bool sign= false; + + if (x < 0) { + x = -x; + sign = true; + } + if (x > 1.0) { + x = 1.0 / x; + complement = true; + } + if (x > (float)f_tantwelfthpi) { + x = (x - (float)f_tansixthpi) / (1 + (float)f_tansixthpi * x); + region = true; + } + + y = atan_66s(x); + if (region) { y += (float)f_sixthpi; } + if (complement) { y = (float)f_halfpi-y; } + if (sign) { y = -y; } + return (y); +} + +float asinf1(float x) +{ + float d = 1.0f - x * x; + if (d < 0.0f) { return NAN; } + return 2 * atan_66(x / (1 + sqrt1(d))); +} + +float acosf1(float x) +{ + float d = 1.0f - x * x; + if (d < 0.0f) { return NAN; } + float y = asinf1(sqrt1(d)); + if (x >= 0.0f) { + return y; + } else { + return (float)f_pi - y; + } +} + + +float sqrt1(const float x) +{ + union { + int i; + float x; + } u; + u.x = x; + u.i = (1 << 29) + (u.i >> 1) - (1 << 22); + + + + + u.x = u.x + x / u.x; + u.x = 0.25f * u.x + x / u.x; + + return u.x; +} +# 387 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" +uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max, + uint16_t ito_min, uint16_t ito_max) { + + if (ifrom_min >= ifrom_max) { + if (ito_min > ito_max) { + return ito_max; + } else { + return ito_min; + } + } + + uint32_t num = inum; + uint32_t from_min = ifrom_min; + uint32_t from_max = ifrom_max; + uint32_t to_min = ito_min; + uint32_t to_max = ito_max; + + + num = (num > from_max ? from_max : (num < from_min ? from_min : num)); + + + if (to_min > to_max) { + + num = (from_max - num) + from_min; + to_min = ito_max; + to_max = ito_min; + } + + uint32_t numerator = (num - from_min) * (to_max - to_min); + uint32_t result; + if (numerator >= 0x80000000L) { + + result = numerator / (from_max - from_min) + to_min; + } else { + result = (((numerator * 2) / (from_max - from_min)) + 1) / 2 + to_min; + } + return (uint32_t) (result > to_max ? to_max : (result < to_min ? to_min : result)); +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_legacy_cores.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_legacy_cores.ino" +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + + + + + +void* memchr(const void* ptr, int value, size_t num) +{ + unsigned char *p = (unsigned char*)ptr; + while (num--) { + if (*p != (unsigned char)value) { + p++; + } else { + return p; + } + } + return 0; +} + + + +size_t strcspn(const char *str1, const char *str2) +{ + size_t ret = 0; + while (*str1) { + if (strchr(str2, *str1)) { + return ret; + } else { + str1++; + ret++; + } + } + return ret; +} + + + +char* strpbrk(const char *s1, const char *s2) +{ + while(*s1) { + if (strchr(s2, *s1++)) { + return (char*)--s1; + } + } + return 0; +} + + + +#ifndef __LONG_LONG_MAX__ +#define __LONG_LONG_MAX__ 9223372036854775807LL +#endif +#ifndef ULLONG_MAX +#define ULLONG_MAX (__LONG_LONG_MAX__ * 2ULL + 1) +#endif + +unsigned long long strtoull(const char *__restrict nptr, char **__restrict endptr, int base) +{ + const char *s = nptr; + char c; + do { c = *s++; } while (isspace((unsigned char)c)); + + int neg = 0; + if (c == '-') { + neg = 1; + c = *s++; + } else { + if (c == '+') { + c = *s++; + } + } + + if ((base == 0 || base == 16) && (c == '0') && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) { base = (c == '0') ? 8 : 10; } + + unsigned long long acc = 0; + int any = 0; + if (base > 1 && base < 37) { + unsigned long long cutoff = ULLONG_MAX / base; + int cutlim = ULLONG_MAX % base; + for ( ; ; c = *s++) { + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'Z') + c -= 'A' - 10; + else if (c >= 'a' && c <= 'z') + c -= 'a' - 10; + else + break; + + if (c >= base) + break; + + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = ULLONG_MAX; + } + else if (any && neg) { + acc = -acc; + } + } + + if (endptr != nullptr) { *endptr = (char *)(any ? s - 1 : nptr); } + + return acc; +} + +#endif + + + +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) + + + + + +void* memmove_P(void *dest, const void *src, size_t n) +{ + if (src > (void*)0x40000000) { + return memcpy_P(dest, src, n); + } else { + return memmove(dest, src, n); + } +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_rotary.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_rotary.ino" +#ifdef USE_LIGHT + +#ifdef ROTARY_V1 + + + + +struct ROTARY { + unsigned long debounce = 0; + uint8_t present = 0; + uint8_t state = 0; + uint8_t position = 128; + uint8_t last_position = 128; + uint8_t interrupts_in_use_count = 0; + uint8_t changed = 0; +} Rotary; + + + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 +void update_rotary(void) ICACHE_RAM_ATTR; +#endif + +void update_rotary(void) +{ + if (MI_DESK_LAMP == my_module_type) { + if (LightPowerIRAM()) { + + + + + uint8_t s = Rotary.state & 3; + if (digitalRead(pin[GPIO_ROT1A])) { s |= 4; } + if (digitalRead(pin[GPIO_ROT1B])) { s |= 8; } + switch (s) { + case 0: case 5: case 10: case 15: + break; + case 1: case 7: case 8: case 14: + Rotary.position++; break; + case 2: case 4: case 11: case 13: + Rotary.position--; break; + case 3: case 12: + Rotary.position = Rotary.position + 2; break; + default: + Rotary.position = Rotary.position - 2; break; + } + Rotary.state = (s >> 2); + } + } +} + +bool RotaryButtonPressed(void) +{ + if ((MI_DESK_LAMP == my_module_type) && (Rotary.changed) && LightPower()) { + Rotary.changed = 0; + return true; + } + return false; +} + +void RotaryInit(void) +{ + Rotary.present = 0; + if ((pin[GPIO_ROT1A] < 99) && (pin[GPIO_ROT1B] < 99)) { + Rotary.present++; + pinMode(pin[GPIO_ROT1A], INPUT_PULLUP); + pinMode(pin[GPIO_ROT1B], INPUT_PULLUP); + + + + + if ((pin[GPIO_ROT1A] < 6) || (pin[GPIO_ROT1A] > 11)) { + attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1A]), update_rotary, CHANGE); + Rotary.interrupts_in_use_count++; + } + if ((pin[GPIO_ROT1B] < 6) || (pin[GPIO_ROT1B] > 11)) { + attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1B]), update_rotary, CHANGE); + Rotary.interrupts_in_use_count++; + } + } +} + + + + + +void RotaryHandler(void) +{ + if (Rotary.interrupts_in_use_count < 2) { + noInterrupts(); + update_rotary(); + } else { + noInterrupts(); + } + if (Rotary.last_position != Rotary.position) { + if (MI_DESK_LAMP == my_module_type) { + if (Button.hold_timer[0]) { + Rotary.changed = 1; + + int16_t t = LightGetColorTemp(); + t = t + (Rotary.position - Rotary.last_position); + if (t < 153) { + t = 153; + } + if (t > 500) { + t = 500; + } + DEBUG_CORE_LOG(PSTR("ROT: " D_CMND_COLORTEMPERATURE " %d"), Rotary.position - Rotary.last_position); + LightSetColorTemp((uint16_t)t); + } else { + int8_t d = Settings.light_dimmer; + d = d + (Rotary.position - Rotary.last_position); + if (d < 1) { + d = 1; + } + if (d > 100) { + d = 100; + } + DEBUG_CORE_LOG(PSTR("ROT: " D_CMND_DIMMER " %d"), Rotary.position - Rotary.last_position); + + LightSetDimmer((uint8_t)d); + Settings.light_dimmer = d; + } + } + Rotary.last_position = 128; + Rotary.position = 128; + } + interrupts(); +} + +void RotaryLoop(void) +{ + if (Rotary.present) { + if (TimeReached(Rotary.debounce)) { + SetNextTimeInterval(Rotary.debounce, Settings.button_debounce); + RotaryHandler(); + } + } +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_rtc.ino" +# 25 "C:/shared/sonoff/Git/Tasmota/tasmota/support_rtc.ino" +const uint32_t SECS_PER_MIN = 60UL; +const uint32_t SECS_PER_HOUR = 3600UL; +const uint32_t SECS_PER_DAY = SECS_PER_HOUR * 24UL; +const uint32_t MINS_PER_HOUR = 60UL; + +#define LEAP_YEAR(Y) (((1970+Y)>0) && !((1970+Y)%4) && (((1970+Y)%100) || !((1970+Y)%400))) + +extern "C" { +#include "sntp.h" +} +#include + +Ticker TickerRtc; + +static const uint8_t kDaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static const char kMonthNamesEnglish[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; + +struct RTC { + uint32_t utc_time = 0; + uint32_t local_time = 0; + uint32_t daylight_saving_time = 0; + uint32_t standard_time = 0; + uint32_t ntp_time = 0; + uint32_t midnight = 0; + uint32_t restart_time = 0; + int32_t drift_time = 0; + int32_t time_timezone = 0; + uint8_t ntp_sync_minute = 0; + bool midnight_now = false; + bool user_time_entry = false; +} Rtc; + +uint32_t UtcTime(void) +{ + return Rtc.utc_time; +} + +uint32_t LocalTime(void) +{ + return Rtc.local_time; +} + +int32_t DriftTime(void) +{ + return Rtc.drift_time; +} + +uint32_t Midnight(void) +{ + return Rtc.midnight; +} + +bool MidnightNow(void) +{ + if (Rtc.midnight_now) { + Rtc.midnight_now = false; + return true; + } + return false; +} + +bool IsDst(void) +{ + if (Rtc.time_timezone == Settings.toffset[1]) { + return true; + } + return false; +} + +String GetBuildDateAndTime(void) +{ + + char bdt[21]; + char *p; + char mdate[] = __DATE__; + char *smonth = mdate; + int day = 0; + int year = 0; + + + uint8_t i = 0; + for (char *str = strtok_r(mdate, " ", &p); str && i < 3; str = strtok_r(nullptr, " ", &p)) { + switch (i++) { + case 0: + smonth = str; + break; + case 1: + day = atoi(str); + break; + case 2: + year = atoi(str); + } + } + int month = (strstr(kMonthNamesEnglish, smonth) -kMonthNamesEnglish) /3 +1; + snprintf_P(bdt, sizeof(bdt), PSTR("%d" D_YEAR_MONTH_SEPARATOR "%02d" D_MONTH_DAY_SEPARATOR "%02d" D_DATE_TIME_SEPARATOR "%s"), year, month, day, __TIME__); + return String(bdt); +} + +String GetMinuteTime(uint32_t minutes) +{ + char tm[6]; + snprintf_P(tm, sizeof(tm), PSTR("%02d:%02d"), minutes / 60, minutes % 60); + + return String(tm); +} + +String GetTimeZone(void) +{ + char tz[7]; + snprintf_P(tz, sizeof(tz), PSTR("%+03d:%02d"), Rtc.time_timezone / 60, abs(Rtc.time_timezone % 60)); + + return String(tz); +} + +String GetDuration(uint32_t time) +{ + char dt[16]; + + TIME_T ut; + BreakTime(time, ut); + + + + + + + snprintf_P(dt, sizeof(dt), PSTR("%dT%02d:%02d:%02d"), ut.days, ut.hour, ut.minute, ut.second); + + return String(dt); +} + +String GetDT(uint32_t time) +{ + + + char dt[20]; + TIME_T tmpTime; + + BreakTime(time, tmpTime); + snprintf_P(dt, sizeof(dt), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"), + tmpTime.year +1970, tmpTime.month, tmpTime.day_of_month, tmpTime.hour, tmpTime.minute, tmpTime.second); + + return String(dt); +} +# 181 "C:/shared/sonoff/Git/Tasmota/tasmota/support_rtc.ino" +String GetDateAndTime(uint8_t time_type) +{ + + uint32_t time = Rtc.local_time; + + switch (time_type) { + case DT_BOOTCOUNT: + time = Settings.bootcount_reset_time; + break; + case DT_ENERGY: + time = Settings.energy_kWhtotal_time; + break; + case DT_UTC: + time = Rtc.utc_time; + break; + case DT_RESTART: + if (Rtc.restart_time == 0) { + return ""; + } + time = Rtc.restart_time; + break; + } + String dt = GetDT(time); + if (Settings.flag3.time_append_timezone && (DT_LOCAL == time_type)) { + dt += GetTimeZone(); + } + return dt; +} + +String GetTime(int type) +{ + + + + + char stime[25]; + + uint32_t time = Rtc.utc_time; + if (1 == type) time = Rtc.local_time; + if (2 == type) time = Rtc.daylight_saving_time; + if (3 == type) time = Rtc.standard_time; + snprintf_P(stime, sizeof(stime), sntp_get_real_time(time)); + + return String(stime); +} + +uint32_t UpTime(void) +{ + if (Rtc.restart_time) { + return Rtc.utc_time - Rtc.restart_time; + } else { + return uptime; + } +} + +uint32_t MinutesUptime(void) +{ + return (UpTime() / 60); +} + +String GetUptime(void) +{ + return GetDuration(UpTime()); +} + +uint32_t MinutesPastMidnight(void) +{ + uint32_t minutes = 0; + + if (RtcTime.valid) { + minutes = (RtcTime.hour *60) + RtcTime.minute; + } + return minutes; +} + +void BreakTime(uint32_t time_input, TIME_T &tm) +{ + + + + + uint8_t year; + uint8_t month; + uint8_t month_length; + uint32_t time; + unsigned long days; + + time = time_input; + tm.second = time % 60; + time /= 60; + tm.minute = time % 60; + time /= 60; + tm.hour = time % 24; + time /= 24; + tm.days = time; + tm.day_of_week = ((time + 4) % 7) + 1; + + year = 0; + days = 0; + while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { + year++; + } + tm.year = year; + + days -= LEAP_YEAR(year) ? 366 : 365; + time -= days; + tm.day_of_year = time; + + for (month = 0; month < 12; month++) { + if (1 == month) { + if (LEAP_YEAR(year)) { + month_length = 29; + } else { + month_length = 28; + } + } else { + month_length = kDaysInMonth[month]; + } + + if (time >= month_length) { + time -= month_length; + } else { + break; + } + } + strlcpy(tm.name_of_month, kMonthNames + (month *3), 4); + tm.month = month + 1; + tm.day_of_month = time + 1; + tm.valid = (time_input > START_VALID_TIME); +} + +uint32_t MakeTime(TIME_T &tm) +{ + + + + int i; + uint32_t seconds; + + + seconds = tm.year * (SECS_PER_DAY * 365); + for (i = 0; i < tm.year; i++) { + if (LEAP_YEAR(i)) { + seconds += SECS_PER_DAY; + } + } + + + for (i = 1; i < tm.month; i++) { + if ((2 == i) && LEAP_YEAR(tm.year)) { + seconds += SECS_PER_DAY * 29; + } else { + seconds += SECS_PER_DAY * kDaysInMonth[i-1]; + } + } + seconds+= (tm.day_of_month - 1) * SECS_PER_DAY; + seconds+= tm.hour * SECS_PER_HOUR; + seconds+= tm.minute * SECS_PER_MIN; + seconds+= tm.second; + return seconds; +} + +uint32_t RuleToTime(TimeRule r, int yr) +{ + TIME_T tm; + uint32_t t; + uint8_t m; + uint8_t w; + + m = r.month; + w = r.week; + if (0 == w) { + if (++m > 12) { + m = 1; + yr++; + } + w = 1; + } + + tm.hour = r.hour; + tm.minute = 0; + tm.second = 0; + tm.day_of_month = 1; + tm.month = m; + tm.year = yr - 1970; + t = MakeTime(tm); + BreakTime(t, tm); + t += (7 * (w - 1) + (r.dow - tm.day_of_week + 7) % 7) * SECS_PER_DAY; + if (0 == r.week) { + t -= 7 * SECS_PER_DAY; + } + return t; +} + +void RtcSecond(void) +{ + TIME_T tmpTime; + + if (!Rtc.user_time_entry && !global_state.wifi_down) { + uint8_t uptime_minute = (uptime / 60) % 60; + if ((Rtc.ntp_sync_minute > 59) && (uptime_minute > 2)) { + Rtc.ntp_sync_minute = 1; + } + uint8_t offset = (uptime < 30) ? RtcTime.second : (((ESP.getChipId() & 0xF) * 3) + 3) ; + if ( (((offset == RtcTime.second) && ( (RtcTime.year < 2016) || + (Rtc.ntp_sync_minute == uptime_minute))) || + ntp_force_sync ) ) { + Rtc.ntp_time = sntp_get_current_timestamp(); + if (Rtc.ntp_time > START_VALID_TIME) { + ntp_force_sync = false; + if (Rtc.utc_time > START_VALID_TIME) { Rtc.drift_time = Rtc.ntp_time - Rtc.utc_time; } + Rtc.utc_time = Rtc.ntp_time; + Rtc.ntp_sync_minute = 60; + if (Rtc.restart_time == 0) { + Rtc.restart_time = Rtc.utc_time - uptime; + } + BreakTime(Rtc.utc_time, tmpTime); + RtcTime.year = tmpTime.year + 1970; + Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); + Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); + + + PrepLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: Drift %d, (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + DriftTime(), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); + + if (Rtc.local_time < START_VALID_TIME) { + rules_flag.time_init = 1; + } else { + rules_flag.time_set = 1; + } + } else { + Rtc.ntp_sync_minute++; + } + } + } + + Rtc.utc_time++; + Rtc.local_time = Rtc.utc_time; + if (Rtc.local_time > START_VALID_TIME) { + int16_t timezone_minutes = Settings.timezone_minutes; + if (Settings.timezone < 0) { timezone_minutes *= -1; } + Rtc.time_timezone = (Settings.timezone * SECS_PER_HOUR) + (timezone_minutes * SECS_PER_MIN); + if (99 == Settings.timezone) { + int32_t dstoffset = Settings.toffset[1] * SECS_PER_MIN; + int32_t stdoffset = Settings.toffset[0] * SECS_PER_MIN; + if (Settings.tflag[1].hemis) { + + if ((Rtc.utc_time >= (Rtc.standard_time - dstoffset)) && (Rtc.utc_time < (Rtc.daylight_saving_time - stdoffset))) { + Rtc.time_timezone = stdoffset; + } else { + Rtc.time_timezone = dstoffset; + } + } else { + + if ((Rtc.utc_time >= (Rtc.daylight_saving_time - stdoffset)) && (Rtc.utc_time < (Rtc.standard_time - dstoffset))) { + Rtc.time_timezone = dstoffset; + } else { + Rtc.time_timezone = stdoffset; + } + } + } + Rtc.local_time += Rtc.time_timezone; + Rtc.time_timezone /= 60; + if (!Settings.energy_kWhtotal_time) { + Settings.energy_kWhtotal_time = Rtc.local_time; + } + if (Settings.bootcount_reset_time < START_VALID_TIME) { + Settings.bootcount_reset_time = Rtc.local_time; + } + } + + BreakTime(Rtc.local_time, RtcTime); + if (RtcTime.valid) { + if (!Rtc.midnight) { + Rtc.midnight = Rtc.local_time - (RtcTime.hour * 3600) - (RtcTime.minute * 60) - RtcTime.second; + } + if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { + Rtc.midnight = Rtc.local_time; + Rtc.midnight_now = true; + } + } + + RtcTime.year += 1970; +} + +void RtcSetTime(uint32_t epoch) +{ + if (epoch < START_VALID_TIME) { + Rtc.user_time_entry = false; + ntp_force_sync = true; + sntp_init(); + } else { + sntp_stop(); + Rtc.user_time_entry = true; + Rtc.utc_time = epoch -1; + } + RtcSecond(); +} + +void RtcInit(void) +{ + sntp_setservername(0, SettingsText(SET_NTPSERVER1)); + sntp_setservername(1, SettingsText(SET_NTPSERVER2)); + sntp_setservername(2, SettingsText(SET_NTPSERVER3)); + sntp_stop(); + sntp_set_timezone(0); + sntp_init(); + Rtc.utc_time = 0; + BreakTime(Rtc.utc_time, RtcTime); + TickerRtc.attach(1, RtcSecond); +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_static_buffer.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_static_buffer.ino" +typedef struct SBuffer_impl { + uint16_t size; + uint16_t len; + uint8_t buf[]; +} SBuffer_impl; + + + +typedef class SBuffer { + +protected: + SBuffer(void) { + + } + +public: + SBuffer(const size_t size) { + _buf = (SBuffer_impl*) new char[size+4]; + _buf->size = size; + _buf->len = 0; + + } + + inline size_t getSize(void) const { return _buf->size; } + inline size_t size(void) const { return _buf->size; } + inline size_t getLen(void) const { return _buf->len; } + inline size_t len(void) const { return _buf->len; } + inline uint8_t *getBuffer(void) const { return _buf->buf; } + inline uint8_t *buf(size_t i = 0) const { return &_buf->buf[i]; } + inline char *charptr(size_t i = 0) const { return (char*) &_buf->buf[i]; } + + virtual ~SBuffer(void) { + delete[] _buf; + } + + inline void setLen(const size_t len) { + uint16_t old_len = _buf->len; + _buf->len = (len <= _buf->size) ? len : _buf->size; + if (old_len < _buf->len) { + memset((void*) &_buf->buf[old_len], 0, _buf->len - old_len); + } + } + + void set8(const size_t offset, const uint8_t data) { + if (offset < _buf->len) { + _buf->buf[offset] = data; + } + } + + size_t add8(const uint8_t data) { + if (_buf->len < _buf->size) { + _buf->buf[_buf->len++] = data; + } + return _buf->len; + } + size_t add16(const uint16_t data) { + if (_buf->len < _buf->size - 1) { + _buf->buf[_buf->len++] = data; + _buf->buf[_buf->len++] = data >> 8; + } + return _buf->len; + } + size_t add32(const uint32_t data) { + if (_buf->len < _buf->size - 3) { + _buf->buf[_buf->len++] = data; + _buf->buf[_buf->len++] = data >> 8; + _buf->buf[_buf->len++] = data >> 16; + _buf->buf[_buf->len++] = data >> 24; + } + return _buf->len; + } + size_t add64(const uint64_t data) { + if (_buf->len < _buf->size - 7) { + _buf->buf[_buf->len++] = data; + _buf->buf[_buf->len++] = data >> 8; + _buf->buf[_buf->len++] = data >> 16; + _buf->buf[_buf->len++] = data >> 24; + _buf->buf[_buf->len++] = data >> 32; + _buf->buf[_buf->len++] = data >> 40; + _buf->buf[_buf->len++] = data >> 48; + _buf->buf[_buf->len++] = data >> 56; + } + return _buf->len; + } + + size_t addBuffer(const SBuffer &buf2) { + if (len() + buf2.len() <= size()) { + for (uint32_t i = 0; i < buf2.len(); i++) { + _buf->buf[_buf->len++] = buf2.buf()[i]; + } + } + return _buf->len; + } + + size_t addBuffer(const uint8_t *buf2, size_t len2) { + if (len() + len2 <= size()) { + for (uint32_t i = 0; i < len2; i++) { + _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); + } + } + return _buf->len; + } + + size_t addBuffer(const char *buf2, size_t len2) { + if (len() + len2 <= size()) { + for (uint32_t i = 0; i < len2; i++) { + _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); + } + } + return _buf->len; + } + + uint8_t get8(size_t offset) const { + if (offset < _buf->len) { + return _buf->buf[offset]; + } else { + return 0; + } + } + uint8_t read8(const size_t offset) const { + if (offset < len()) { + return _buf->buf[offset]; + } + return 0; + } + uint16_t get16(const size_t offset) const { + if (offset < len() - 1) { + return _buf->buf[offset] | (_buf->buf[offset+1] << 8); + } + return 0; + } + uint32_t get32(const size_t offset) const { + if (offset < len() - 3) { + return _buf->buf[offset] | (_buf->buf[offset+1] << 8) | + (_buf->buf[offset+2] << 16) | (_buf->buf[offset+3] << 24); + } + return 0; + } + uint64_t get64(const size_t offset) const { + if (offset < len() - 7) { + return (uint64_t)_buf->buf[offset] | ((uint64_t)_buf->buf[offset+1] << 8) | + ((uint64_t)_buf->buf[offset+2] << 16) | ((uint64_t)_buf->buf[offset+3] << 24) | + ((uint64_t)_buf->buf[offset+4] << 32) | ((uint64_t)_buf->buf[offset+5] << 40) | + ((uint64_t)_buf->buf[offset+6] << 48) | ((uint64_t)_buf->buf[offset+7] << 56); + } + return 0; + } + + + inline size_t strlen(const size_t offset) const { + return strnlen((const char*) &_buf->buf[offset], len() - offset); + } + + size_t strlen_s(const size_t offset) const { + size_t slen = this->strlen(offset); + if (slen == len() - offset) { + return 0; + } else { + return slen; + } + } + + SBuffer subBuffer(const size_t start, size_t len) const { + if (start >= _buf->len) { + len = 0; + } else if (start + len > _buf->len) { + len = _buf->len - start; + } + + SBuffer buf2(len); + memcpy(buf2.buf(), buf()+start, len); + buf2._buf->len = len; + return buf2; + } + + static SBuffer SBufferFromHex(const char *hex, size_t len) { + size_t buf_len = (len + 3) / 2; + SBuffer buf2(buf_len); + uint8_t val; + + for (; len > 1; len -= 2) { + val = asc2byte(*hex++) << 4; + val |= asc2byte(*hex++); + buf2.add8(val); + } + return buf2; + } + +protected: + + static uint8_t asc2byte(char chr) { + uint8_t rVal = 0; + if (isdigit(chr)) { rVal = chr - '0'; } + else if (chr >= 'A' && chr <= 'F') { rVal = chr + 10 - 'A'; } + else if (chr >= 'a' && chr <= 'f') { rVal = chr + 10 - 'a'; } + return rVal; + } + + static void unHex(const char* in, uint8_t *out, size_t len) { + } + +protected: + SBuffer_impl * _buf; + +} SBuffer; + +typedef class PreAllocatedSBuffer : public SBuffer { + +public: + PreAllocatedSBuffer(const size_t size, void * buffer) { + _buf = (SBuffer_impl*) buffer; + _buf->size = size - 4; + _buf->len = 0; + } + + ~PreAllocatedSBuffer(void) { + + _buf = nullptr; + } +} PreAllocatedSBuffer; +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_statistics.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_statistics.ino" +#define USE_STATS_CODE + +#ifdef USE_STATS_CODE + + + + +String GetStatistics(void) +{ + char data[40]; + snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/%d\""), GetSettingsTextLen(), settings_text_size); + return String(data); +} + +#else + +String GetStatistics(void) +{ + return String(""); +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_switch.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_switch.ino" +#define SWITCH_V2 +#ifdef SWITCH_V2 + + + + + + +const uint8_t SWITCH_PROBE_INTERVAL = 10; + +#include + +Ticker TickerSwitch; + +struct SWITCH { + unsigned long debounce = 0; + uint16_t no_pullup_mask = 0; + uint8_t state[MAX_SWITCHES] = { 0 }; + uint8_t last_state[MAX_SWITCHES]; + uint8_t hold_timer[MAX_SWITCHES] = { 0 }; + uint8_t virtual_state[MAX_SWITCHES]; + uint8_t present = 0; +} Switch; + + + +void SwitchPullupFlag(uint16 switch_bit) +{ + bitSet(Switch.no_pullup_mask, switch_bit); +} + +void SwitchSetVirtual(uint32_t index, uint8_t state) +{ + Switch.virtual_state[index] = state; +} + +uint8_t SwitchGetVirtual(uint32_t index) +{ + return Switch.virtual_state[index]; +} + +uint8_t SwitchLastState(uint32_t index) +{ + return Switch.last_state[index]; +} + +bool SwitchState(uint32_t index) +{ + uint32_t switchmode = Settings.switchmode[index]; + return ((FOLLOW_INV == switchmode) || + (PUSHBUTTON_INV == switchmode) || + (PUSHBUTTONHOLD_INV == switchmode) || + (FOLLOWMULTI_INV == switchmode) || + (PUSHHOLDMULTI_INV == switchmode) + ) ^ Switch.last_state[index]; +} + + + +void SwitchProbe(void) +{ + if (uptime < 4) { return; } + + uint8_t state_filter = Settings.switch_debounce / SWITCH_PROBE_INTERVAL; + uint8_t force_high = (Settings.switch_debounce % 50) &1; + uint8_t force_low = (Settings.switch_debounce % 50) &2; + + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + if (pin[GPIO_SWT1 +i] < 99) { + + if (1 == digitalRead(pin[GPIO_SWT1 +i])) { + + if (force_high) { + if (1 == Switch.virtual_state[i]) { + Switch.state[i] = state_filter; + } + } + + if (Switch.state[i] < state_filter) { + Switch.state[i]++; + if (state_filter == Switch.state[i]) { + Switch.virtual_state[i] = 1; + } + } + } else { + + if (force_low) { + if (0 == Switch.virtual_state[i]) { + Switch.state[i] = 0; + } + } + + if (Switch.state[i] > 0) { + Switch.state[i]--; + if (0 == Switch.state[i]) { + Switch.virtual_state[i] = 0; + } + } + } + } + } + TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); +} + +void SwitchInit(void) +{ + Switch.present = 0; + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + Switch.last_state[i] = 1; + if (pin[GPIO_SWT1 +i] < 99) { + Switch.present++; + pinMode(pin[GPIO_SWT1 +i], bitRead(Switch.no_pullup_mask, i) ? INPUT : ((16 == pin[GPIO_SWT1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); + Switch.last_state[i] = digitalRead(pin[GPIO_SWT1 +i]); + } + Switch.virtual_state[i] = Switch.last_state[i]; + } + if (Switch.present) { TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); } +} + + + + + +void SwitchHandler(uint8_t mode) +{ + if (uptime < 4) { return; } + + uint16_t loops_per_second = 1000 / Settings.switch_debounce; + + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + if ((pin[GPIO_SWT1 +i] < 99) || (mode)) { + uint8_t button = Switch.virtual_state[i]; + uint8_t switchflag = POWER_TOGGLE +1; + + if (Switch.hold_timer[i]) { + Switch.hold_timer[i]--; + if (0 == Switch.hold_timer[i]) { + + switch (Settings.switchmode[i]) { + case TOGGLEMULTI: + switchflag = POWER_TOGGLE; + break; + case FOLLOWMULTI: + switchflag = button &1; + break; + case FOLLOWMULTI_INV: + switchflag = ~button &1; + break; + case PUSHHOLDMULTI: + if (NOT_PRESSED == button){ + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 25; + SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); + } + else + SendKey(KEY_SWITCH, i +1, POWER_CLEAR); + break; + case PUSHHOLDMULTI_INV: + if (PRESSED == button){ + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 25; + SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); + } + else + SendKey(KEY_SWITCH, i +1, POWER_CLEAR); + break; + default: + SendKey(KEY_SWITCH, i +1, POWER_HOLD); + break; + } + } + } + + + + if (button != Switch.last_state[i]) { + switch (Settings.switchmode[i]) { + case TOGGLE: + case PUSHBUTTON_TOGGLE: + switchflag = POWER_TOGGLE; + break; + case FOLLOW: + switchflag = button &1; + break; + case FOLLOW_INV: + switchflag = ~button &1; + break; + case PUSHBUTTON: + if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i])) { + switchflag = POWER_TOGGLE; + } + break; + case PUSHBUTTON_INV: + if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i])) { + switchflag = POWER_TOGGLE; + } + break; + case PUSHBUTTONHOLD: + if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i])) { + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; + } + if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i]) && (Switch.hold_timer[i])) { + Switch.hold_timer[i] = 0; + switchflag = POWER_TOGGLE; + } + break; + case PUSHBUTTONHOLD_INV: + if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i])) { + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; + } + if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i]) && (Switch.hold_timer[i])) { + Switch.hold_timer[i] = 0; + switchflag = POWER_TOGGLE; + } + break; + case TOGGLEMULTI: + case FOLLOWMULTI: + case FOLLOWMULTI_INV: + if (Switch.hold_timer[i]) { + Switch.hold_timer[i] = 0; + SendKey(KEY_SWITCH, i +1, POWER_HOLD); + } else { + Switch.hold_timer[i] = loops_per_second / 2; + } + break; + case PUSHHOLDMULTI: + if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i])) { + if(Switch.hold_timer[i]!=0) + SendKey(KEY_SWITCH, i +1, POWER_INV); + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; + } + if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i])) { + if(Switch.hold_timer[i] > loops_per_second * Settings.param[P_HOLD_TIME] / 25) + switchflag = POWER_TOGGLE; + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; + } + break; + case PUSHHOLDMULTI_INV: + if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i])) { + if(Switch.hold_timer[i]!=0) + SendKey(KEY_SWITCH, i +1, POWER_INV); + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; + } + if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i])) { + if(Switch.hold_timer[i] > loops_per_second * Settings.param[P_HOLD_TIME] / 25) + switchflag = POWER_TOGGLE; + Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; + } + break; + } + Switch.last_state[i] = button; + } + if (switchflag <= POWER_TOGGLE) { + if (!SendKey(KEY_SWITCH, i +1, switchflag)) { + ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); + } + } + } + } +} + +void SwitchLoop(void) +{ + if (Switch.present) { + if (TimeReached(Switch.debounce)) { + SetNextTimeInterval(Switch.debounce, Settings.switch_debounce); + SwitchHandler(0); + } + } +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" +const char kSleepMode[] PROGMEM = "Dynamic|Normal"; +const char kPrefixes[] PROGMEM = D_CMND "|" D_STAT "|" D_TELE; + +char* Format(char* output, const char* input, int size) +{ + char *token; + uint32_t digits = 0; + + if (strstr(input, "%") != nullptr) { + strlcpy(output, input, size); + token = strtok(output, "%"); + if (strstr(input, "%") == input) { + output[0] = '\0'; + } else { + token = strtok(nullptr, ""); + } + if (token != nullptr) { + digits = atoi(token); + if (digits) { + char tmp[size]; + if (strchr(token, 'd')) { + snprintf_P(tmp, size, PSTR("%s%c0%dd"), output, '%', digits); + snprintf_P(output, size, tmp, ESP.getChipId() & 0x1fff); + } else { + snprintf_P(tmp, size, PSTR("%s%c0%dX"), output, '%', digits); + snprintf_P(output, size, tmp, ESP.getChipId()); + } + } else { + if (strchr(token, 'd')) { + snprintf_P(output, size, PSTR("%s%d"), output, ESP.getChipId()); + digits = 8; + } + } + } + } + if (!digits) { + strlcpy(output, input, size); + } + return output; +} + +char* GetOtaUrl(char *otaurl, size_t otaurl_size) +{ + if (strstr(SettingsText(SET_OTAURL), "%04d") != nullptr) { + snprintf(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP.getChipId() & 0x1fff); + } + else if (strstr(SettingsText(SET_OTAURL), "%d") != nullptr) { + snprintf_P(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP.getChipId()); + } + else { + strlcpy(otaurl, SettingsText(SET_OTAURL), otaurl_size); + } + + return otaurl; +} + +char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopic) +{ +# 88 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" + char romram[CMDSZ]; + String fulltopic; + + snprintf_P(romram, sizeof(romram), subtopic); + if (fallback_topic_flag || (prefix > 3)) { + bool fallback = (prefix < 8); + prefix &= 3; + char stemp[11]; + fulltopic = GetTextIndexed(stemp, sizeof(stemp), prefix, kPrefixes); + fulltopic += F("/"); + if (fallback) { + fulltopic += mqtt_client; + fulltopic += F("_fb"); + } else { + fulltopic += topic; + } + } else { + fulltopic = SettingsText(SET_MQTT_FULLTOPIC); + if ((0 == prefix) && (-1 == fulltopic.indexOf(FPSTR(MQTT_TOKEN_PREFIX)))) { + fulltopic += F("/"); + fulltopic += FPSTR(MQTT_TOKEN_PREFIX); + } + for (uint32_t i = 0; i < MAX_MQTT_PREFIXES; i++) { + if (!strlen(SettingsText(SET_MQTTPREFIX1 + i))) { + char temp[TOPSZ]; + SettingsUpdateText(SET_MQTTPREFIX1 + i, GetTextIndexed(temp, sizeof(temp), i, kPrefixes)); + } + } + fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), SettingsText(SET_MQTTPREFIX1 + prefix)); + + fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), topic); + fulltopic.replace(F("%hostname%"), my_hostname); + String token_id = WiFi.macAddress(); + token_id.replace(":", ""); + fulltopic.replace(F("%id%"), token_id); + } + fulltopic.replace(F("#"), ""); + fulltopic.replace(F("//"), "/"); + if (!fulltopic.endsWith("/")) { + fulltopic += "/"; + } + snprintf_P(stopic, TOPSZ, PSTR("%s%s"), fulltopic.c_str(), romram); + return stopic; +} + +char* GetGroupTopic_P(char *stopic, const char* subtopic) +{ + + + return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, SettingsText(SET_MQTT_GRP_TOPIC), subtopic); +} + +char* GetFallbackTopic_P(char *stopic, const char* subtopic) +{ + return GetTopic_P(stopic, CMND +4, nullptr, subtopic); +} + +char* GetStateText(uint32_t state) +{ + if (state >= MAX_STATE_TEXT) { + state = 1; + } + return SettingsText(SET_STATE_TXT1 + state); +} + + + +void SetLatchingRelay(power_t lpower, uint32_t state) +{ + + + + + + if (state && !latching_relay_pulse) { + latching_power = lpower; + latching_relay_pulse = 2; + } + + for (uint32_t i = 0; i < devices_present; i++) { + uint32_t port = (i << 1) + ((latching_power >> i) &1); + DigitalWrite(GPIO_REL1 +port, bitRead(rel_inverted, port) ? !state : state); + } +} + +void SetDevicePower(power_t rpower, uint32_t source) +{ + ShowSource(source); + last_source = source; + + if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { + power = (1 << devices_present) -1; + rpower = power; + } + + if (Settings.flag.interlock) { + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { + power_t mask = 1; + uint32_t count = 0; + for (uint32_t j = 0; j < devices_present; j++) { + if ((Settings.interlock[i] & mask) && (rpower & mask)) { + count++; + } + mask <<= 1; + } + if (count > 1) { + mask = ~Settings.interlock[i]; + power &= mask; + rpower &= mask; + } + } + } + + if (rpower) { + last_power = rpower; + } + + XdrvMailbox.index = rpower; + XdrvCall(FUNC_SET_POWER); + + XdrvMailbox.index = rpower; + XdrvMailbox.payload = source; + if (XdrvCall(FUNC_SET_DEVICE_POWER)) { + + } + else if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { + Serial.write(0xA0); + Serial.write(0x04); + Serial.write(rpower &0xFF); + Serial.write(0xA1); + Serial.write('\n'); + Serial.flush(); + } + else if (EXS_RELAY == my_module_type) { + SetLatchingRelay(rpower, 1); + } + else { + for (uint32_t i = 0; i < devices_present; i++) { + power_t state = rpower &1; + if (i < MAX_RELAYS) { + DigitalWrite(GPIO_REL1 +i, bitRead(rel_inverted, i) ? !state : state); + } + rpower >>= 1; + } + } +} + +void RestorePower(bool publish_power, uint32_t source) +{ + if (power != last_power) { + power = last_power; + SetDevicePower(power, source); + if (publish_power) { + MqttPublishAllPowerState(); + } + } +} + +void SetAllPower(uint32_t state, uint32_t source) +{ +# 256 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" + bool publish_power = true; + if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { + state &= 3; + publish_power = false; + } + if ((state >= POWER_OFF) && (state <= POWER_TOGGLE)) { + power_t all_on = (1 << devices_present) -1; + switch (state) { + case POWER_OFF: + power = 0; + break; + case POWER_ON: + power = all_on; + break; + case POWER_TOGGLE: + power ^= all_on; + } + SetDevicePower(power, source); + } + if (publish_power) { + MqttPublishAllPowerState(); + } +} + +void SetPowerOnState(void) +{ + if (MOTOR == my_module_type) { + Settings.poweronstate = POWER_ALL_ON; + } + if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { + SetDevicePower(1, SRC_RESTART); + } else { + if ((ResetReason() == REASON_DEFAULT_RST) || (ResetReason() == REASON_EXT_SYS_RST)) { + switch (Settings.poweronstate) { + case POWER_ALL_OFF: + case POWER_ALL_OFF_PULSETIME_ON: + power = 0; + SetDevicePower(power, SRC_RESTART); + break; + case POWER_ALL_ON: + power = (1 << devices_present) -1; + SetDevicePower(power, SRC_RESTART); + break; + case POWER_ALL_SAVED_TOGGLE: + power = (Settings.power & ((1 << devices_present) -1)) ^ POWER_MASK; + if (Settings.flag.save_state) { + SetDevicePower(power, SRC_RESTART); + } + break; + case POWER_ALL_SAVED: + power = Settings.power & ((1 << devices_present) -1); + if (Settings.flag.save_state) { + SetDevicePower(power, SRC_RESTART); + } + break; + } + } else { + power = Settings.power & ((1 << devices_present) -1); + if (Settings.flag.save_state) { + SetDevicePower(power, SRC_RESTART); + } + } + } + + + for (uint32_t i = 0; i < devices_present; i++) { + if (!Settings.flag3.no_power_feedback) { + if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { + bitWrite(power, i, digitalRead(pin[GPIO_REL1 +i]) ^ bitRead(rel_inverted, i)); + } + } + if ((i < MAX_PULSETIMERS) && (bitRead(power, i) || (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate))) { + SetPulseTimer(i, Settings.pulse_timer[i]); + } + } + blink_powersave = power; +} + +void SetLedPowerIdx(uint32_t led, uint32_t state) +{ + if ((99 == pin[GPIO_LEDLNK]) && (0 == led)) { + if (pin[GPIO_LED2] < 99) { + led = 1; + } + } + if (pin[GPIO_LED1 + led] < 99) { + uint32_t mask = 1 << led; + if (state) { + state = 1; + led_power |= mask; + } else { + led_power &= (0xFF ^ mask); + } + DigitalWrite(GPIO_LED1 + led, bitRead(led_inverted, led) ? !state : state); + } +#ifdef USE_BUZZER + if (led == 0) { + BuzzerSetStateToLed(state); + } +#endif +} + +void SetLedPower(uint32_t state) +{ + if (99 == pin[GPIO_LEDLNK]) { + SetLedPowerIdx(0, state); + } else { + power_t mask = 1; + for (uint32_t i = 0; i < leds_present; i++) { + bool tstate = (power & mask); + SetLedPowerIdx(i, tstate); + mask <<= 1; + } + } +} + +void SetLedPowerAll(uint32_t state) +{ + for (uint32_t i = 0; i < leds_present; i++) { + SetLedPowerIdx(i, state); + } +} + +void SetLedLink(uint32_t state) +{ + uint32_t led_pin = pin[GPIO_LEDLNK]; + uint32_t led_inv = ledlnk_inverted; + if (99 == led_pin) { + led_pin = pin[GPIO_LED1]; + led_inv = bitRead(led_inverted, 0); + } + if (led_pin < 99) { + if (state) { state = 1; } + digitalWrite(led_pin, (led_inv) ? !state : state); + } +#ifdef USE_BUZZER + BuzzerSetStateToLed(state); +#endif +} + +void SetPulseTimer(uint32_t index, uint32_t time) +{ + pulse_timer[index] = (time > 111) ? millis() + (1000 * (time - 100)) : (time > 0) ? millis() + (100 * time) : 0L; +} + +uint32_t GetPulseTimer(uint32_t index) +{ + long time = TimePassedSince(pulse_timer[index]); + if (time < 0) { + time *= -1; + return (time > 11100) ? (time / 1000) + 100 : (time > 0) ? time / 100 : 0; + } + return 0; +} + + + +bool SendKey(uint32_t key, uint32_t device, uint32_t state) +{ +# 423 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" + char stopic[TOPSZ]; + char scommand[CMDSZ]; + char key_topic[TOPSZ]; + bool result = false; + + char *tmp = (key) ? SettingsText(SET_MQTT_SWITCH_TOPIC) : SettingsText(SET_MQTT_BUTTON_TOPIC); + Format(key_topic, tmp, sizeof(key_topic)); + if (Settings.flag.mqtt_enabled && MqttIsConnected() && (strlen(key_topic) != 0) && strcmp(key_topic, "0")) { + if (!key && (device > devices_present)) { + device = 1; + } + GetTopic_P(stopic, CMND, key_topic, + GetPowerDevice(scommand, device, sizeof(scommand), (key + Settings.flag.device_index_enable))); + if (CLEAR_RETAIN == state) { + mqtt_data[0] = '\0'; + } else { + if ((Settings.flag3.button_switch_force_local || + !strcmp(mqtt_topic, key_topic) || + !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), key_topic)) && + (POWER_TOGGLE == state)) { + state = ~(power >> (device -1)) &1; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), GetStateText(state)); + } +#ifdef USE_DOMOTICZ + if (!(DomoticzSendKey(key, device, state, strlen(mqtt_data)))) { +#endif + MqttPublish(stopic, ((key) ? Settings.flag.mqtt_switch_retain + : Settings.flag.mqtt_button_retain) && + (state != POWER_HOLD || !Settings.flag3.no_hold_retain)); +#ifdef USE_DOMOTICZ + } +#endif + result = !Settings.flag3.button_switch_force_local; + } else { + Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state); + result = XdrvRulesProcess(); + } + int32_t payload_save = XdrvMailbox.payload; + XdrvMailbox.payload = key << 16 | state << 8 | device; + XdrvCall(FUNC_ANY_KEY); + XdrvMailbox.payload = payload_save; + return result; +} + +void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) +{ +# 483 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + blink_mask &= 1; + Settings.flag.interlock = 0; + Settings.pulse_timer[1] = 0; + Settings.pulse_timer[2] = 0; + Settings.pulse_timer[3] = 0; + } +#endif + + bool publish_power = true; + if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { + state &= 3; + publish_power = false; + } + + if ((device < 1) || (device > devices_present)) { + device = 1; + } + active_device = device; + + if (device <= MAX_PULSETIMERS) { + SetPulseTimer(device -1, 0); + } + power_t mask = 1 << (device -1); + if (state <= POWER_TOGGLE) { + if ((blink_mask & mask)) { + blink_mask &= (POWER_MASK ^ mask); + MqttPublishPowerBlinkState(device); + } + + if (Settings.flag.interlock && + !interlock_mutex && + ((POWER_ON == state) || ((POWER_TOGGLE == state) && !(power & mask))) + ) { + interlock_mutex = true; + for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { + if (Settings.interlock[i] & mask) { + for (uint32_t j = 0; j < devices_present; j++) { + power_t imask = 1 << j; + if ((Settings.interlock[i] & imask) && (power & imask) && (mask != imask)) { + ExecuteCommandPower(j +1, POWER_OFF, SRC_IGNORE); + delay(50); + } + } + break; + } + } + interlock_mutex = false; + } + + switch (state) { + case POWER_OFF: { + power &= (POWER_MASK ^ mask); + break; } + case POWER_ON: + power |= mask; + break; + case POWER_TOGGLE: + power ^= mask; + } + SetDevicePower(power, source); +#ifdef USE_DOMOTICZ + DomoticzUpdatePowerState(device); +#endif +#ifdef USE_KNX + KnxUpdatePowerState(device, power); +#endif + if (publish_power && Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } + if (device <= MAX_PULSETIMERS) { + SetPulseTimer(device -1, (((POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? ~power : power) & mask) ? Settings.pulse_timer[device -1] : 0); + } + } + else if (POWER_BLINK == state) { + if (!(blink_mask & mask)) { + blink_powersave = (blink_powersave & (POWER_MASK ^ mask)) | (power & mask); + blink_power = (power >> (device -1))&1; + } + blink_timer = millis() + 100; + blink_counter = ((!Settings.blinkcount) ? 64000 : (Settings.blinkcount *2)) +1; + blink_mask |= mask; + MqttPublishPowerBlinkState(device); + return; + } + else if (POWER_BLINK_STOP == state) { + bool flag = (blink_mask & mask); + blink_mask &= (POWER_MASK ^ mask); + MqttPublishPowerBlinkState(device); + if (flag) { + ExecuteCommandPower(device, (blink_powersave >> (device -1))&1, SRC_IGNORE); + } + return; + } + if (publish_power) { + MqttPublishPowerState(device); + } +} + +void StopAllPowerBlink(void) +{ + power_t mask; + + for (uint32_t i = 1; i <= devices_present; i++) { + mask = 1 << (i -1); + if (blink_mask & mask) { + blink_mask &= (POWER_MASK ^ mask); + MqttPublishPowerBlinkState(i); + ExecuteCommandPower(i, (blink_powersave >> (i -1))&1, SRC_IGNORE); + } + } +} + +void MqttShowPWMState(void) +{ + ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{")); + bool first = true; + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (pin[GPIO_PWM1 + i] < 99) { + ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings.pwm_value[i]); + first = false; + } + } + ResponseJsonEnd(); +} + +void MqttShowState(void) +{ + char stemp1[TOPSZ]; + + ResponseAppendTime(); + ResponseAppend_P(PSTR(",\"" D_JSON_UPTIME "\":\"%s\",\"UptimeSec\":%u"), GetUptime().c_str(), UpTime()); + +#ifdef USE_ADC_VCC + dtostrfd((double)ESP.getVcc()/1000, 3, stemp1); + ResponseAppend_P(PSTR(",\"" D_JSON_VCC "\":%s"), stemp1); +#endif + + ResponseAppend_P(PSTR(",\"" D_JSON_HEAPSIZE "\":%d,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u,\"MqttCount\":%u"), + ESP.getFreeHeap()/1024, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), + sleep, loop_load_avg, MqttConnectCount()); + + for (uint32_t i = 1; i <= devices_present; i++) { +#ifdef USE_LIGHT + if ((LightDevice()) && (i >= LightDevice())) { + if (i == LightDevice()) { LightState(1); } + } else { +#endif + ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), + GetStateText(bitRead(power, i-1))); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + ResponseAppend_P(PSTR(",\"" D_CMND_FANSPEED "\":%d"), GetFanspeed()); + break; + } +#endif +#ifdef USE_LIGHT + } +#endif + } + + if (pwm_present) { + ResponseAppend_P(PSTR(",")); + MqttShowPWMState(); + } + + ResponseAppend_P(PSTR(",\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_SIGNAL "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"), + Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), WiFi.BSSIDstr().c_str(), WiFi.channel(), + WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI(), WifiLinkCount(), WifiDowntime().c_str()); +} + +void MqttPublishTeleState(void) +{ + mqtt_data[0] = '\0'; + MqttShowState(); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); +#if defined(USE_RULES) || defined(USE_SCRIPT) + RulesTeleperiod(); +#endif +} + +bool MqttShowSensor(void) +{ + ResponseAppendTime(); + + int json_data_start = strlen(mqtt_data); + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { +#ifdef USE_TM1638 + if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { +#else + if (pin[GPIO_SWT1 +i] < 99) { +#endif + ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(SwitchState(i))); + } + } + XsnsCall(FUNC_JSON_APPEND); + XdrvCall(FUNC_JSON_APPEND); + + bool json_data_available = (strlen(mqtt_data) - json_data_start); + if (strstr_P(mqtt_data, PSTR(D_JSON_PRESSURE)) != nullptr) { + ResponseAppend_P(PSTR(",\"" D_JSON_PRESSURE_UNIT "\":\"%s\""), PressureUnit().c_str()); + } + if (strstr_P(mqtt_data, PSTR(D_JSON_TEMPERATURE)) != nullptr) { + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE_UNIT "\":\"%c\""), TempUnit()); + } + ResponseJsonEnd(); + + if (json_data_available) { XdrvCall(FUNC_SHOW_SENSOR); } + return json_data_available; +} + +void MqttPublishSensor(void) +{ + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishTeleSensor(); + } +} +# 710 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" +void PerformEverySecond(void) +{ + uptime++; + + if (POWER_CYCLE_TIME == uptime) { + UpdateQuickPowerCycle(false); + } + + if (BOOT_LOOP_TIME == uptime) { + RtcRebootReset(); + +#ifdef USE_DEEPSLEEP + if (!(DeepSleepEnabled() && !Settings.flag3.bootcount_update)) { +#endif + Settings.bootcount++; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BOOT_COUNT " %d"), Settings.bootcount); +#ifdef USE_DEEPSLEEP + } +#endif + } + + if (mqtt_cmnd_blocked_reset) { + mqtt_cmnd_blocked_reset--; + if (!mqtt_cmnd_blocked_reset) { + mqtt_cmnd_blocked = 0; + } + } + + if (seriallog_timer) { + seriallog_timer--; + if (!seriallog_timer) { + if (seriallog_level) { + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SERIAL_LOGGING_DISABLED)); + } + seriallog_level = 0; + } + } + + if (syslog_timer) { + syslog_timer--; + if (!syslog_timer) { + syslog_level = Settings.syslog_level; + if (Settings.syslog_level) { + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_LOGGING_REENABLED)); + } + } + } + + ResetGlobalValues(); + + if (Settings.tele_period) { + if (tele_period >= 9999) { + if (!global_state.wifi_down) { + tele_period = 0; + } + } else { + tele_period++; + if (tele_period >= Settings.tele_period) { + tele_period = 0; + + MqttPublishTeleState(); + + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +#if defined(USE_RULES) || defined(USE_SCRIPT) + RulesTeleperiod(); +#endif + } + + XdrvCall(FUNC_AFTER_TELEPERIOD); + } + } + } +} + + + + + +void Every100mSeconds(void) +{ + + power_t power_now; + + if (prepped_loglevel) { + AddLog(prepped_loglevel); + prepped_loglevel = 0; + } + + if (latching_relay_pulse) { + latching_relay_pulse--; + if (!latching_relay_pulse) SetLatchingRelay(0, 0); + } + + for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) { + if (pulse_timer[i] != 0L) { + if (TimeReached(pulse_timer[i])) { + pulse_timer[i] = 0L; + ExecuteCommandPower(i +1, (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? POWER_ON : POWER_OFF, SRC_PULSETIMER); + } + } + } + + if (blink_mask) { + if (TimeReached(blink_timer)) { + SetNextTimeInterval(blink_timer, 100 * Settings.blinktime); + blink_counter--; + if (!blink_counter) { + StopAllPowerBlink(); + } else { + blink_power ^= 1; + power_now = (power & (POWER_MASK ^ blink_mask)) | ((blink_power) ? blink_mask : 0); + SetDevicePower(power_now, SRC_IGNORE); + } + } + } +} + + + + + +void Every250mSeconds(void) +{ + + + uint32_t blinkinterval = 1; + + state_250mS++; + state_250mS &= 0x3; + + if (!Settings.flag.global_state) { + if (global_state.data) { + if (global_state.mqtt_down) { blinkinterval = 7; } + if (global_state.wifi_down) { blinkinterval = 3; } + blinks = 201; + } + } + if (blinks || restart_flag || ota_state_flag) { + if (restart_flag || ota_state_flag) { + blinkstate = true; + } else { + blinkspeed--; + if (!blinkspeed) { + blinkspeed = blinkinterval; + blinkstate ^= 1; + } + } + if ((!(Settings.ledstate &0x08)) && ((Settings.ledstate &0x06) || (blinks > 200) || (blinkstate))) { + SetLedLink(blinkstate); + } + if (!blinkstate) { + blinks--; + if (200 == blinks) blinks = 0; + } + } + if (Settings.ledstate &1 && (pin[GPIO_LEDLNK] < 99 || !(blinks || restart_flag || ota_state_flag)) ) { + bool tstate = power & Settings.ledmask; + if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) { + tstate = (!power) ? 1 : 0; + } + SetLedPower(tstate); + } + + + + + + switch (state_250mS) { + case 0: + if (ota_state_flag && BACKLOG_EMPTY) { + ota_state_flag--; + if (2 == ota_state_flag) { + RtcSettings.ota_loader = 0; + ota_retry_counter = OTA_ATTEMPTS; + ESPhttpUpdate.rebootOnUpdate(false); + SettingsSave(1); + } + if (ota_state_flag <= 0) { +#ifdef USE_WEBSERVER + if (Settings.webserver) StopWebserver(); +#endif +#ifdef USE_ARILUX_RF + AriluxRfDisable(); +#endif + ota_state_flag = 92; + ota_result = 0; + ota_retry_counter--; + if (ota_retry_counter) { + strlcpy(mqtt_data, GetOtaUrl(log_data, sizeof(log_data)), sizeof(mqtt_data)); +#ifndef FIRMWARE_MINIMAL + if (RtcSettings.ota_loader) { +# 915 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" + char *bch = strrchr(mqtt_data, '/'); + if (bch == nullptr) { bch = mqtt_data; } + + char *ech = strrchr(bch, '.'); + if ((ech != nullptr) && (0 == strncasecmp_P(ech, PSTR(".GZ"), 3))) { + char *fch = ech; + *fch = '\0'; + ech = strrchr(bch, '.'); + *fch = '.'; + } + if (ech == nullptr) { ech = mqtt_data + strlen(mqtt_data); } + char ota_url_type[strlen(ech) +1]; + strncpy(ota_url_type, ech, sizeof(ota_url_type)); + + char *pch = strrchr(bch, '-'); + if (pch == nullptr) { pch = ech; } + *pch = '\0'; + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s-" D_JSON_MINIMAL "%s"), mqtt_data, ota_url_type); + } +#endif + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "%s"), mqtt_data); +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) + ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(mqtt_data)); +#else + + WiFiClient OTAclient; + ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(OTAclient, mqtt_data)); +#endif + if (!ota_result) { +#ifndef FIRMWARE_MINIMAL + int ota_error = ESPhttpUpdate.getLastError(); + DEBUG_CORE_LOG(PSTR("OTA: Error %d"), ota_error); + if ((HTTP_UE_TOO_LESS_SPACE == ota_error) || (HTTP_UE_BIN_FOR_WRONG_FLASH == ota_error)) { + RtcSettings.ota_loader = 1; + } +#endif + ota_state_flag = 2; + } + } + } + if (90 == ota_state_flag) { + ota_state_flag = 0; + Response_P(PSTR("{\"" D_CMND_UPGRADE "\":\"")); + if (ota_result) { + + if (!VersionCompatible()) { + ResponseAppend_P(PSTR(D_JSON_FAILED " " D_UPLOAD_ERR_14)); + } else { + ResponseAppend_P(PSTR(D_JSON_SUCCESSFUL ". " D_JSON_RESTARTING)); + restart_flag = 2; + } + } else { + ResponseAppend_P(PSTR(D_JSON_FAILED " %s"), ESPhttpUpdate.getLastErrorString().c_str()); + } + ResponseAppend_P(PSTR("\"}")); + + MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_UPGRADE)); + } + } + break; + case 1: + if (MidnightNow()) { + XsnsCall(FUNC_SAVE_AT_MIDNIGHT); + } + if (save_data_counter && BACKLOG_EMPTY) { + save_data_counter--; + if (save_data_counter <= 0) { + if (Settings.flag.save_state) { + power_t mask = POWER_MASK; + for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) { + if ((Settings.pulse_timer[i] > 0) && (Settings.pulse_timer[i] < 30)) { + mask &= ~(1 << i); + } + } + if (!((Settings.power &mask) == (power &mask))) { + Settings.power = power; + } + } else { + Settings.power = 0; + } + SettingsSave(0); + save_data_counter = Settings.save_data; + } + } + if (restart_flag && BACKLOG_EMPTY) { + if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) { + + char storage_ssid1[strlen(SettingsText(SET_STASSID1)) +1]; + strncpy(storage_ssid1, SettingsText(SET_STASSID1), sizeof(storage_ssid1)); + char storage_ssid2[strlen(SettingsText(SET_STASSID2)) +1]; + strncpy(storage_ssid2, SettingsText(SET_STASSID2), sizeof(storage_ssid2)); + char storage_pass1[strlen(SettingsText(SET_STAPWD1)) +1]; + strncpy(storage_pass1, SettingsText(SET_STAPWD1), sizeof(storage_pass1)); + char storage_pass2[strlen(SettingsText(SET_STAPWD2)) +1]; + strncpy(storage_pass2, SettingsText(SET_STAPWD2), sizeof(storage_pass2)); + + char storage_mqtthost[strlen(SettingsText(SET_MQTT_HOST)) +1]; + strncpy(storage_mqtthost, SettingsText(SET_MQTT_HOST), sizeof(storage_mqtthost)); + char storage_mqttuser[strlen(SettingsText(SET_MQTT_USER)) +1]; + strncpy(storage_mqttuser, SettingsText(SET_MQTT_USER), sizeof(storage_mqttuser)); + char storage_mqttpwd[strlen(SettingsText(SET_MQTT_PWD)) +1]; + strncpy(storage_mqttpwd, SettingsText(SET_MQTT_PWD), sizeof(storage_mqttpwd)); + char storage_mqtttopic[strlen(SettingsText(SET_MQTT_TOPIC)) +1]; + strncpy(storage_mqtttopic, SettingsText(SET_MQTT_TOPIC), sizeof(storage_mqtttopic)); + uint16_t mqtt_port = Settings.mqtt_port; + + + + + if ((215 == restart_flag) || (216 == restart_flag)) { + SettingsErase(0); + } + SettingsDefault(); + + SettingsUpdateText(SET_STASSID1, storage_ssid1); + SettingsUpdateText(SET_STASSID2, storage_ssid2); + SettingsUpdateText(SET_STAPWD1, storage_pass1); + SettingsUpdateText(SET_STAPWD2, storage_pass2); + if (216 == restart_flag) { + + SettingsUpdateText(SET_MQTT_HOST, storage_mqtthost); + SettingsUpdateText(SET_MQTT_USER, storage_mqttuser); + SettingsUpdateText(SET_MQTT_PWD, storage_mqttpwd); + SettingsUpdateText(SET_MQTT_TOPIC, storage_mqtttopic); + Settings.mqtt_port = mqtt_port; + } + restart_flag = 2; + } + else if (213 == restart_flag) { + SettingsSdkErase(); + restart_flag = 2; + } + else if (212 == restart_flag) { + SettingsErase(0); + restart_flag = 211; + } + if (211 == restart_flag) { + SettingsDefault(); + restart_flag = 2; + } + if (2 == restart_flag) { + SettingsSaveAll(); + } + restart_flag--; + if (restart_flag <= 0) { + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); + EspRestart(); + } + } + break; + case 2: + WifiCheck(wifi_state_flag); + wifi_state_flag = WIFI_RESTART; + break; + case 3: + if (!global_state.wifi_down) { MqttCheck(); } + break; + } +} + +#ifdef USE_ARDUINO_OTA + + + + + + + +bool arduino_ota_triggered = false; +uint16_t arduino_ota_progress_dot_count = 0; + +void ArduinoOTAInit(void) +{ + ArduinoOTA.setPort(8266); + ArduinoOTA.setHostname(my_hostname); + if (strlen(SettingsText(SET_WEBPWD))) { + ArduinoOTA.setPassword(SettingsText(SET_WEBPWD)); + } + + ArduinoOTA.onStart([]() + { + SettingsSave(1); +#ifdef USE_WEBSERVER + if (Settings.webserver) { StopWebserver(); } +#endif +#ifdef USE_ARILUX_RF + AriluxRfDisable(); +#endif + if (Settings.flag.mqtt_enabled) { + MqttDisconnect(); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_UPLOAD_STARTED)); + arduino_ota_triggered = true; + arduino_ota_progress_dot_count = 0; + delay(100); + }); + + 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) + { + + + + + 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); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA %s. " D_RESTARTING), error_str); + EspRestart(); + }); + + ArduinoOTA.onEnd([]() + { + if ((LOG_LEVEL_DEBUG <= seriallog_level)) { Serial.println(); } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_SUCCESSFUL ". " D_RESTARTING)); + EspRestart(); + }); + + ArduinoOTA.begin(); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_ENABLED " " D_PORT " 8266")); +} + +void ArduinoOtaLoop(void) +{ + MDNS.update(); + ArduinoOTA.handle(); + + while (arduino_ota_triggered) { ArduinoOTA.handle(); } +} +#endif + + + +void SerialInput(void) +{ + while (Serial.available()) { + + delay(0); + serial_in_byte = Serial.read(); + + + + + if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { + serial_in_byte = ButtonSerial(serial_in_byte); + } + + + + if (XdrvCall(FUNC_SERIAL)) { + serial_in_byte_counter = 0; + Serial.flush(); + return; + } + + + + if (serial_in_byte > 127 && !Settings.flag.mqtt_serial_raw) { + serial_in_byte_counter = 0; + Serial.flush(); + return; + } + if (!Settings.flag.mqtt_serial) { + if (isprint(serial_in_byte)) { + if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + } else { + serial_in_byte_counter = 0; + } + } + } else { + if (serial_in_byte || Settings.flag.mqtt_serial_raw) { + if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && + ((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || + ((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || + Settings.flag.mqtt_serial_raw)) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + serial_polling_window = millis(); + } else { + serial_polling_window = 0; + break; + } + } + } + +#ifdef USE_SONOFF_SC + + + + if (SONOFF_SC == my_module_type) { + if (serial_in_byte == '\x1B') { + serial_in_buffer[serial_in_byte_counter] = 0; + SonoffScSerialInput(serial_in_buffer); + serial_in_byte_counter = 0; + Serial.flush(); + return; + } + } else +#endif + + + if (!Settings.flag.mqtt_serial && (serial_in_byte == '\n')) { + serial_in_buffer[serial_in_byte_counter] = 0; + seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (uint8_t)LOG_LEVEL_INFO : Settings.seriallog_level; + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), serial_in_buffer); + ExecuteCommand(serial_in_buffer, SRC_SERIAL); + serial_in_byte_counter = 0; + serial_polling_window = 0; + Serial.flush(); + return; + } + } + + if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { + serial_in_buffer[serial_in_byte_counter] = 0; + char hex_char[(serial_in_byte_counter * 2) + 2]; + bool assume_json = (!Settings.flag.mqtt_serial_raw && (serial_in_buffer[0] == '{')); + Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":%s%s%s}"), + (assume_json) ? "" : """", + (Settings.flag.mqtt_serial_raw) ? ToHex_P((unsigned char*)serial_in_buffer, serial_in_byte_counter, hex_char, sizeof(hex_char)) : serial_in_buffer, + (assume_json) ? "" : """"); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); + XdrvRulesProcess(); + serial_in_byte_counter = 0; + } +} + + + +void GpioInit(void) +{ + uint32_t mpin; + + if (!ValidModule(Settings.module)) { + uint32_t module = MODULE; + if (!ValidModule(MODULE)) { module = SONOFF_BASIC; } + Settings.module = module; + Settings.last_module = module; + } + SetModuleType(); + + if (Settings.module != Settings.last_module) { + Settings.baudrate = APP_BAUDRATE / 300; + Settings.serial_config = TS_SERIAL_8N1; + } + + for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { + if ((Settings.user_template.gp.io[i] >= GPIO_SENSOR_END) && (Settings.user_template.gp.io[i] < GPIO_USER)) { + Settings.user_template.gp.io[i] = GPIO_USER; + } + } + + myio def_gp; + ModuleGpios(&def_gp); + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + if ((Settings.my_gp.io[i] >= GPIO_SENSOR_END) && (Settings.my_gp.io[i] < GPIO_USER)) { + Settings.my_gp.io[i] = GPIO_NONE; + } + else if (Settings.my_gp.io[i] > GPIO_NONE) { + my_module.io[i] = Settings.my_gp.io[i]; + } + if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < GPIO_USER)) { + my_module.io[i] = def_gp.io[i]; + } + } + if ((Settings.my_adc0 >= ADC0_END) && (Settings.my_adc0 < ADC0_USER)) { + Settings.my_adc0 = ADC0_NONE; + } + else if (Settings.my_adc0 > ADC0_NONE) { + my_adc0 = Settings.my_adc0; + } + my_module_flag = ModuleFlag(); + uint32_t template_adc0 = my_module_flag.data &15; + if ((template_adc0 > ADC0_NONE) && (template_adc0 < ADC0_USER)) { + my_adc0 = template_adc0; + } + + for (uint32_t i = 0; i < GPIO_MAX; i++) { + pin[i] = 99; + } + for (uint32_t i = 0; i < sizeof(my_module.io); i++) { + mpin = ValidPin(i, my_module.io[i]); + + DEBUG_CORE_LOG(PSTR("INI: gpio pin %d, mpin %d"), i, mpin); + + if (mpin) { + XdrvMailbox.index = mpin; + XdrvMailbox.payload = i; + + if ((mpin >= GPIO_SWT1_NP) && (mpin < (GPIO_SWT1_NP + MAX_SWITCHES))) { + SwitchPullupFlag(mpin - GPIO_SWT1_NP); + mpin -= (GPIO_SWT1_NP - GPIO_SWT1); + } + else if ((mpin >= GPIO_KEY1_NP) && (mpin < (GPIO_KEY1_NP + MAX_KEYS))) { + ButtonPullupFlag(mpin - GPIO_KEY1_NP); + mpin -= (GPIO_KEY1_NP - GPIO_KEY1); + } + else if ((mpin >= GPIO_KEY1_INV) && (mpin < (GPIO_KEY1_INV + MAX_KEYS))) { + ButtonInvertFlag(mpin - GPIO_KEY1_INV); + mpin -= (GPIO_KEY1_INV - GPIO_KEY1); + } + else if ((mpin >= GPIO_KEY1_INV_NP) && (mpin < (GPIO_KEY1_INV_NP + MAX_KEYS))) { + ButtonPullupFlag(mpin - GPIO_KEY1_INV_NP); + ButtonInvertFlag(mpin - GPIO_KEY1_INV_NP); + mpin -= (GPIO_KEY1_INV_NP - GPIO_KEY1); + } + else if ((mpin >= GPIO_REL1_INV) && (mpin < (GPIO_REL1_INV + MAX_RELAYS))) { + bitSet(rel_inverted, mpin - GPIO_REL1_INV); + mpin -= (GPIO_REL1_INV - GPIO_REL1); + } + else if ((mpin >= GPIO_LED1_INV) && (mpin < (GPIO_LED1_INV + MAX_LEDS))) { + bitSet(led_inverted, mpin - GPIO_LED1_INV); + mpin -= (GPIO_LED1_INV - GPIO_LED1); + } + else if (mpin == GPIO_LEDLNK_INV) { + ledlnk_inverted = 1; + mpin -= (GPIO_LEDLNK_INV - GPIO_LEDLNK); + } + else if ((mpin >= GPIO_PWM1_INV) && (mpin < (GPIO_PWM1_INV + MAX_PWMS))) { + bitSet(pwm_inverted, mpin - GPIO_PWM1_INV); + mpin -= (GPIO_PWM1_INV - GPIO_PWM1); + } + else if (XdrvCall(FUNC_PIN_STATE)) { + mpin = XdrvMailbox.index; + } + else if (XsnsCall(FUNC_PIN_STATE)) { + mpin = XdrvMailbox.index; + }; + } + if (mpin) pin[mpin] = i; + } + + if ((2 == pin[GPIO_TXD]) || (H801 == my_module_type)) { Serial.set_tx(2); } + + analogWriteRange(Settings.pwm_range); + analogWriteFreq(Settings.pwm_frequency); + +#ifdef USE_SPI + spi_flg = ((((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CS] > 14)) || (pin[GPIO_SPI_CS] < 12)) || (((pin[GPIO_SPI_DC] < 99) && (pin[GPIO_SPI_DC] > 14)) || (pin[GPIO_SPI_DC] < 12))); + if (spi_flg) { + for (uint32_t i = 0; i < GPIO_MAX; i++) { + if ((pin[i] >= 12) && (pin[i] <=14)) pin[i] = 99; + } + my_module.io[12] = GPIO_SPI_MISO; + pin[GPIO_SPI_MISO] = 12; + my_module.io[13] = GPIO_SPI_MOSI; + pin[GPIO_SPI_MOSI] = 13; + my_module.io[14] = GPIO_SPI_CLK; + pin[GPIO_SPI_CLK] = 14; + } + soft_spi_flg = ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && ((pin[GPIO_SSPI_MOSI] < 99) || (pin[GPIO_SSPI_MOSI] < 99))); +#endif + +#ifdef USE_I2C + i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99)); + if (i2c_flg) { + Wire.begin(pin[GPIO_I2C_SDA], pin[GPIO_I2C_SCL]); + } +#endif + + devices_present = 0; + light_type = LT_BASIC; + if (XdrvCall(FUNC_MODULE_INIT)) { + + } + else if (YTF_IR_BRIDGE == my_module_type) { + ClaimSerial(); + + } + else if (SONOFF_DUAL == my_module_type) { + devices_present = 2; + SetSerial(19200, TS_SERIAL_8N1); + } + else if (CH4 == my_module_type) { + devices_present = 4; + SetSerial(19200, TS_SERIAL_8N1); + } +#ifdef USE_SONOFF_SC + else if (SONOFF_SC == my_module_type) { + SetSerial(19200, TS_SERIAL_8N1); + } +#endif + + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (pin[GPIO_PWM1 +i] < 99) { + pinMode(pin[GPIO_PWM1 +i], OUTPUT); + if (light_type) { + + analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); + } else { + pwm_present = true; + analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); + } + } + } + + for (uint32_t i = 0; i < MAX_RELAYS; i++) { + if (pin[GPIO_REL1 +i] < 99) { + pinMode(pin[GPIO_REL1 +i], OUTPUT); + devices_present++; + if (EXS_RELAY == my_module_type) { + digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0); + if (i &1) { devices_present--; } + } + } + } + + for (uint32_t i = 0; i < MAX_LEDS; i++) { + if (pin[GPIO_LED1 +i] < 99) { +#ifdef USE_ARILUX_RF + if ((3 == i) && (leds_present < 2) && (99 == pin[GPIO_ARIRFSEL])) { + pin[GPIO_ARIRFSEL] = pin[GPIO_LED4]; + pin[GPIO_LED4] = 99; + } else { +#endif + pinMode(pin[GPIO_LED1 +i], OUTPUT); + leds_present++; + digitalWrite(pin[GPIO_LED1 +i], bitRead(led_inverted, i)); +#ifdef USE_ARILUX_RF + } +#endif + } + } + if (pin[GPIO_LEDLNK] < 99) { + pinMode(pin[GPIO_LEDLNK], OUTPUT); + digitalWrite(pin[GPIO_LEDLNK], ledlnk_inverted); + } + + ButtonInit(); + SwitchInit(); +#ifdef ROTARY_V1 + RotaryInit(); +#endif + + SetLedPower(Settings.ledstate &8); + SetLedLink(Settings.ledstate &8); + + XdrvCall(FUNC_PRE_INIT); +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_udp.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_udp.ino" +#ifdef USE_EMULATION + +#define UDP_BUFFER_SIZE 200 +#define UDP_MSEARCH_SEND_DELAY 1500 + +#include +Ticker TickerMSearch; + +IPAddress udp_remote_ip; +uint16_t udp_remote_port; + +bool udp_connected = false; +bool udp_response_mutex = false; + + + + + +const char URN_BELKIN_DEVICE[] PROGMEM = "urn:belkin:device:**"; +const char URN_BELKIN_DEVICE_CAP[] PROGMEM = "urn:Belkin:device:**"; +const char UPNP_ROOTDEVICE[] PROGMEM = "upnp:rootdevice"; +const char SSDPSEARCH_ALL[] PROGMEM = "ssdpsearch:all"; +const char SSDP_ALL[] PROGMEM = "ssdp:all"; + + + + + +bool UdpDisconnect(void) +{ + if (udp_connected) { + PortUdp.flush(); + WiFiUDP::stopAll(); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_DISABLED)); + udp_connected = false; + } + return udp_connected; +} + +bool UdpConnect(void) +{ + if (!udp_connected) { + + if (PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), 1900)) { + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED)); + udp_response_mutex = false; + udp_connected = true; + } else { + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_JOIN_FAILED)); + udp_connected = false; + } + } + return udp_connected; +} + +void PollUdp(void) +{ + if (udp_connected) { + while (PortUdp.parsePacket()) { + char packet_buffer[UDP_BUFFER_SIZE]; + + int len = PortUdp.read(packet_buffer, UDP_BUFFER_SIZE -1); + packet_buffer[len] = 0; + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d)"), len); + + + +#ifdef USE_SCRIPT_HUE + if (!udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) { +#else + if (devices_present && !udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) { +#endif + udp_response_mutex = true; + + udp_remote_ip = PortUdp.remoteIP(); + udp_remote_port = PortUdp.remotePort(); + + + + + uint32_t response_delay = UDP_MSEARCH_SEND_DELAY + ((millis() &0x7) * 100); + + LowerCase(packet_buffer, packet_buffer); + RemoveSpace(packet_buffer); + +#ifdef USE_EMULATION_WEMO + if (EMUL_WEMO == Settings.flag2.emulation) { + if (strstr_P(packet_buffer, URN_BELKIN_DEVICE) != nullptr) { + TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 1); + return; + } + else if ((strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || + (strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) || + (strstr_P(packet_buffer, SSDP_ALL) != nullptr)) { + TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 2); + return; + } + } +#endif + +#ifdef USE_EMULATION_HUE + if (EMUL_HUE == Settings.flag2.emulation) { + if ((strstr_P(packet_buffer, PSTR(":device:basic:1")) != nullptr) || + (strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || + (strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) || + (strstr_P(packet_buffer, SSDP_ALL) != nullptr)) { + TickerMSearch.attach_ms(response_delay, HueRespondToMSearch); + return; + } + } +#endif + + udp_response_mutex = false; + } + + } + optimistic_yield(100); + } +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_wifi.ino" +# 24 "C:/shared/sonoff/Git/Tasmota/tasmota/support_wifi.ino" +#ifndef WIFI_RSSI_THRESHOLD + +#define WIFI_RSSI_THRESHOLD 5 +#endif +#ifndef WIFI_RESCAN_MINUTES + +#define WIFI_RESCAN_MINUTES 5 +#endif + +const uint8_t WIFI_CONFIG_SEC = 180; + +const uint8_t WIFI_CHECK_SEC = 5; +const uint8_t WIFI_RETRY_OFFSET_SEC = 20; + +#include +#if LWIP_IPV6 +#include +#endif + +struct WIFI { + uint32_t last_event = 0; + uint32_t downtime = 0; + uint16_t link_count = 0; + uint8_t counter; + uint8_t retry_init; + uint8_t retry; + uint8_t status; + uint8_t config_type = 0; + uint8_t config_counter = 0; + uint8_t mdns_begun = 0; + uint8_t scan_state; + uint8_t bssid[6] = {0}; + uint8_t bssid_last[6] = {0}; + int8_t best_network_db; +} Wifi; + +int WifiGetRssiAsQuality(int rssi) +{ + int quality = 0; + + if (rssi <= -100) { + quality = 0; + } else if (rssi >= -50) { + quality = 100; + } else { + quality = 2 * (rssi + 100); + } + return quality; +} + +bool WifiConfigCounter(void) +{ + if (Wifi.config_counter) { + Wifi.config_counter = WIFI_CONFIG_SEC; + } + return (Wifi.config_counter); +} + +void WifiConfig(uint8_t type) +{ + if (!Wifi.config_type) { + if ((WIFI_RETRY == type) || (WIFI_WAIT == type)) { return; } +#ifdef USE_EMULATION + UdpDisconnect(); +#endif + WiFi.disconnect(); + Wifi.config_type = type; + +#ifndef USE_WEBSERVER + if (WIFI_MANAGER == Wifi.config_type) { + Wifi.config_type = WIFI_SERIAL; + } +#endif + + Wifi.config_counter = WIFI_CONFIG_SEC; + Wifi.counter = Wifi.config_counter +5; + blinks = 1999; + if (WIFI_RESTART == Wifi.config_type) { + restart_flag = 2; + } + else if (WIFI_SERIAL == Wifi.config_type) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_6_SERIAL " " D_ACTIVE_FOR_3_MINUTES)); + } +#ifdef USE_WEBSERVER + else if (WIFI_MANAGER == Wifi.config_type || WIFI_MANAGER_RESET_ONLY == Wifi.config_type) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_2_WIFIMANAGER " " D_ACTIVE_FOR_3_MINUTES)); + WifiManagerBegin(WIFI_MANAGER_RESET_ONLY == Wifi.config_type); + } +#endif + } +} + +void WifiSetMode(WiFiMode_t wifi_mode) +{ + if (WiFi.getMode() == wifi_mode) { return; } + + if (wifi_mode != WIFI_OFF) { + + WiFi.forceSleepWake(); + delay(100); + } + + uint32_t retry = 2; + while (!WiFi.mode(wifi_mode) && retry--) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR("Retry set Mode...")); + delay(100); + } + + if (wifi_mode == WIFI_OFF) { + delay(1000); + WiFi.forceSleepBegin(); + delay(1); + } else { + delay(30); + } +} + +void WiFiSetSleepMode(void) +{ +# 156 "C:/shared/sonoff/Git/Tasmota/tasmota/support_wifi.ino" +#if defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) +#else + if (sleep && Settings.flag3.sleep_normal) { + WiFi.setSleepMode(WIFI_LIGHT_SLEEP); + } else { + WiFi.setSleepMode(WIFI_MODEM_SLEEP); + } +#endif + WifiSetOutputPower(); +} + +void WifiBegin(uint8_t flag, uint8_t channel) +{ + const char kWifiPhyMode[] = " BGN"; + +#ifdef USE_EMULATION + UdpDisconnect(); +#endif + +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR(D_PATCH_ISSUE_2186)); + + WifiSetMode(WIFI_OFF); +#endif + + WiFi.persistent(false); + WiFi.disconnect(true); + delay(200); + + WifiSetMode(WIFI_STA); + WiFiSetSleepMode(); + + + if (!WiFi.getAutoConnect()) { WiFi.setAutoConnect(true); } + + + + + + switch (flag) { + case 0: + case 1: + Settings.sta_active = flag; + break; + case 2: + Settings.sta_active ^= 1; + } + if (!strlen(SettingsText(SET_STASSID1 + Settings.sta_active))) { + Settings.sta_active ^= 1; + } + if (Settings.ip_address[0]) { + WiFi.config(Settings.ip_address[0], Settings.ip_address[1], Settings.ip_address[2], Settings.ip_address[3]); + } + WiFi.hostname(my_hostname); + + char stemp[40] = { 0 }; + if (channel) { + WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active), channel, Wifi.bssid); + + char hex_char[18]; + snprintf_P(stemp, sizeof(stemp), PSTR(" Channel %d BSSId %s"), channel, ToHex_P((unsigned char*)Wifi.bssid, 6, hex_char, sizeof(hex_char), ':')); + } else { + WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active)); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s%s " D_IN_MODE " 11%c " D_AS " %s..."), + Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), stemp, kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname); + +#if LWIP_IPV6 + for (bool configured = false; !configured;) { + uint16_t cfgcnt = 0; + for (auto addr : addrList) { + if ((configured = !addr.isLocal() && addr.isV6()) || cfgcnt==30) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI "Got IPv6 global address %s"), addr.toString().c_str()); + break; + } + delay(500); + cfgcnt++; + } + } +#endif +} + +void WifiBeginAfterScan(void) +{ + + if (0 == Wifi.scan_state) { return; } + + if (1 == Wifi.scan_state) { + memset((void*) &Wifi.bssid, 0, sizeof(Wifi.bssid)); + Wifi.best_network_db = -127; + Wifi.scan_state = 3; + } + + if (2 == Wifi.scan_state) { + uint8_t* bssid = WiFi.BSSID(); + memcpy((void*) &Wifi.bssid, (void*) bssid, sizeof(Wifi.bssid)); + Wifi.best_network_db = WiFi.RSSI(); + if (Wifi.best_network_db < -WIFI_RSSI_THRESHOLD) { + Wifi.best_network_db += WIFI_RSSI_THRESHOLD; + } + Wifi.scan_state = 3; + } + + if (3 == Wifi.scan_state) { + if (WiFi.scanComplete() != WIFI_SCAN_RUNNING) { + WiFi.scanNetworks(true); + Wifi.scan_state++; + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR("Network (re)scan started...")); + return; + } + } + int8_t wifi_scan_result = WiFi.scanComplete(); + + if (4 == Wifi.scan_state) { + if (wifi_scan_result != WIFI_SCAN_RUNNING) { + Wifi.scan_state++; + } + } + + if (5 == Wifi.scan_state) { + uint32_t number_known = 0; + int32_t channel_max = 0; + int8_t ap_max = 3; + uint8_t bssid_max[6]; + memcpy((void*) &bssid_max, (void*) &Wifi.bssid, sizeof(bssid_max)); + + int32_t channel = 0; + int8_t ap = 3; + uint8_t last_bssid[6]; + memcpy((void*) &last_bssid, (void*) &Wifi.bssid, sizeof(last_bssid)); + + if (wifi_scan_result > 0) { + + for (uint32_t i = 0; i < wifi_scan_result; ++i) { + + String ssid_scan; + int32_t rssi_scan; + uint8_t sec_scan; + uint8_t* bssid_scan; + int32_t chan_scan; + bool hidden_scan; + + WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, bssid_scan, chan_scan, hidden_scan); + + bool known = false; + uint32_t j; + for (j = 0; j < MAX_SSIDS; j++) { + if (ssid_scan == SettingsText(SET_STASSID1 + j)) { + known = true; + number_known++; + if (rssi_scan > Wifi.best_network_db) { + if (sec_scan == ENC_TYPE_NONE || SettingsText(SET_STAPWD1 + j)) { + + memcpy((void*) &bssid_max, (void*) bssid_scan, sizeof(bssid_max)); + channel_max = chan_scan; + ap_max = j; + + for (uint32_t i = 0; i < sizeof(Wifi.bssid_last); i++) { + if (bssid_scan[i] != Wifi.bssid_last[i]) { + Wifi.best_network_db = (int8_t)rssi_scan; + channel = chan_scan; + ap = j; + memcpy((void*) &Wifi.bssid, (void*) bssid_scan, sizeof(Wifi.bssid)); + + memcpy((void*) &Wifi.bssid_last, (void*) bssid_scan, sizeof(Wifi.bssid_last)); + break; + } + } + } + } + break; + } + } + char hex_char[18]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network %d, AP%c, SSId %s, Channel %d, BSSId %s, RSSI %d, Encryption %d"), + i, + (known) ? (j) ? '2' : '1' : '-', + ssid_scan.c_str(), + chan_scan, + ToHex_P((unsigned char*)bssid_scan, 6, hex_char, sizeof(hex_char), ':'), + rssi_scan, + (sec_scan == ENC_TYPE_NONE) ? 0 : 1); + delay(0); + } + WiFi.scanDelete(); + delay(0); + } + + + if (number_known == 1) { + + memset((void*) &Wifi.bssid_last, 0, sizeof(Wifi.bssid_last)); + memcpy((void*) &Wifi.bssid, (void*) bssid_max, sizeof(Wifi.bssid)); + channel = channel_max; + ap = ap_max; + } + + Wifi.scan_state = 0; + + for (uint32_t i = 0; i < sizeof(Wifi.bssid); i++) { + if (last_bssid[i] != Wifi.bssid[i]) { + WifiBegin(ap, channel); + break; + } + } + } +} + +uint16_t WifiLinkCount(void) +{ + return Wifi.link_count; +} + +String WifiDowntime(void) +{ + return GetDuration(Wifi.downtime); +} + +void WifiSetState(uint8_t state) +{ + if (state == global_state.wifi_down) { + if (state) { + rules_flag.wifi_connected = 1; + Wifi.link_count++; + Wifi.downtime += UpTime() - Wifi.last_event; + } else { + rules_flag.wifi_disconnected = 1; + Wifi.last_event = UpTime(); + } + } + global_state.wifi_down = state ^1; +} + +#if LWIP_IPV6 +bool WifiCheckIPv6(void) +{ + bool ipv6_global=false; + + for (auto a : addrList) { + if(!a.isLocal() && a.isV6()) ipv6_global=true; + } + return ipv6_global; +} + +String WifiGetIPv6(void) +{ + for (auto a : addrList) { + if(!a.isLocal() && a.isV6()) return a.toString(); + } + return ""; +} + +bool WifiCheckIPAddrStatus(void) +{ + bool ip_global=false; + + for (auto a : addrList) { + if(!a.isLocal()) ip_global=true; + } + return ip_global; +} +#endif + +void WifiCheckIp(void) +{ +#if LWIP_IPV6 + if(WifiCheckIPAddrStatus()) { + Wifi.status = WL_CONNECTED; +#else + if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0)) { +#endif + + memset((void*) &Wifi.bssid_last, 0, sizeof(Wifi.bssid_last)); + WifiSetState(1); + Wifi.counter = WIFI_CHECK_SEC; + Wifi.retry = Wifi.retry_init; + if (Wifi.status != WL_CONNECTED) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECTED)); + } + Wifi.status = WL_CONNECTED; +#ifdef USE_DISCOVERY +#ifdef WEBSERVER_ADVERTISE + if (2 == Wifi.mdns_begun) { + MDNS.update(); + AddLog_P(LOG_LEVEL_DEBUG_MORE, D_LOG_MDNS, "MDNS.update"); + } +#endif +#endif + } else { + WifiSetState(0); + uint8_t wifi_config_tool = Settings.sta_config; + Wifi.status = WiFi.status(); + switch (Wifi.status) { + case WL_CONNECTED: + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_NO_IP_ADDRESS)); + + + wifi_station_dhcpc_start(); + break; + case WL_NO_SSID_AVAIL: + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_NOT_REACHED)); + + if (WIFI_WAIT == Settings.sta_config) { + Wifi.retry = Wifi.retry_init; + } else { + if (Wifi.retry > (Wifi.retry_init / 2)) { + Wifi.retry = Wifi.retry_init / 2; + } + else if (Wifi.retry) { + Wifi.retry = 0; + } + } + + break; + case WL_CONNECT_FAILED: + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_WRONG_PASSWORD)); + + if (Wifi.retry > (Wifi.retry_init / 2)) { + Wifi.retry = Wifi.retry_init / 2; + } + else if (Wifi.retry) { + Wifi.retry = 0; + } + + break; + default: + + if (!Wifi.retry || ((Wifi.retry_init / 2) == Wifi.retry)) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_TIMEOUT)); + } else { + if (!strlen(SettingsText(SET_STASSID1)) && !strlen(SettingsText(SET_STASSID2))) { + wifi_config_tool = WIFI_MANAGER; + Wifi.retry = 0; + } else { + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR(D_ATTEMPTING_CONNECTION)); + } + } + } + + if (Wifi.retry) { + if (Settings.flag3.use_wifi_scan) { + + if ((Wifi.retry_init == Wifi.retry) || ((Wifi.retry_init / 2) == Wifi.retry)){ + Wifi.scan_state = 1; + } + } else { + if (Wifi.retry_init == Wifi.retry) { + WifiBegin(3, 0); + } + if ((Settings.sta_config != WIFI_WAIT) && ((Wifi.retry_init / 2) == Wifi.retry)) { + WifiBegin(2, 0); + } + } + Wifi.counter = 1; + Wifi.retry--; + } else { + WifiConfig(wifi_config_tool); + Wifi.counter = 1; + Wifi.retry = Wifi.retry_init; + } + } +} + +void WifiCheck(uint8_t param) +{ + Wifi.counter--; + switch (param) { + case WIFI_SERIAL: + case WIFI_MANAGER: + WifiConfig(param); + break; + default: + if (Wifi.config_counter) { + Wifi.config_counter--; + Wifi.counter = Wifi.config_counter +5; + if (Wifi.config_counter) { + if (!Wifi.config_counter) { + if (strlen(WiFi.SSID().c_str())) { + SettingsUpdateText(SET_STASSID1, WiFi.SSID().c_str()); + } + if (strlen(WiFi.psk().c_str())) { + SettingsUpdateText(SET_STAPWD1, WiFi.psk().c_str()); + } + Settings.sta_active = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_2_WIFIMANAGER D_CMND_SSID "1 %s"), SettingsText(SET_STASSID1)); + } + } + if (!Wifi.config_counter) { + + restart_flag = 2; + } + } else { + if (Wifi.scan_state) { WifiBeginAfterScan(); } + + if (Wifi.counter <= 0) { + AddLog_P(LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CHECKING_CONNECTION)); + Wifi.counter = WIFI_CHECK_SEC; + WifiCheckIp(); + } +#if LWIP_IPV6 + if (WifiCheckIPAddrStatus()) { +#else + if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0) && !Wifi.config_type) { +#endif + WifiSetState(1); + + if (Settings.flag3.use_wifi_rescan) { + if (!(uptime % (60 * WIFI_RESCAN_MINUTES))) { + Wifi.scan_state = 2; + } + } + +#ifdef FIRMWARE_MINIMAL + if (1 == RtcSettings.ota_loader) { + RtcSettings.ota_loader = 0; + ota_state_flag = 3; + } +#endif + +#ifdef USE_DISCOVERY + if (Settings.flag3.mdns_enabled) { + if (!Wifi.mdns_begun) { + + + + + + Wifi.mdns_begun = (uint8_t)MDNS.begin(my_hostname); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (Wifi.mdns_begun) ? D_INITIALIZED : D_FAILED); + + } + } +#endif + +#ifdef USE_WEBSERVER + if (Settings.webserver) { + StartWebserver(Settings.webserver, WiFi.localIP()); +#ifdef USE_DISCOVERY +#ifdef WEBSERVER_ADVERTISE + if (1 == Wifi.mdns_begun) { + Wifi.mdns_begun = 2; + MDNS.addService("http", "tcp", WEB_PORT); + } +#endif +#endif + } else { + StopWebserver(); + } +#ifdef USE_EMULATION + if (Settings.flag2.emulation) { UdpConnect(); } +#endif +#endif + +#ifdef USE_KNX + if (!knx_started && Settings.flag.knx_enabled) { + KNXStart(); + knx_started = true; + } +#endif + + } else { + WifiSetState(0); +#ifdef USE_EMULATION + UdpDisconnect(); +#endif + Wifi.mdns_begun = 0; +#ifdef USE_KNX + knx_started = false; +#endif + } + } + } +} + +int WifiState(void) +{ + int state = -1; + + if (!global_state.wifi_down) { state = WIFI_RESTART; } + if (Wifi.config_type) { state = Wifi.config_type; } + return state; +} + +String WifiGetOutputPower(void) +{ + char stemp1[TOPSZ]; + dtostrfd((float)(Settings.wifi_output_power) / 10, 1, stemp1); + return String(stemp1); +} +void WifiSetOutputPower(void) +{ + WiFi.setOutputPower((float)(Settings.wifi_output_power) / 10); +} + +void WifiConnect(void) +{ + WifiSetState(0); + WifiSetOutputPower(); + WiFi.persistent(false); + Wifi.status = 0; + + + Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + (ESP.getChipId() & 0xF); + Wifi.retry = Wifi.retry_init; + Wifi.counter = 1; +} + + + +void WifiDisconnect(void) +{ + + WiFi.persistent(true); + ETS_UART_INTR_DISABLE(); + wifi_station_disconnect(); + ETS_UART_INTR_ENABLE(); + WiFi.persistent(false); +} + +void WifiShutdown(void) +{ + delay(100); + if (Settings.flag.mqtt_enabled) { + MqttDisconnect(); + } + WifiDisconnect(); +} + +void EspRestart(void) +{ + WifiShutdown(); + CrashDumpClear(); + + ESP.reset(); +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota_ca.ino" +# 24 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota_ca.ino" +#ifdef USE_MQTT_TLS_CA_CERT + +#ifndef USE_MQTT_AWS_IOT +# 38 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota_ca.ino" +static const unsigned char PROGMEM TA0_DN[] = { + 0x30, 0x4A, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x0D, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x1A, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, + 0x79, 0x20, 0x58, 0x33 +}; + +static const unsigned char PROGMEM TA0_RSA_N[] = { + 0x9C, 0xD3, 0x0C, 0xF0, 0x5A, 0xE5, 0x2E, 0x47, 0xB7, 0x72, 0x5D, 0x37, + 0x83, 0xB3, 0x68, 0x63, 0x30, 0xEA, 0xD7, 0x35, 0x26, 0x19, 0x25, 0xE1, + 0xBD, 0xBE, 0x35, 0xF1, 0x70, 0x92, 0x2F, 0xB7, 0xB8, 0x4B, 0x41, 0x05, + 0xAB, 0xA9, 0x9E, 0x35, 0x08, 0x58, 0xEC, 0xB1, 0x2A, 0xC4, 0x68, 0x87, + 0x0B, 0xA3, 0xE3, 0x75, 0xE4, 0xE6, 0xF3, 0xA7, 0x62, 0x71, 0xBA, 0x79, + 0x81, 0x60, 0x1F, 0xD7, 0x91, 0x9A, 0x9F, 0xF3, 0xD0, 0x78, 0x67, 0x71, + 0xC8, 0x69, 0x0E, 0x95, 0x91, 0xCF, 0xFE, 0xE6, 0x99, 0xE9, 0x60, 0x3C, + 0x48, 0xCC, 0x7E, 0xCA, 0x4D, 0x77, 0x12, 0x24, 0x9D, 0x47, 0x1B, 0x5A, + 0xEB, 0xB9, 0xEC, 0x1E, 0x37, 0x00, 0x1C, 0x9C, 0xAC, 0x7B, 0xA7, 0x05, + 0xEA, 0xCE, 0x4A, 0xEB, 0xBD, 0x41, 0xE5, 0x36, 0x98, 0xB9, 0xCB, 0xFD, + 0x6D, 0x3C, 0x96, 0x68, 0xDF, 0x23, 0x2A, 0x42, 0x90, 0x0C, 0x86, 0x74, + 0x67, 0xC8, 0x7F, 0xA5, 0x9A, 0xB8, 0x52, 0x61, 0x14, 0x13, 0x3F, 0x65, + 0xE9, 0x82, 0x87, 0xCB, 0xDB, 0xFA, 0x0E, 0x56, 0xF6, 0x86, 0x89, 0xF3, + 0x85, 0x3F, 0x97, 0x86, 0xAF, 0xB0, 0xDC, 0x1A, 0xEF, 0x6B, 0x0D, 0x95, + 0x16, 0x7D, 0xC4, 0x2B, 0xA0, 0x65, 0xB2, 0x99, 0x04, 0x36, 0x75, 0x80, + 0x6B, 0xAC, 0x4A, 0xF3, 0x1B, 0x90, 0x49, 0x78, 0x2F, 0xA2, 0x96, 0x4F, + 0x2A, 0x20, 0x25, 0x29, 0x04, 0xC6, 0x74, 0xC0, 0xD0, 0x31, 0xCD, 0x8F, + 0x31, 0x38, 0x95, 0x16, 0xBA, 0xA8, 0x33, 0xB8, 0x43, 0xF1, 0xB1, 0x1F, + 0xC3, 0x30, 0x7F, 0xA2, 0x79, 0x31, 0x13, 0x3D, 0x2D, 0x36, 0xF8, 0xE3, + 0xFC, 0xF2, 0x33, 0x6A, 0xB9, 0x39, 0x31, 0xC5, 0xAF, 0xC4, 0x8D, 0x0D, + 0x1D, 0x64, 0x16, 0x33, 0xAA, 0xFA, 0x84, 0x29, 0xB6, 0xD4, 0x0B, 0xC0, + 0xD8, 0x7D, 0xC3, 0x93 +}; + +static const unsigned char TA0_RSA_E[] = { + 0x01, 0x00, 0x01 +}; + +static const br_x509_trust_anchor PROGMEM LetsEncryptX3CrossSigned_TA = { + { (unsigned char *)TA0_DN, sizeof TA0_DN }, + BR_X509_TA_CA, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N, + (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E, + } } + } +}; + +#define TAs_NUM 1 + +#endif + +#ifdef USE_MQTT_AWS_IOT +# 106 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota_ca.ino" +const unsigned char PROGMEM TA0_DN[] = { + 0x30, 0x39, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x0F, 0x30, 0x0D, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x06, 0x41, 0x6D, 0x61, 0x7A, 0x6F, 0x6E, 0x31, 0x19, 0x30, 0x17, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x41, 0x6D, 0x61, 0x7A, 0x6F, + 0x6E, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31 +}; + +const unsigned char PROGMEM TA0_RSA_N[] = { + 0xB2, 0x78, 0x80, 0x71, 0xCA, 0x78, 0xD5, 0xE3, 0x71, 0xAF, 0x47, 0x80, + 0x50, 0x74, 0x7D, 0x6E, 0xD8, 0xD7, 0x88, 0x76, 0xF4, 0x99, 0x68, 0xF7, + 0x58, 0x21, 0x60, 0xF9, 0x74, 0x84, 0x01, 0x2F, 0xAC, 0x02, 0x2D, 0x86, + 0xD3, 0xA0, 0x43, 0x7A, 0x4E, 0xB2, 0xA4, 0xD0, 0x36, 0xBA, 0x01, 0xBE, + 0x8D, 0xDB, 0x48, 0xC8, 0x07, 0x17, 0x36, 0x4C, 0xF4, 0xEE, 0x88, 0x23, + 0xC7, 0x3E, 0xEB, 0x37, 0xF5, 0xB5, 0x19, 0xF8, 0x49, 0x68, 0xB0, 0xDE, + 0xD7, 0xB9, 0x76, 0x38, 0x1D, 0x61, 0x9E, 0xA4, 0xFE, 0x82, 0x36, 0xA5, + 0xE5, 0x4A, 0x56, 0xE4, 0x45, 0xE1, 0xF9, 0xFD, 0xB4, 0x16, 0xFA, 0x74, + 0xDA, 0x9C, 0x9B, 0x35, 0x39, 0x2F, 0xFA, 0xB0, 0x20, 0x50, 0x06, 0x6C, + 0x7A, 0xD0, 0x80, 0xB2, 0xA6, 0xF9, 0xAF, 0xEC, 0x47, 0x19, 0x8F, 0x50, + 0x38, 0x07, 0xDC, 0xA2, 0x87, 0x39, 0x58, 0xF8, 0xBA, 0xD5, 0xA9, 0xF9, + 0x48, 0x67, 0x30, 0x96, 0xEE, 0x94, 0x78, 0x5E, 0x6F, 0x89, 0xA3, 0x51, + 0xC0, 0x30, 0x86, 0x66, 0xA1, 0x45, 0x66, 0xBA, 0x54, 0xEB, 0xA3, 0xC3, + 0x91, 0xF9, 0x48, 0xDC, 0xFF, 0xD1, 0xE8, 0x30, 0x2D, 0x7D, 0x2D, 0x74, + 0x70, 0x35, 0xD7, 0x88, 0x24, 0xF7, 0x9E, 0xC4, 0x59, 0x6E, 0xBB, 0x73, + 0x87, 0x17, 0xF2, 0x32, 0x46, 0x28, 0xB8, 0x43, 0xFA, 0xB7, 0x1D, 0xAA, + 0xCA, 0xB4, 0xF2, 0x9F, 0x24, 0x0E, 0x2D, 0x4B, 0xF7, 0x71, 0x5C, 0x5E, + 0x69, 0xFF, 0xEA, 0x95, 0x02, 0xCB, 0x38, 0x8A, 0xAE, 0x50, 0x38, 0x6F, + 0xDB, 0xFB, 0x2D, 0x62, 0x1B, 0xC5, 0xC7, 0x1E, 0x54, 0xE1, 0x77, 0xE0, + 0x67, 0xC8, 0x0F, 0x9C, 0x87, 0x23, 0xD6, 0x3F, 0x40, 0x20, 0x7F, 0x20, + 0x80, 0xC4, 0x80, 0x4C, 0x3E, 0x3B, 0x24, 0x26, 0x8E, 0x04, 0xAE, 0x6C, + 0x9A, 0xC8, 0xAA, 0x0D +}; + +static const unsigned char PROGMEM TA0_RSA_E[] = { + 0x01, 0x00, 0x01 +}; + +const br_x509_trust_anchor PROGMEM AmazonRootCA1_TA = { + { (unsigned char *)TA0_DN, sizeof TA0_DN }, + BR_X509_TA_CA, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N, + (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E, + } } + } +}; + +#define TAs_NUM 1 + +#endif + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_01_webserver.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_01_webserver.ino" +#ifdef USE_WEBSERVER + + + + + + + +#define XDRV_01 1 + +#ifndef WIFI_SOFT_AP_CHANNEL +#define WIFI_SOFT_AP_CHANNEL 1 +#endif + +const uint16_t CHUNKED_BUFFER_SIZE = 400; + +const uint16_t HTTP_REFRESH_TIME = 2345; +#define HTTP_RESTART_RECONNECT_TIME 9000 +#define HTTP_OTA_RESTART_RECONNECT_TIME 20000 + +#include +#include + +#ifdef USE_RF_FLASH +uint8_t *efm8bb1_update = nullptr; +#endif + +enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1, UPL_TASMOTASLAVE }; + +static const char * HEADER_KEYS[] = { "User-Agent", }; + +const char HTTP_HEADER[] PROGMEM = + "" + "" + "" + "" + "%s - %s" + + ""; + +const char HTTP_HEAD_STYLE1[] PROGMEM = + "" + + "" + "" + "
" +#ifdef FIRMWARE_MINIMAL + "

" D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "

" +#endif + "
" +#ifdef LANGUAGE_MODULE_NAME + "

" D_MODULE " %s

" +#else + "

%s " D_MODULE "

" +#endif + "

%s

"; + +const char HTTP_MSG_SLIDER_GRADIENT[] PROGMEM = + "
" + "" + "
"; +const char HTTP_MSG_SLIDER_SHUTTER[] PROGMEM = + "
" D_CLOSE "" D_OPEN "
" + "
"; + +const char HTTP_MSG_RSTRT[] PROGMEM = + "
" D_DEVICE_WILL_RESTART "

"; + +const char HTTP_FORM_LOGIN[] PROGMEM = + "
" + "
" + "

" D_USER "

" + "

" D_PASSWORD "

" + "
" + "" + "
"; + +const char HTTP_FORM_TEMPLATE[] PROGMEM = + "
 " D_TEMPLATE_PARAMETERS " " + "
"; +const char HTTP_FORM_TEMPLATE_FLAG[] PROGMEM = + "

" + "
 " D_TEMPLATE_FLAGS " 

" + + "

"; + +const char HTTP_FORM_MODULE[] PROGMEM = + "
 " D_MODULE_PARAMETERS " " + "" + "

" D_MODULE_TYPE " (%s)

" + "
"; + +const char HTTP_FORM_WIFI[] PROGMEM = + "
 " D_WIFI_PARAMETERS " " + "" + "

" D_AP1_SSID " (" STA_SSID1 ")

" + "

" D_AP1_PASSWORD "

" + "

" D_AP2_SSID " (" STA_SSID2 ")

" + "

" D_AP2_PASSWORD "

" + "

" D_HOSTNAME " (%s)

" + "

" D_CORS_DOMAIN "

"; + +const char HTTP_FORM_LOG1[] PROGMEM = + "
 " D_LOGGING_PARAMETERS " " + ""; +const char HTTP_FORM_LOG2[] PROGMEM = + "

" D_SYSLOG_HOST " (" SYS_LOG_HOST ")

" + "

" D_SYSLOG_PORT " (" STR(SYS_LOG_PORT) ")

" + "

" D_TELEMETRY_PERIOD " (" STR(TELE_PERIOD) ")

"; + +const char HTTP_FORM_OTHER[] PROGMEM = + "
 " D_OTHER_PARAMETERS " " + "" + "

" + "
 " D_TEMPLATE " " + "

" + "

" D_ACTIVATE "

" + "
" + "
" + "" D_WEB_ADMIN_PASSWORD "

" + "
" + "" D_MQTT_ENABLE "
" + "
"; + +const char HTTP_FORM_END[] PROGMEM = + "
" + "" + "
"; + +const char HTTP_FORM_RST[] PROGMEM = + "
" + "
 " D_RESTORE_CONFIGURATION " "; +const char HTTP_FORM_UPG[] PROGMEM = + "
" + "
 " D_UPGRADE_BY_WEBSERVER " " + "
" + "
" D_OTA_URL "

" + "
" + "


" + "
 " D_UPGRADE_BY_FILE_UPLOAD " "; +const char HTTP_FORM_RST_UPG[] PROGMEM = + "
" + "

" + "
" + "
" + "
" + ""; + +const char HTTP_FORM_CMND[] PROGMEM = + "


" + "
" + "
" + + ""; + +const char HTTP_TABLE100[] PROGMEM = + "
"; + +const char HTTP_COUNTER[] PROGMEM = + "
"; + +const char HTTP_END[] PROGMEM = + "" + "" + "" + ""; + +const char HTTP_DEVICE_CONTROL[] PROGMEM = ""; +const char HTTP_DEVICE_STATE[] PROGMEM = ""; + +enum ButtonTitle { + BUTTON_RESTART, BUTTON_RESET_CONFIGURATION, + BUTTON_MAIN, BUTTON_CONFIGURATION, BUTTON_INFORMATION, BUTTON_FIRMWARE_UPGRADE, BUTTON_CONSOLE, + BUTTON_MODULE, BUTTON_WIFI, BUTTON_LOGGING, BUTTON_OTHER, BUTTON_TEMPLATE, BUTTON_BACKUP, BUTTON_RESTORE }; +const char kButtonTitle[] PROGMEM = + D_RESTART "|" D_RESET_CONFIGURATION "|" + D_MAIN_MENU "|" D_CONFIGURATION "|" D_INFORMATION "|" D_FIRMWARE_UPGRADE "|" D_CONSOLE "|" + D_CONFIGURE_MODULE "|" D_CONFIGURE_WIFI"|" D_CONFIGURE_LOGGING "|" D_CONFIGURE_OTHER "|" D_CONFIGURE_TEMPLATE "|" D_BACKUP_CONFIGURATION "|" D_RESTORE_CONFIGURATION; +const char kButtonAction[] PROGMEM = + ".|rt|" + ".|cn|in|up|cs|" + "md|wi|lg|co|tp|dl|rs"; +const char kButtonConfirm[] PROGMEM = D_CONFIRM_RESTART "|" D_CONFIRM_RESET_CONFIGURATION; + +enum CTypes { CT_HTML, CT_PLAIN, CT_XML, CT_JSON, CT_STREAM }; +const char kContentTypes[] PROGMEM = "text/html|text/plain|text/xml|application/json|application/octet-stream"; + +const char kLoggingOptions[] PROGMEM = D_SERIAL_LOG_LEVEL "|" D_WEB_LOG_LEVEL "|" D_MQTT_LOG_LEVEL "|" D_SYS_LOG_LEVEL; +const char kLoggingLevels[] PROGMEM = D_NONE "|" D_ERROR "|" D_INFO "|" D_DEBUG "|" D_MORE_DEBUG; + +const char kEmulationOptions[] PROGMEM = D_NONE "|" D_BELKIN_WEMO "|" D_HUE_BRIDGE; + +const char kUploadErrors[] PROGMEM = + D_UPLOAD_ERR_1 "|" D_UPLOAD_ERR_2 "|" D_UPLOAD_ERR_3 "|" D_UPLOAD_ERR_4 "|" D_UPLOAD_ERR_5 "|" D_UPLOAD_ERR_6 "|" D_UPLOAD_ERR_7 "|" D_UPLOAD_ERR_8 "|" D_UPLOAD_ERR_9 +#ifdef USE_RF_FLASH + "|" D_UPLOAD_ERR_10 "|" D_UPLOAD_ERR_11 "|" D_UPLOAD_ERR_12 "|" D_UPLOAD_ERR_13 +#endif + "|" D_UPLOAD_ERR_14 + ; + +const uint16_t DNS_PORT = 53; +enum HttpOptions {HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER, HTTP_MANAGER_RESET_ONLY}; + +DNSServer *DnsServer; +ESP8266WebServer *WebServer; + +struct WEB { + String chunk_buffer = ""; + bool reset_web_log_flag = false; + uint8_t state = HTTP_OFF; + uint8_t upload_error = 0; + uint8_t upload_file_type; + uint8_t upload_progress_dot_count; + uint8_t config_block_count = 0; + uint8_t config_xor_on = 0; + uint8_t config_xor_on_set = CONFIG_FILE_XOR; +} Web; + + +static void WebGetArg(const char* arg, char* out, size_t max) +{ + String s = WebServer->arg(arg); + strlcpy(out, s.c_str(), max); + +} + +static bool WifiIsInManagerMode(){ + return (HTTP_MANAGER == Web.state || HTTP_MANAGER_RESET_ONLY == Web.state); +} + +void ShowWebSource(uint32_t source) +{ + if ((source > 0) && (source < SRC_MAX)) { + char stemp1[20]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SRC: %s from %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource), WebServer->client().remoteIP().toString().c_str()); + } +} + +void ExecuteWebCommand(char* svalue, uint32_t source) +{ + ShowWebSource(source); + last_source = source; + ExecuteCommand(svalue, SRC_IGNORE); +} + +void StartWebserver(int type, IPAddress ipweb) +{ + if (!Settings.web_refresh) { Settings.web_refresh = HTTP_REFRESH_TIME; } + if (!Web.state) { + if (!WebServer) { + WebServer = new ESP8266WebServer((HTTP_MANAGER == type || HTTP_MANAGER_RESET_ONLY == type) ? 80 : WEB_PORT); + WebServer->on("/", HandleRoot); + WebServer->onNotFound(HandleNotFound); + WebServer->on("/up", HandleUpgradeFirmware); + WebServer->on("/u1", HandleUpgradeFirmwareStart); + WebServer->on("/u2", HTTP_POST, HandleUploadDone, HandleUploadLoop); + WebServer->on("/u2", HTTP_OPTIONS, HandlePreflightRequest); + WebServer->on("/cs", HTTP_GET, HandleConsole); + WebServer->on("/cs", HTTP_OPTIONS, HandlePreflightRequest); + WebServer->on("/cm", HandleHttpCommand); +#ifndef FIRMWARE_MINIMAL + WebServer->on("/cn", HandleConfiguration); + WebServer->on("/md", HandleModuleConfiguration); + WebServer->on("/wi", HandleWifiConfiguration); + WebServer->on("/lg", HandleLoggingConfiguration); + WebServer->on("/tp", HandleTemplateConfiguration); + WebServer->on("/co", HandleOtherConfiguration); + WebServer->on("/dl", HandleBackupConfiguration); + WebServer->on("/rs", HandleRestoreConfiguration); + WebServer->on("/rt", HandleResetConfiguration); + WebServer->on("/in", HandleInformation); + XdrvCall(FUNC_WEB_ADD_HANDLER); + XsnsCall(FUNC_WEB_ADD_HANDLER); +#endif + } + Web.reset_web_log_flag = false; + + + + WebServer->collectHeaders(HEADER_KEYS, sizeof(HEADER_KEYS)/sizeof(char*)); + + WebServer->begin(); + } + if (Web.state != type) { +#if LWIP_IPV6 + String ipv6_addr = WifiGetIPv6(); + if(ipv6_addr!="") AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s and IPv6 global address %s "), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str(),ipv6_addr.c_str()); + else AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str()); +#else + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str()); +#endif + rules_flag.http_init = 1; + } + if (type) { Web.state = type; } +} + +void StopWebserver(void) +{ + if (Web.state) { + WebServer->close(); + Web.state = HTTP_OFF; + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_STOPPED)); + } +} + +void WifiManagerBegin(bool reset_only) +{ + + if (!global_state.wifi_down) { + + WifiSetMode(WIFI_AP_STA); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION)); + } else { + + WifiSetMode(WIFI_AP); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT)); + } + + StopWebserver(); + + DnsServer = new DNSServer(); + + int channel = WIFI_SOFT_AP_CHANNEL; + if ((channel < 1) || (channel > 13)) { channel = 1; } + +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + + WiFi.softAP(my_hostname, WIFI_AP_PASSPHRASE, channel, 0); +#else + + WiFi.softAP(my_hostname, WIFI_AP_PASSPHRASE, channel, 0, 1); +#endif + + delay(500); + + DnsServer->setErrorReplyCode(DNSReplyCode::NoError); + DnsServer->start(DNS_PORT, "*", WiFi.softAPIP()); + + StartWebserver((reset_only ? HTTP_MANAGER_RESET_ONLY : HTTP_MANAGER), WiFi.softAPIP()); +} + +void PollDnsWebserver(void) +{ + if (DnsServer) { DnsServer->processNextRequest(); } + if (WebServer) { WebServer->handleClient(); } +} + + + +bool WebAuthenticate(void) +{ + if (strlen(SettingsText(SET_WEBPWD)) && (HTTP_MANAGER_RESET_ONLY != Web.state)) { + return WebServer->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD)); + } else { + return true; + } +} + +bool HttpCheckPriviledgedAccess(bool autorequestauth = true) +{ + if (HTTP_USER == Web.state) { + HandleRoot(); + return false; + } + if (autorequestauth && !WebAuthenticate()) { + WebServer->requestAuthentication(); + return false; + } + return true; +} + +void HttpHeaderCors(void) +{ + if (strlen(SettingsText(SET_CORS))) { + WebServer->sendHeader(F("Access-Control-Allow-Origin"), SettingsText(SET_CORS)); + } +} + +void WSHeaderSend(void) +{ + WebServer->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); + WebServer->sendHeader(F("Pragma"), F("no-cache")); + WebServer->sendHeader(F("Expires"), F("-1")); + HttpHeaderCors(); +} + + + + + +void WSSend(int code, int ctype, const String& content) +{ + char ct[25]; + WebServer->send(code, GetTextIndexed(ct, sizeof(ct), ctype, kContentTypes), content); +} + + + + + +void WSContentBegin(int code, int ctype) +{ + WebServer->client().flush(); + WSHeaderSend(); +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + WebServer->sendHeader(F("Accept-Ranges"),F("none")); + WebServer->sendHeader(F("Transfer-Encoding"),F("chunked")); +#endif + WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); + WSSend(code, ctype, ""); + Web.chunk_buffer = ""; +} + +void _WSContentSend(const String& content) +{ + size_t len = content.length(); + +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + const char * footer = "\r\n"; + char chunk_size[11]; + sprintf(chunk_size, "%x\r\n", len); + WebServer->sendContent(String() + chunk_size + content + footer); +#else + WebServer->sendContent(content); +#endif + +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("WSContentSend")); +#endif + DEBUG_CORE_LOG(PSTR("WEB: Chunk size %d"), len); +} + +void WSContentFlush(void) +{ + if (Web.chunk_buffer.length() > 0) { + _WSContentSend(Web.chunk_buffer); + Web.chunk_buffer = ""; + } +} + +void _WSContentSendBuffer(void) +{ + int len = strlen(mqtt_data); + + if (0 == len) { + return; + } + else if (len == sizeof(mqtt_data)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: Content too large")); + } + else if (len < CHUNKED_BUFFER_SIZE) { + Web.chunk_buffer += mqtt_data; + len = Web.chunk_buffer.length(); + } + + if (len >= CHUNKED_BUFFER_SIZE) { + WSContentFlush(); + } + if (strlen(mqtt_data) >= CHUNKED_BUFFER_SIZE) { + _WSContentSend(mqtt_data); + } +} + +void WSContentSend_P(const char* formatP, ...) +{ + + va_list arg; + va_start(arg, formatP); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); + va_end(arg); + +#ifdef DEBUG_TASMOTA_CORE + if (len > (sizeof(mqtt_data) -1)) { + mqtt_data[33] = '\0'; + DEBUG_CORE_LOG(PSTR("ERROR: WSContentSend_P size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); + } +#endif + + _WSContentSendBuffer(); +} + +void WSContentSend_PD(const char* formatP, ...) +{ + + va_list arg; + va_start(arg, formatP); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); + va_end(arg); + +#ifdef DEBUG_TASMOTA_CORE + if (len > (sizeof(mqtt_data) -1)) { + mqtt_data[33] = '\0'; + DEBUG_CORE_LOG(PSTR("ERROR: WSContentSend_PD size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); + } +#endif + + if (D_DECIMAL_SEPARATOR[0] != '.') { + for (uint32_t i = 0; i < len; i++) { + if ('.' == mqtt_data[i]) { + mqtt_data[i] = D_DECIMAL_SEPARATOR[0]; + } + } + } + + _WSContentSendBuffer(); +} + +void WSContentStart_P(const char* title, bool auth) +{ + if (auth && strlen(SettingsText(SET_WEBPWD)) && !WebServer->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD))) { + return WebServer->requestAuthentication(); + } + + WSContentBegin(200, CT_HTML); + + if (title != nullptr) { + char ctitle[strlen_P(title) +1]; + strcpy_P(ctitle, title); + WSContentSend_P(HTTP_HEADER, SettingsText(SET_FRIENDLYNAME1), ctitle); + } +} + +void WSContentStart_P(const char* title) +{ + WSContentStart_P(title, true); +} + +void WSContentSendStyle_P(const char* formatP, ...) +{ + if (WifiIsInManagerMode()) { + if (WifiConfigCounter()) { + WSContentSend_P(HTTP_SCRIPT_COUNTER); + } + } + WSContentSend_P(HTTP_HEAD_LAST_SCRIPT); + + WSContentSend_P(HTTP_HEAD_STYLE1, WebColor(COL_FORM), WebColor(COL_INPUT), WebColor(COL_INPUT_TEXT), WebColor(COL_INPUT), + WebColor(COL_INPUT_TEXT), WebColor(COL_CONSOLE), WebColor(COL_CONSOLE_TEXT), WebColor(COL_BACKGROUND)); + WSContentSend_P(HTTP_HEAD_STYLE2, WebColor(COL_BUTTON), WebColor(COL_BUTTON_TEXT), WebColor(COL_BUTTON_HOVER), + WebColor(COL_BUTTON_RESET), WebColor(COL_BUTTON_RESET_HOVER), WebColor(COL_BUTTON_SAVE), WebColor(COL_BUTTON_SAVE_HOVER), + WebColor(COL_BUTTON)); + if (formatP != nullptr) { + + va_list arg; + va_start(arg, formatP); + int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); + va_end(arg); + +#ifdef DEBUG_TASMOTA_CORE + if (len > (sizeof(mqtt_data) -1)) { + mqtt_data[33] = '\0'; + DEBUG_CORE_LOG(PSTR("ERROR: WSContentSendStyle_P size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); + } +#endif + + _WSContentSendBuffer(); + } + WSContentSend_P(HTTP_HEAD_STYLE3, WebColor(COL_TEXT), +#ifdef FIRMWARE_MINIMAL + WebColor(COL_TEXT_WARNING), +#endif + WebColor(COL_TITLE), + ModuleName().c_str(), SettingsText(SET_FRIENDLYNAME1)); + if (Settings.flag3.gui_hostname_ip) { + bool lip = (static_cast(WiFi.localIP()) != 0); + bool sip = (static_cast(WiFi.softAPIP()) != 0); + WSContentSend_P(PSTR("

%s%s (%s%s%s)

"), + my_hostname, + (Wifi.mdns_begun) ? ".local" : "", + (lip) ? WiFi.localIP().toString().c_str() : "", + (lip && sip) ? ", " : "", + (sip) ? WiFi.softAPIP().toString().c_str() : ""); + } + WSContentSend_P(PSTR("")); +} + +void WSContentSendStyle(void) +{ + WSContentSendStyle_P(nullptr); +} + +void WSContentButton(uint32_t title_index) +{ + char action[4]; + char title[100]; + + if (title_index <= BUTTON_RESET_CONFIGURATION) { + char confirm[100]; + WSContentSend_P(PSTR("

"), + GetTextIndexed(action, sizeof(action), title_index, kButtonAction), + GetTextIndexed(confirm, sizeof(confirm), title_index, kButtonConfirm), + (!title_index) ? "rst" : "non", + GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); + } else { + WSContentSend_P(PSTR("

"), + GetTextIndexed(action, sizeof(action), title_index, kButtonAction), + GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); + } +} + +void WSContentSpaceButton(uint32_t title_index) +{ + WSContentSend_P(PSTR("
")); + WSContentButton(title_index); +} + +void WSContentEnd(void) +{ + WSContentFlush(); + _WSContentSend(""); + WebServer->client().stop(); +} + +void WSContentStop(void) +{ + if (WifiIsInManagerMode()) { + if (WifiConfigCounter()) { + WSContentSend_P(HTTP_COUNTER); + } + } + WSContentSend_P(HTTP_END, my_version); + WSContentEnd(); +} + + + +void WebRestart(uint32_t type) +{ + + + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTART); + + bool reset_only = (HTTP_MANAGER_RESET_ONLY == Web.state); + + WSContentStart_P((type) ? S_SAVE_CONFIGURATION : S_RESTART, !reset_only); + WSContentSend_P(HTTP_SCRIPT_RELOAD); + WSContentSendStyle(); + if (type) { + WSContentSend_P(PSTR("
" D_CONFIGURATION_SAVED "
")); + if (2 == type) { + WSContentSend_P(PSTR("
" D_TRYING_TO_CONNECT "
")); + } + WSContentSend_P(PSTR("
")); + } + WSContentSend_P(HTTP_MSG_RSTRT); + if (HTTP_MANAGER == Web.state || reset_only) { + Web.state = HTTP_ADMIN; + } else { + WSContentSpaceButton(BUTTON_MAIN); + } + WSContentStop(); + + ShowWebSource(SRC_WEBGUI); + restart_flag = 2; +} + + + +void HandleWifiLogin(void) +{ + WSContentStart_P(S_CONFIGURE_WIFI, false); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_LOGIN); + + if (HTTP_MANAGER_RESET_ONLY == Web.state) { + WSContentSpaceButton(BUTTON_RESTART); +#ifndef FIRMWARE_MINIMAL + WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); +#endif + } + + WSContentStop(); +} + +void WebSliderColdWarm(void) +{ + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "a", + "#fff", "#ff0", + 1, + 153, 500, + LightGetColorTemp(), + 't', 0); +} + +void HandleRoot(void) +{ + if (CaptivePortal()) { return; } + + if (WebServer->hasArg("rst")) { + WebRestart(0); + return; + } + + if (WifiIsInManagerMode()) { +#ifndef FIRMWARE_MINIMAL + if (strlen(SettingsText(SET_WEBPWD)) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) { + HandleWifiLogin(); + } else { + if (!strlen(SettingsText(SET_WEBPWD)) || (((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) { + HandleWifiConfiguration(); + } else { + + HandleWifiLogin(); + } + } +#endif + return; + } + + if (HandleRootStatusRefresh()) { + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_MAIN_MENU); + + char stemp[33]; + + WSContentStart_P(S_MAIN_MENU); +#ifdef USE_SCRIPT_WEB_DISPLAY + WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh, Settings.web_refresh); +#else + WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh); +#endif + WSContentSend_P(HTTP_SCRIPT_ROOT_PART2); + + WSContentSendStyle(); + + WSContentSend_P(PSTR("
")); + if (devices_present) { +#ifdef USE_LIGHT + if (light_type) { + uint8_t light_subtype = light_type &7; + if (!Settings.flag3.pwm_multi_channels) { + bool split_white = ((LST_RGBW <= light_subtype) && (devices_present > 1)); + + if ((LST_COLDWARM == light_subtype) || ((LST_RGBCW == light_subtype) && !split_white)) { + WebSliderColdWarm(); + } + + if (light_subtype > 2) { + uint16_t hue; + uint8_t sat; + LightGetHSB(&hue, &sat, nullptr); + + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "b", + "#800", "#f00 5%,#ff0 20%,#0f0 35%,#0ff 50%,#00f 65%,#f0f 80%,#f00 95%,#800", + 2, + 1, 359, + hue, + 'h', 0); + + uint8_t dcolor = changeUIntScale(Settings.light_dimmer, 0, 100, 0, 255); + char scolor[8]; + snprintf_P(scolor, sizeof(scolor), PSTR("#%02X%02X%02X"), dcolor, dcolor, dcolor); + uint8_t red, green, blue; + LightHsToRgb(hue, 255, &red, &green, &blue); + snprintf_P(stemp, sizeof(stemp), PSTR("#%02X%02X%02X"), red, green, blue); + + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "s", + scolor, stemp, + 3, + 0, 100, + changeUIntScale(sat, 0, 255, 0, 100), + 'n', 0); + } + + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "c", + "#000", "#fff", + 4, + Settings.flag3.slider_dimmer_stay_on, 100, + Settings.light_dimmer, + 'd', 0); + + if (split_white) { + if (LST_RGBCW == light_subtype) { + WebSliderColdWarm(); + } + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + "f", + "#000", "#fff", + 5, + Settings.flag3.slider_dimmer_stay_on, 100, + LightGetDimmer(2), + 'w', 0); + } + } else { + uint32_t pwm_channels = light_subtype > LST_MAX ? LST_MAX : light_subtype; + stemp[0] = 'e'; stemp[1] = '0'; stemp[2] = '\0'; + for (uint32_t i = 0; i < pwm_channels; i++) { + stemp[1]++; + + WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, + stemp, + "#000", "#fff", + i+1, + 1, 100, + changeUIntScale(Settings.light_color[i], 0, 255, 0, 100), + 'e', i+1); + } + } + } +#endif +#ifdef USE_SHUTTER + if (Settings.flag3.shutter_mode) { + for (uint32_t i = 0; i < shutters_present; i++) { + WSContentSend_P(HTTP_MSG_SLIDER_SHUTTER, Settings.shutter_position[i], i+1); + } + } +#endif + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("
")); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + WSContentSend_P(HTTP_DEVICE_CONTROL, 36, 1, + (strlen(SettingsText(SET_BUTTON1))) ? SettingsText(SET_BUTTON1) : D_BUTTON_TOGGLE, + ""); + for (uint32_t i = 0; i < MaxFanspeed(); i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i); + WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i +2, + (strlen(SettingsText(SET_BUTTON2 + i))) ? SettingsText(SET_BUTTON2 + i) : stemp, + ""); + } + } else { +#endif + for (uint32_t idx = 1; idx <= devices_present; idx++) { + bool set_button = ((idx <= MAX_BUTTON_TEXT) && strlen(SettingsText(SET_BUTTON1 + idx -1))); +#ifdef USE_SHUTTER + int32_t ShutterWebButton; + if (ShutterWebButton = IsShutterWebButton(idx)) { + WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, + (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : ((Settings.shutter_options[abs(ShutterWebButton)-1] & 2) ? "-" : ((ShutterWebButton>0) ? "â–²" : "â–¼")), + ""); + continue; + } +#endif + snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx); + WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, + (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (devices_present < 5) ? D_BUTTON_TOGGLE : "", + (set_button) ? "" : (devices_present > 1) ? stemp : ""); + } +#ifdef USE_SONOFF_IFAN + } +#endif + WSContentSend_P(PSTR("
%s
")); + } +#ifdef USE_SONOFF_RF + if (SONOFF_BRIDGE == my_module_type) { + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("")); + uint32_t idx = 0; + for (uint32_t i = 0; i < 4; i++) { + if (idx > 0) { WSContentSend_P(PSTR("")); } + for (uint32_t j = 0; j < 4; j++) { + idx++; + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), idx); + WSContentSend_P(PSTR(""), idx, + (strlen(SettingsText(SET_BUTTON1 + idx -1))) ? SettingsText(SET_BUTTON1 + idx -1) : stemp); + } + } + WSContentSend_P(PSTR("")); + } +#endif + +#ifndef FIRMWARE_MINIMAL + XdrvCall(FUNC_WEB_ADD_MAIN_BUTTON); + XsnsCall(FUNC_WEB_ADD_MAIN_BUTTON); +#endif + + if (HTTP_ADMIN == Web.state) { +#ifdef FIRMWARE_MINIMAL + WSContentSpaceButton(BUTTON_FIRMWARE_UPGRADE); +#else + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentButton(BUTTON_INFORMATION); + WSContentButton(BUTTON_FIRMWARE_UPGRADE); +#endif + WSContentButton(BUTTON_CONSOLE); + WSContentButton(BUTTON_RESTART); + } + WSContentStop(); +} + +bool HandleRootStatusRefresh(void) +{ + if (!WebAuthenticate()) { + WebServer->requestAuthentication(); + return true; + } + + if (!WebServer->hasArg("m")) { + return false; + } + + #ifdef USE_SCRIPT_WEB_DISPLAY + Script_Check_HTML_Setvars(); + #endif + + char tmp[8]; + char svalue[32]; + char webindex[5]; + + WebGetArg("o", tmp, sizeof(tmp)); + if (strlen(tmp)) { + ShowWebSource(SRC_WEBGUI); + uint32_t device = atoi(tmp); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + if (device < 2) { + ExecuteCommandPower(1, POWER_TOGGLE, SRC_IGNORE); + } else { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), device -2); + ExecuteCommand(svalue, SRC_WEBGUI); + } + } else { +#endif +#ifdef USE_SHUTTER + int32_t ShutterWebButton; + if (ShutterWebButton = IsShutterWebButton(device)) { + snprintf_P(svalue, sizeof(svalue), PSTR("ShutterPosition%d %s"), abs(ShutterWebButton), (ShutterWebButton>0) ? PSTR(D_CMND_SHUTTER_TOGGLEUP) : PSTR(D_CMND_SHUTTER_TOGGLEDOWN)); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } else { +#endif + ExecuteCommandPower(device, POWER_TOGGLE, SRC_IGNORE); +#ifdef USE_SHUTTER + } +#endif +#ifdef USE_SONOFF_IFAN + } +#endif + } + WebGetArg("d0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + WebGetArg("w0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + for (uint32_t j = 1; j <= pwm_channels; j++) { + snprintf_P(webindex, sizeof(webindex), PSTR("e%d"), j); + WebGetArg(webindex, tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_CHANNEL "%d %s"), j, tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + } + WebGetArg("t0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + WebGetArg("h0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "1 %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + WebGetArg("n0", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "2 %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } +#ifdef USE_SHUTTER + for (uint32_t j = 1; j <= shutters_present; j++) { + snprintf_P(webindex, sizeof(webindex), PSTR("u%d"), j); + WebGetArg(webindex, tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR("ShutterPosition%d %s"), j, tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + } +#endif +#ifdef USE_SONOFF_RF + WebGetArg("k", tmp, sizeof(tmp)); + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RFKEY "%s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } +#endif + WSContentBegin(200, CT_HTML); + WSContentSend_P(PSTR("{t}")); + XsnsCall(FUNC_WEB_SENSOR); +#ifdef USE_SCRIPT_WEB_DISPLAY + XdrvCall(FUNC_WEB_SENSOR); +#endif + + WSContentSend_P(PSTR("")); + + if (devices_present) { + WSContentSend_P(PSTR("{t}")); + uint32_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { + WSContentSend_P(HTTP_DEVICE_STATE, 36, (bitRead(power, 0)) ? "bold" : "normal", 54, GetStateText(bitRead(power, 0))); + uint32_t fanspeed = GetFanspeed(); + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fanspeed); + WSContentSend_P(HTTP_DEVICE_STATE, 64, (fanspeed) ? "bold" : "normal", 54, (fanspeed) ? svalue : GetStateText(0)); + } else { +#endif + for (uint32_t idx = 1; idx <= devices_present; idx++) { + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(power, idx -1)); + WSContentSend_P(HTTP_DEVICE_STATE, 100 / devices_present, (bitRead(power, idx -1)) ? "bold" : "normal", fsize, (devices_present < 5) ? GetStateText(bitRead(power, idx -1)) : svalue); + } +#ifdef USE_SONOFF_IFAN + } +#endif + WSContentSend_P(PSTR("")); + } + WSContentEnd(); + + return true; +} + +#ifdef USE_SHUTTER +int32_t IsShutterWebButton(uint32_t idx) { + + int32_t ShutterWebButton = 0; + if (Settings.flag3.shutter_mode) { + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + if (Settings.shutter_startrelay[i] && ((Settings.shutter_startrelay[i] == idx) || (Settings.shutter_startrelay[i] == (idx-1)))) { + ShutterWebButton = (Settings.shutter_startrelay[i] == idx) ? (i+1): (-1-i); + break; + } + } + } + return ShutterWebButton; +} +#endif + + + +#ifndef FIRMWARE_MINIMAL + +void HandleConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURATION); + + WSContentStart_P(S_CONFIGURATION); + WSContentSendStyle(); + + WSContentButton(BUTTON_MODULE); + WSContentButton(BUTTON_WIFI); + + XdrvCall(FUNC_WEB_ADD_BUTTON); + XsnsCall(FUNC_WEB_ADD_BUTTON); + + WSContentButton(BUTTON_LOGGING); + WSContentButton(BUTTON_OTHER); + WSContentButton(BUTTON_TEMPLATE); + + WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); + WSContentButton(BUTTON_BACKUP); + WSContentButton(BUTTON_RESTORE); + + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} + + + +void HandleTemplateConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + if (WebServer->hasArg("save")) { + TemplateSaveSettings(); + WebRestart(1); + return; + } + + char stemp[30]; + + if (WebServer->hasArg("m")) { + WSContentBegin(200, CT_PLAIN); + for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { + uint32_t midx = pgm_read_byte(kModuleNiceList + i); + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), midx +1); + } + WSContentEnd(); + return; + } + + WebGetArg("t", stemp, sizeof(stemp)); + if (strlen(stemp)) { + uint32_t module = atoi(stemp); + uint32_t module_save = Settings.module; + Settings.module = module; + myio cmodule; + ModuleGpios(&cmodule); + gpio_flag flag = ModuleFlag(); + Settings.module = module_save; + + WSContentBegin(200, CT_PLAIN); + WSContentSend_P(PSTR("%s}1"), AnyModuleName(module).c_str()); + for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { + if (1 == i) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, 255, D_SENSOR_USER, 255); + } + uint32_t midx = pgm_read_byte(kGpioNiceList + i); + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); + } + WSContentSend_P(PSTR("}1")); + + for (uint32_t i = 0; i < ADC0_END; i++) { + if (1 == i) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, ADC0_USER, D_SENSOR_USER, ADC0_USER); + } + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, i, GetTextIndexed(stemp, sizeof(stemp), i, kAdc0Names), i); + } + WSContentSend_P(PSTR("}1")); + + for (uint32_t i = 0; i < sizeof(cmodule); i++) { + if ((i < 6) || ((i > 8) && (i != 11))) { + WSContentSend_P(PSTR("%s%d"), (i>0)?",":"", cmodule.io[i]); + } + } + WSContentSend_P(PSTR("}1%d}1%d"), flag, Settings.user_template_base); + WSContentEnd(); + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TEMPLATE); + + WSContentStart_P(S_CONFIGURE_TEMPLATE); + WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); + WSContentSend_P(HTTP_SCRIPT_TEMPLATE); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_TEMPLATE); + WSContentSend_P(HTTP_TABLE100); + WSContentSend_P(PSTR("" D_TEMPLATE_NAME "" + "" D_BASE_TYPE "" + "" + "
")); + WSContentSend_P(HTTP_TABLE100); + for (uint32_t i = 0; i < 17; i++) { + if ((i < 6) || ((i > 8) && (i != 11))) { + WSContentSend_P(PSTR("" D_GPIO "%d"), + ((9==i)||(10==i)) ? WebColor(COL_TEXT_WARNING) : WebColor(COL_TEXT), i, (0==i) ? " style='width:200px'" : "", i); + } + } + WSContentSend_P(PSTR("" D_ADC "0"), WebColor(COL_TEXT)); + WSContentSend_P(PSTR("")); + gpio_flag flag = ModuleFlag(); + if (flag.data > ADC0_USER) { + WSContentSend_P(HTTP_FORM_TEMPLATE_FLAG); + } + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void TemplateSaveSettings(void) +{ + char tmp[sizeof(Settings.user_template.name)]; + char webindex[5]; + char svalue[128]; + + WebGetArg("s1", tmp, sizeof(tmp)); + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " {\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), tmp); + + uint32_t j = 0; + for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { + if (6 == i) { j = 9; } + if (8 == i) { j = 12; } + snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), j); + WebGetArg(webindex, tmp, sizeof(tmp)); + uint8_t gpio = atoi(tmp); + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s%d"), svalue, (i>0)?",":"", gpio); + j++; + } + + WebGetArg("g17", tmp, sizeof(tmp)); + uint32_t flag = atoi(tmp); + for (uint32_t i = 0; i < GPIO_FLAG_USED; i++) { + snprintf_P(webindex, sizeof(webindex), PSTR("c%d"), i); + uint32_t state = WebServer->hasArg(webindex) << i +4; + flag += state; + } + WebGetArg("g99", tmp, sizeof(tmp)); + uint32_t base = atoi(tmp) +1; + + snprintf_P(svalue, sizeof(svalue), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), svalue, flag, base); + ExecuteWebCommand(svalue, SRC_WEBGUI); +} + + + +void HandleModuleConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + if (WebServer->hasArg("save")) { + ModuleSaveSettings(); + WebRestart(1); + return; + } + + char stemp[30]; + uint32_t midx; + myio cmodule; + ModuleGpios(&cmodule); + + if (WebServer->hasArg("m")) { + WSContentBegin(200, CT_PLAIN); + uint32_t vidx = 0; + for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { + if (0 == i) { + midx = USER_MODULE; + vidx = 0; + } else { + midx = pgm_read_byte(kModuleNiceList + i -1); + vidx = midx +1; + } + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), vidx); + } + WSContentEnd(); + return; + } + + if (WebServer->hasArg("g")) { + WSContentBegin(200, CT_PLAIN); + for (uint32_t j = 0; j < sizeof(kGpioNiceList); j++) { + midx = pgm_read_byte(kGpioNiceList + j); + if (!GetUsedInModule(midx, cmodule.io)) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); + } + } + WSContentEnd(); + return; + } + +#ifndef USE_ADC_VCC + if (WebServer->hasArg("a")) { + WSContentBegin(200, CT_PLAIN); + for (uint32_t j = 0; j < ADC0_END; j++) { + WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, j, GetTextIndexed(stemp, sizeof(stemp), j, kAdc0Names), j); + } + WSContentEnd(); + return; + } +#endif + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MODULE); + + WSContentStart_P(S_CONFIGURE_MODULE); + WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); + WSContentSend_P(HTTP_SCRIPT_MODULE1, Settings.module); + for (uint32_t i = 0; i < sizeof(cmodule); i++) { + if (ValidGPIO(i, cmodule.io[i])) { + WSContentSend_P(PSTR("sk(%d,%d);"), my_module.io[i], i); + } + } + WSContentSend_P(HTTP_SCRIPT_MODULE2, Settings.my_adc0); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_MODULE, AnyModuleName(MODULE).c_str()); + for (uint32_t i = 0; i < sizeof(cmodule); i++) { + if (ValidGPIO(i, cmodule.io[i])) { + snprintf_P(stemp, 3, PINS_WEMOS +i*2); + char sesp8285[40]; + snprintf_P(sesp8285, sizeof(sesp8285), PSTR("ESP8285"), WebColor(COL_TEXT_WARNING)); + WSContentSend_P(PSTR("%s " D_GPIO "%d %s"), + (WEMOS==my_module_type)?stemp:"", i, (0==i)? D_SENSOR_BUTTON "1":(1==i)? D_SERIAL_OUT :(3==i)? D_SERIAL_IN :((9==i)||(10==i))? sesp8285 :(12==i)? D_SENSOR_RELAY "1":(13==i)? D_SENSOR_LED "1i":(14==i)? D_SENSOR :"", i); + } + } +#ifndef USE_ADC_VCC + if (ValidAdc()) { + WSContentSend_P(PSTR("%s " D_ADC "0"), (WEMOS==my_module_type)?"A0":""); + } +#endif + WSContentSend_P(PSTR("")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void ModuleSaveSettings(void) +{ + char tmp[8]; + char webindex[5]; + + WebGetArg("g99", tmp, sizeof(tmp)); + uint32_t new_module = (!strlen(tmp)) ? MODULE : atoi(tmp); + Settings.last_module = Settings.module; + Settings.module = new_module; + SetModuleType(); + myio cmodule; + ModuleGpios(&cmodule); + String gpios = ""; + for (uint32_t i = 0; i < sizeof(cmodule); i++) { + if (Settings.last_module != new_module) { + Settings.my_gp.io[i] = GPIO_NONE; + } else { + if (ValidGPIO(i, cmodule.io[i])) { + snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), i); + WebGetArg(webindex, tmp, sizeof(tmp)); + Settings.my_gp.io[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + gpios += F(", " D_GPIO ); gpios += String(i); gpios += F(" "); gpios += String(Settings.my_gp.io[i]); + } + } + } +#ifndef USE_ADC_VCC + WebGetArg("g17", tmp, sizeof(tmp)); + Settings.my_adc0 = (!strlen(tmp)) ? 0 : atoi(tmp); + gpios += F(", " D_ADC "0 "); gpios += String(Settings.my_adc0); +#endif + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MODULE "%s " D_CMND_MODULE "%s"), ModuleName().c_str(), gpios.c_str()); +} + + + +const char kUnescapeCode[] = "&><\"\'"; +const char kEscapeCode[] PROGMEM = "&|>|<|"|'"; + +String HtmlEscape(const String unescaped) { + char escaped[10]; + size_t ulen = unescaped.length(); + String result = ""; + for (size_t i = 0; i < ulen; i++) { + char c = unescaped[i]; + char *p = strchr(kUnescapeCode, c); + if (p != nullptr) { + result += GetTextIndexed(escaped, sizeof(escaped), p - kUnescapeCode, kEscapeCode); + } else { + result += c; + } + } + return result; +} + + +const char kEncryptionType[] PROGMEM = "|||" D_WPA_PSK "||" D_WPA2_PSK "|" D_WEP "||" D_NONE "|" D_AUTO; + +void HandleWifiConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_WIFI); + + if (WebServer->hasArg("save") && HTTP_MANAGER_RESET_ONLY != Web.state) { + WifiSaveSettings(); + WebRestart(2); + return; + } + + WSContentStart_P(S_CONFIGURE_WIFI, !WifiIsInManagerMode()); + WSContentSend_P(HTTP_SCRIPT_WIFI); + WSContentSendStyle(); + + if (HTTP_MANAGER_RESET_ONLY != Web.state) { + if (WebServer->hasArg("scan")) { +#ifdef USE_EMULATION + UdpDisconnect(); +#endif + int n = WiFi.scanNetworks(); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SCAN_DONE)); + + if (0 == n) { + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, S_NO_NETWORKS_FOUND); + WSContentSend_P(S_NO_NETWORKS_FOUND); + WSContentSend_P(PSTR(". " D_REFRESH_TO_SCAN_AGAIN ".")); + } else { + + int indices[n]; + for (uint32_t i = 0; i < n; i++) { + indices[i] = i; + } + + + for (uint32_t i = 0; i < n; i++) { + for (uint32_t j = i + 1; j < n; j++) { + if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) { + std::swap(indices[i], indices[j]); + } + } + } + + + String cssid; + for (uint32_t i = 0; i < n; i++) { + if (-1 == indices[i]) { continue; } + cssid = WiFi.SSID(indices[i]); + uint32_t cschn = WiFi.channel(indices[i]); + for (uint32_t j = i + 1; j < n; j++) { + if ((cssid == WiFi.SSID(indices[j])) && (cschn == WiFi.channel(indices[j]))) { + DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_DUPLICATE_ACCESSPOINT " %s"), WiFi.SSID(indices[j]).c_str()); + indices[j] = -1; + } + } + } + + + for (uint32_t i = 0; i < n; i++) { + if (-1 == indices[i]) { continue; } + DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_SSID " %s, " D_BSSID " %s, " D_CHANNEL " %d, " D_RSSI " %d"), + WiFi.SSID(indices[i]).c_str(), WiFi.BSSIDstr(indices[i]).c_str(), WiFi.channel(indices[i]), WiFi.RSSI(indices[i])); + int quality = WifiGetRssiAsQuality(WiFi.RSSI(indices[i])); + int auth = WiFi.encryptionType(indices[i]); + char encryption[20]; + WSContentSend_P(PSTR("
%s (%d) %s %d%% (%d dBm)
"), + HtmlEscape(WiFi.SSID(indices[i])).c_str(), + WiFi.channel(indices[i]), + GetTextIndexed(encryption, sizeof(encryption), auth +1, kEncryptionType), + quality, WiFi.RSSI(indices[i]) + ); + delay(0); + + } + WSContentSend_P(PSTR("
")); + } + } else { + WSContentSend_P(PSTR("
")); + } + + + WSContentSend_P(HTTP_FORM_WIFI, SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), WIFI_HOSTNAME, WIFI_HOSTNAME, SettingsText(SET_HOSTNAME), SettingsText(SET_CORS)); + WSContentSend_P(HTTP_FORM_END); + } + + if (WifiIsInManagerMode()) { +#ifndef FIRMWARE_MINIMAL + WSContentSpaceButton(BUTTON_RESTORE); + WSContentButton(BUTTON_RESET_CONFIGURATION); +#endif + WSContentSpaceButton(BUTTON_RESTART); + } else { + WSContentSpaceButton(BUTTON_CONFIGURATION); + } + WSContentStop(); +} + +void WifiSaveSettings(void) +{ + char tmp[TOPSZ]; + + WebGetArg("h", tmp, sizeof(tmp)); + SettingsUpdateText(SET_HOSTNAME, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp); + if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { + SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); + } + WebGetArg("c", tmp, sizeof(tmp)); + SettingsUpdateText(SET_CORS, (!strlen(tmp)) ? CORS_DOMAIN : tmp); + WebGetArg("s1", tmp, sizeof(tmp)); + SettingsUpdateText(SET_STASSID1, (!strlen(tmp)) ? STA_SSID1 : tmp); + WebGetArg("s2", tmp, sizeof(tmp)); + SettingsUpdateText(SET_STASSID2, (!strlen(tmp)) ? STA_SSID2 : tmp); + WebGetArg("p1", tmp, sizeof(tmp)); + SettingsUpdateText(SET_STAPWD1, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD1) : tmp); + WebGetArg("p2", tmp, sizeof(tmp)); + SettingsUpdateText(SET_STAPWD2, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD2) : tmp); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s, " D_CMND_CORS " %s"), + SettingsText(SET_HOSTNAME), SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), SettingsText(SET_CORS)); +} + + + +void HandleLoggingConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_LOGGING); + + if (WebServer->hasArg("save")) { + LoggingSaveSettings(); + HandleConfiguration(); + return; + } + + WSContentStart_P(S_CONFIGURE_LOGGING); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_LOG1); + char stemp1[45]; + char stemp2[32]; + uint8_t dlevel[4] = { LOG_LEVEL_INFO, LOG_LEVEL_INFO, LOG_LEVEL_NONE, LOG_LEVEL_NONE }; + for (uint32_t idx = 0; idx < 4; idx++) { + if ((2==idx) && !Settings.flag.mqtt_enabled) { continue; } + uint32_t llevel = (0==idx)?Settings.seriallog_level:(1==idx)?Settings.weblog_level:(2==idx)?Settings.mqttlog_level:Settings.syslog_level; + WSContentSend_P(PSTR("

%s (%s)

")); + } + WSContentSend_P(HTTP_FORM_LOG2, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void LoggingSaveSettings(void) +{ + char tmp[TOPSZ]; + + WebGetArg("l0", tmp, sizeof(tmp)); + SetSeriallog((!strlen(tmp)) ? SERIAL_LOG_LEVEL : atoi(tmp)); + WebGetArg("l1", tmp, sizeof(tmp)); + Settings.weblog_level = (!strlen(tmp)) ? WEB_LOG_LEVEL : atoi(tmp); + WebGetArg("l2", tmp, sizeof(tmp)); + Settings.mqttlog_level = (!strlen(tmp)) ? MQTT_LOG_LEVEL : atoi(tmp); + WebGetArg("l3", tmp, sizeof(tmp)); + SetSyslog((!strlen(tmp)) ? SYS_LOG_LEVEL : atoi(tmp)); + WebGetArg("lh", tmp, sizeof(tmp)); + SettingsUpdateText(SET_SYSLOG_HOST, (!strlen(tmp)) ? SYS_LOG_HOST : tmp); + WebGetArg("lp", tmp, sizeof(tmp)); + Settings.syslog_port = (!strlen(tmp)) ? SYS_LOG_PORT : atoi(tmp); + WebGetArg("lt", tmp, sizeof(tmp)); + Settings.tele_period = (!strlen(tmp)) ? TELE_PERIOD : atoi(tmp); + if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) { + Settings.tele_period = 10; + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D_CMND_WEBLOG " %d, " D_CMND_MQTTLOG " %d, " D_CMND_SYSLOG " %d, " D_CMND_LOGHOST " %s, " D_CMND_LOGPORT " %d, " D_CMND_TELEPERIOD " %d"), + Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period); +} + + + +void HandleOtherConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_OTHER); + + if (WebServer->hasArg("save")) { + OtherSaveSettings(); + WebRestart(1); + return; + } + + WSContentStart_P(S_CONFIGURE_OTHER); + WSContentSendStyle(); + + TemplateJson(); + char stemp[strlen(mqtt_data) +1]; + strlcpy(stemp, mqtt_data, sizeof(stemp)); + WSContentSend_P(HTTP_FORM_OTHER, stemp, (USER_MODULE == Settings.module) ? " checked disabled" : "", (Settings.flag.mqtt_enabled) ? " checked" : ""); + + uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { maxfn = 1; } +#endif + for (uint32_t i = 0; i < maxfn; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i +1); + WSContentSend_P(PSTR("" D_FRIENDLY_NAME " %d (" FRIENDLY_NAME "%s)

"), + i +1, + (i) ? stemp : "", + i, + (i) ? stemp : "", + SettingsText(SET_FRIENDLYNAME1 + i)); + } + +#ifdef USE_EMULATION + WSContentSend_P(PSTR("

 " D_EMULATION " 

")); + for (uint32_t i = 0; i < EMUL_MAX; i++) { +#ifndef USE_EMULATION_WEMO + if (i == EMUL_WEMO) { i++; } +#endif +#ifndef USE_EMULATION_HUE + if (i == EMUL_HUE) { i++; } +#endif + if (i < EMUL_MAX) { + WSContentSend_P(PSTR("%s %s
"), + i, i, + (i == Settings.flag2.emulation) ? " checked" : "", + GetTextIndexed(stemp, sizeof(stemp), i, kEmulationOptions), + (i == EMUL_NONE) ? "" : (i == EMUL_WEMO) ? D_SINGLE_DEVICE : D_MULTI_DEVICE); + } + } + WSContentSend_P(PSTR("

")); +#endif + + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void OtherSaveSettings(void) +{ + char tmp[TOPSZ]; + char webindex[5]; + char friendlyname[TOPSZ]; + char message[LOGSZ]; + + WebGetArg("wp", tmp, sizeof(tmp)); + SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp); + Settings.flag.mqtt_enabled = WebServer->hasArg("b1"); +#ifdef USE_EMULATION + WebGetArg("b2", tmp, sizeof(tmp)); + Settings.flag2.emulation = (!strlen(tmp)) ? 0 : atoi(tmp); +#endif + + snprintf_P(message, sizeof(message), PSTR(D_LOG_OTHER D_MQTT_ENABLE " %s, " D_CMND_EMULATION " %d, " D_CMND_FRIENDLYNAME), GetStateText(Settings.flag.mqtt_enabled), Settings.flag2.emulation); + for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) { + snprintf_P(webindex, sizeof(webindex), PSTR("a%d"), i); + WebGetArg(webindex, tmp, sizeof(tmp)); + snprintf_P(friendlyname, sizeof(friendlyname), PSTR(FRIENDLY_NAME"%d"), i +1); + SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp); + snprintf_P(message, sizeof(message), PSTR("%s%s %s"), message, (i) ? "," : "", SettingsText(SET_FRIENDLYNAME1 +i)); + } + AddLog_P(LOG_LEVEL_INFO, message); + + WebGetArg("t1", tmp, sizeof(tmp)); + if (strlen(tmp)) { + char svalue[128]; + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " %s"), tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + + if (WebServer->hasArg("t2")) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_MODULE " 0")); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + + } +} + + + +void HandleBackupConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_BACKUP_CONFIGURATION)); + + if (!SettingsBufferAlloc()) { return; } + + WiFiClient myClient = WebServer->client(); + WebServer->setContentLength(sizeof(Settings)); + + char attachment[TOPSZ]; + + + + + char hostname[sizeof(my_hostname)]; + snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(hostname, my_hostname), my_version); + + WebServer->sendHeader(F("Content-Disposition"), attachment); + + WSSend(200, CT_STREAM, ""); + + uint32_t cfg_crc32 = Settings.cfg_crc32; + Settings.cfg_crc32 = GetSettingsCrc32(); + + memcpy(settings_buffer, &Settings, sizeof(Settings)); + if (Web.config_xor_on_set) { + for (uint32_t i = 2; i < sizeof(Settings); i++) { + settings_buffer[i] ^= (Web.config_xor_on_set +i); + } + } + +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + size_t written = myClient.write((const char*)settings_buffer, sizeof(Settings)); + if (written < sizeof(Settings)) { + myClient.write((const char*)settings_buffer +written, sizeof(Settings) -written); + } +#else + myClient.write((const char*)settings_buffer, sizeof(Settings)); +#endif + + SettingsBufferFree(); + + Settings.cfg_crc32 = cfg_crc32; +} + + + +void HandleResetConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESET_CONFIGURATION); + + WSContentStart_P(S_RESET_CONFIGURATION, !WifiIsInManagerMode()); + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_CONFIGURATION_RESET "
")); + WSContentSend_P(HTTP_MSG_RSTRT); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); + + char command[CMDSZ]; + snprintf_P(command, sizeof(command), PSTR(D_CMND_RESET " 1")); + ExecuteWebCommand(command, SRC_WEBGUI); +} + +void HandleRestoreConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTORE_CONFIGURATION); + + WSContentStart_P(S_RESTORE_CONFIGURATION); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_RST); + WSContentSend_P(HTTP_FORM_RST_UPG, D_RESTORE); + if (WifiIsInManagerMode()) { + WSContentSpaceButton(BUTTON_MAIN); + } else { + WSContentSpaceButton(BUTTON_CONFIGURATION); + } + WSContentStop(); + + Web.upload_error = 0; + Web.upload_file_type = UPL_SETTINGS; +} + + + +void HandleInformation(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_INFORMATION); + + char stopic[TOPSZ]; + + int freeMem = ESP.getFreeHeap(); + + WSContentStart_P(S_INFORMATION); + + + + WSContentSend_P(HTTP_SCRIPT_INFO_BEGIN); + WSContentSend_P(PSTR("
")); + WSContentSend_P(PSTR(D_PROGRAM_VERSION "}2%s%s"), my_version, my_image); + WSContentSend_P(PSTR("}1" D_BUILD_DATE_AND_TIME "}2%s"), GetBuildDateAndTime().c_str()); + WSContentSend_P(PSTR("}1" D_CORE_AND_SDK_VERSION "}2" ARDUINO_ESP8266_RELEASE "/%s"), ESP.getSdkVersion()); + WSContentSend_P(PSTR("}1" D_UPTIME "}2%s"), GetUptime().c_str()); + WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d at 0x%X"), Settings.save_flag, GetSettingsAddress()); + WSContentSend_P(PSTR("}1" D_BOOT_COUNT "}2%d"), Settings.bootcount); + WSContentSend_P(PSTR("}1" D_RESTART_REASON "}2%s"), GetResetReason().c_str()); + uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { maxfn = 1; } +#endif + for (uint32_t i = 0; i < maxfn; i++) { + WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, SettingsText(SET_FRIENDLYNAME1 +i)); + } + WSContentSend_P(PSTR("}1}2 ")); + WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%, %d dBm)"), Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI()); + WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (Wifi.mdns_begun) ? ".local" : ""); +#if LWIP_IPV6 + String ipv6_addr = WifiGetIPv6(); + if(ipv6_addr != ""){ + WSContentSend_P(PSTR("}1 IPv6 Address }2%s"), ipv6_addr.c_str()); + } +#endif + if (static_cast(WiFi.localIP()) != 0) { + WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.localIP().toString().c_str()); + WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), IPAddress(Settings.ip_address[1]).toString().c_str()); + WSContentSend_P(PSTR("}1" D_SUBNET_MASK "}2%s"), IPAddress(Settings.ip_address[2]).toString().c_str()); + WSContentSend_P(PSTR("}1" D_DNS_SERVER "}2%s"), IPAddress(Settings.ip_address[3]).toString().c_str()); + WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.macAddress().c_str()); + } + if (static_cast(WiFi.softAPIP()) != 0) { + WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.softAPIP().toString().c_str()); + WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), WiFi.softAPIP().toString().c_str()); + WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.softAPmacAddress().c_str()); + } + WSContentSend_P(PSTR("}1}2 ")); + if (Settings.flag.mqtt_enabled) { + WSContentSend_P(PSTR("}1" D_MQTT_HOST "}2%s"), SettingsText(SET_MQTT_HOST)); + WSContentSend_P(PSTR("}1" D_MQTT_PORT "}2%d"), Settings.mqtt_port); + WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), SettingsText(SET_MQTT_USER)); + WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), mqtt_client); + WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC)); + + WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), GetGroupTopic_P(stopic, "")); + WSContentSend_P(PSTR("}1" D_MQTT_FULL_TOPIC "}2%s"), GetTopic_P(stopic, CMND, mqtt_topic, "")); + WSContentSend_P(PSTR("}1" D_MQTT " " D_FALLBACK_TOPIC "}2%s"), GetFallbackTopic_P(stopic, "")); + } else { + WSContentSend_P(PSTR("}1" D_MQTT "}2" D_DISABLED)); + } + WSContentSend_P(PSTR("}1}2 ")); + +#ifdef USE_EMULATION + WSContentSend_P(PSTR("}1" D_EMULATION "}2%s"), GetTextIndexed(stopic, sizeof(stopic), Settings.flag2.emulation, kEmulationOptions)); +#else + WSContentSend_P(PSTR("}1" D_EMULATION "}2" D_DISABLED)); +#endif + +#ifdef USE_DISCOVERY + WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2%s"), (Settings.flag3.mdns_enabled) ? D_ENABLED : D_DISABLED); + if (Settings.flag3.mdns_enabled) { +#ifdef WEBSERVER_ADVERTISE + WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_WEB_SERVER)); +#else + WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_DISABLED)); +#endif + } +#else + WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2" D_DISABLED)); +#endif + + WSContentSend_P(PSTR("}1}2 ")); + WSContentSend_P(PSTR("}1" D_ESP_CHIP_ID "}2%d"), ESP.getChipId()); + WSContentSend_P(PSTR("}1" D_FLASH_CHIP_ID "}20x%06X"), ESP.getFlashChipId()); + WSContentSend_P(PSTR("}1" D_FLASH_CHIP_SIZE "}2%dkB"), ESP.getFlashChipRealSize() / 1024); + WSContentSend_P(PSTR("}1" D_PROGRAM_FLASH_SIZE "}2%dkB"), ESP.getFlashChipSize() / 1024); + WSContentSend_P(PSTR("}1" D_PROGRAM_SIZE "}2%dkB"), ESP.getSketchSize() / 1024); + WSContentSend_P(PSTR("}1" D_FREE_PROGRAM_SPACE "}2%dkB"), ESP.getFreeSketchSpace() / 1024); + WSContentSend_P(PSTR("}1" D_FREE_MEMORY "}2%dkB"), freeMem / 1024); + WSContentSend_P(PSTR("
")); + + WSContentSend_P(HTTP_SCRIPT_INFO_END); + WSContentSendStyle(); + + WSContentSend_P(PSTR("" + "
")); + + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} +#endif + + + +void HandleUpgradeFirmware(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_FIRMWARE_UPGRADE); + + WSContentStart_P(S_FIRMWARE_UPGRADE); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_UPG, SettingsText(SET_OTAURL)); + WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); + + Web.upload_error = 0; + Web.upload_file_type = UPL_TASMOTA; +} + +void HandleUpgradeFirmwareStart(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + char command[TOPSZ + 10]; + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED)); + WifiConfigCounter(); + + char otaurl[TOPSZ]; + WebGetArg("o", otaurl, sizeof(otaurl)); + if (strlen(otaurl)) { + snprintf_P(command, sizeof(command), PSTR(D_CMND_OTAURL " %s"), otaurl); + ExecuteWebCommand(command, SRC_WEBGUI); + } + + WSContentStart_P(S_INFORMATION); + WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA); + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_UPGRADE_STARTED " ...
")); + WSContentSend_P(HTTP_MSG_RSTRT); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); + + snprintf_P(command, sizeof(command), PSTR(D_CMND_UPGRADE " 1")); + ExecuteWebCommand(command, SRC_WEBGUI); +} + +void HandleUploadDone(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE)); + + char error[100]; + + WifiConfigCounter(); + restart_flag = 0; + MqttRetryCounter(0); + + WSContentStart_P(S_INFORMATION); + if (!Web.upload_error) { + WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA); + } + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_UPLOAD " " D_FAILED "

"), WebColor(COL_TEXT_WARNING)); +#ifdef USE_RF_FLASH + if (Web.upload_error < 15) { +#else + if ((Web.upload_error < 10) || (14 == Web.upload_error)) { + if (14 == Web.upload_error) { Web.upload_error = 10; } +#endif + GetTextIndexed(error, sizeof(error), Web.upload_error -1, kUploadErrors); + } else { + snprintf_P(error, sizeof(error), PSTR(D_UPLOAD_ERROR_CODE " %d"), Web.upload_error); + } + WSContentSend_P(error); + DEBUG_CORE_LOG(PSTR("UPL: %s"), error); + stop_flash_rotate = Settings.flag.stop_flash_rotate; + } else { + WSContentSend_P(PSTR("%06x'>" D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS)); + WSContentSend_P(HTTP_MSG_RSTRT); + ShowWebSource(SRC_WEBGUI); +#ifdef USE_TASMOTA_SLAVE + if (TasmotaSlave_GetFlagFlashing()) { + restart_flag = 0; + } else { + restart_flag = 2; + } +#else + restart_flag = 2; +#endif + } + SettingsBufferFree(); + WSContentSend_P(PSTR("

")); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +#ifdef USE_TASMOTA_SLAVE + if (TasmotaSlave_GetFlagFlashing()) { + TasmotaSlave_Flash(); + } +#endif +} + +void HandleUploadLoop(void) +{ + + bool _serialoutput = (LOG_LEVEL_DEBUG <= seriallog_level); + + if (HTTP_USER == Web.state) { return; } + if (Web.upload_error) { + if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); } + return; + } + + HTTPUpload& upload = WebServer->upload(); + + if (UPLOAD_FILE_START == upload.status) { + restart_flag = 60; + if (0 == upload.filename.c_str()[0]) { + Web.upload_error = 1; + return; + } + SettingsSave(1); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_FILE " %s ..."), upload.filename.c_str()); + if (UPL_SETTINGS == Web.upload_file_type) { + if (!SettingsBufferAlloc()) { + Web.upload_error = 2; + return; + } + } else { + MqttRetryCounter(60); +#ifdef USE_EMULATION + UdpDisconnect(); +#endif +#ifdef USE_ARILUX_RF + AriluxRfDisable(); +#endif + if (Settings.flag.mqtt_enabled) { + MqttDisconnect(); + } + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + if (!Update.begin(maxSketchSpace)) { + + + + + + + Web.upload_error = 2; + return; + } + } + Web.upload_progress_dot_count = 0; + } else if (!Web.upload_error && (UPLOAD_FILE_WRITE == upload.status)) { + if (0 == upload.totalSize) { + if (UPL_SETTINGS == Web.upload_file_type) { + Web.config_block_count = 0; + } + else { +#ifdef USE_RF_FLASH + if ((SONOFF_BRIDGE == my_module_type) && (upload.buf[0] == ':')) { + Update.end(); + Web.upload_file_type = UPL_EFM8BB1; + + Web.upload_error = SnfBrUpdateInit(); + if (Web.upload_error != 0) { return; } + } else +#endif +#ifdef USE_TASMOTA_SLAVE + if ((WEMOS == my_module_type) && (upload.buf[0] == ':')) { + Update.end(); + Web.upload_file_type = UPL_TASMOTASLAVE; + Web.upload_error = TasmotaSlave_UpdateInit(); + if (Web.upload_error != 0) { return; } + } else +#endif + { + if ((upload.buf[0] != 0xE9) && (upload.buf[0] != 0x1F)) { + Web.upload_error = 3; + return; + } + if (0xE9 == upload.buf[0]) { + uint32_t bin_flash_size = ESP.magicFlashChipSize((upload.buf[3] & 0xf0) >> 4); + if (bin_flash_size > ESP.getFlashChipRealSize()) { + Web.upload_error = 4; + return; + } + + } + } + } + } + if (UPL_SETTINGS == Web.upload_file_type) { + if (!Web.upload_error) { + if (upload.currentSize > (sizeof(Settings) - (Web.config_block_count * HTTP_UPLOAD_BUFLEN))) { + Web.upload_error = 9; + return; + } + memcpy(settings_buffer + (Web.config_block_count * HTTP_UPLOAD_BUFLEN), upload.buf, upload.currentSize); + Web.config_block_count++; + } + } +#ifdef USE_RF_FLASH + else if (UPL_EFM8BB1 == Web.upload_file_type) { + if (efm8bb1_update != nullptr) { + ssize_t result = rf_glue_remnant_with_new_data_and_write(efm8bb1_update, upload.buf, upload.currentSize); + free(efm8bb1_update); + efm8bb1_update = nullptr; + if (result != 0) { + Web.upload_error = abs(result); + return; + } + } + ssize_t result = rf_search_and_write(upload.buf, upload.currentSize); + if (result < 0) { + Web.upload_error = abs(result); + return; + } else if (result > 0) { + if ((size_t)result > upload.currentSize) { + + Web.upload_error = 9; + return; + } + + size_t remnant_sz = upload.currentSize - result; + efm8bb1_update = (uint8_t *) malloc(remnant_sz + 1); + if (efm8bb1_update == nullptr) { + Web.upload_error = 2; + return; + } + memcpy(efm8bb1_update, upload.buf + result, remnant_sz); + + efm8bb1_update[remnant_sz] = '\0'; + } + } +#endif +#ifdef USE_TASMOTA_SLAVE + else if (UPL_TASMOTASLAVE == Web.upload_file_type) { + TasmotaSlave_WriteBuffer(upload.buf, upload.currentSize); + } +#endif + else { + if (!Web.upload_error && (Update.write(upload.buf, upload.currentSize) != upload.currentSize)) { + Web.upload_error = 5; + return; + } + if (_serialoutput) { + Serial.printf("."); + Web.upload_progress_dot_count++; + if (!(Web.upload_progress_dot_count % 80)) { Serial.println(); } + } + } + } else if(!Web.upload_error && (UPLOAD_FILE_END == upload.status)) { + if (_serialoutput && (Web.upload_progress_dot_count % 80)) { + Serial.println(); + } + if (UPL_SETTINGS == Web.upload_file_type) { + if (Web.config_xor_on_set) { + for (uint32_t i = 2; i < sizeof(Settings); i++) { + settings_buffer[i] ^= (Web.config_xor_on_set +i); + } + } + bool valid_settings = false; + unsigned long buffer_version = settings_buffer[11] << 24 | settings_buffer[10] << 16 | settings_buffer[9] << 8 | settings_buffer[8]; + if (buffer_version > 0x06000000) { + uint32_t buffer_size = settings_buffer[3] << 8 | settings_buffer[2]; + if (buffer_version > 0x0606000A) { + uint32_t buffer_crc32 = settings_buffer[4095] << 24 | settings_buffer[4094] << 16 | settings_buffer[4093] << 8 | settings_buffer[4092]; + valid_settings = (GetCfgCrc32(settings_buffer, buffer_size -4) == buffer_crc32); + } else { + uint16_t buffer_crc16 = settings_buffer[15] << 8 | settings_buffer[14]; + valid_settings = (GetCfgCrc16(settings_buffer, buffer_size) == buffer_crc16); + } + } else { + valid_settings = (settings_buffer[0] == CONFIG_FILE_SIGN); + } + if (valid_settings) { + SettingsDefaultSet2(); + memcpy((char*)&Settings +16, settings_buffer +16, sizeof(Settings) -16); + Settings.version = buffer_version; + SettingsBufferFree(); + } else { + Web.upload_error = 8; + return; + } + } +#ifdef USE_RF_FLASH + else if (UPL_EFM8BB1 == Web.upload_file_type) { + + Web.upload_file_type = UPL_TASMOTA; + } +#endif +#ifdef USE_TASMOTA_SLAVE + else if (UPL_TASMOTASLAVE == Web.upload_file_type) { + + TasmotaSlave_SetFlagFlashing(true); + Web.upload_file_type = UPL_TASMOTA; + } +#endif + else { + if (!Update.end(true)) { + if (_serialoutput) { Update.printError(Serial); } + Web.upload_error = 6; + return; + } + if (!VersionCompatible()) { + Web.upload_error = 14; + return; + } + } + if (!Web.upload_error) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes. " D_RESTARTING), upload.totalSize); + } + } else if (UPLOAD_FILE_ABORTED == upload.status) { + restart_flag = 0; + MqttRetryCounter(0); + Web.upload_error = 7; + if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); } + } + delay(0); +} + + + +void HandlePreflightRequest(void) +{ + HttpHeaderCors(); + WebServer->sendHeader(F("Access-Control-Allow-Methods"), F("GET, POST")); + WebServer->sendHeader(F("Access-Control-Allow-Headers"), F("authorization")); + WSSend(200, CT_HTML, ""); +} + + + +void HandleHttpCommand(void) +{ + if (!HttpCheckPriviledgedAccess(false)) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND)); + + bool valid = true; + if (strlen(SettingsText(SET_WEBPWD))) { + char tmp1[33]; + WebGetArg("user", tmp1, sizeof(tmp1)); + char tmp2[strlen(SettingsText(SET_WEBPWD)) +1]; + WebGetArg("password", tmp2, sizeof(tmp2)); + if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, SettingsText(SET_WEBPWD)))) { valid = false; } + } + + WSContentBegin(200, CT_JSON); + if (valid) { + uint32_t curridx = web_log_index; + String svalue = WebServer->arg("cmnd"); + if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { + ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCOMMAND); + if (web_log_index != curridx) { + uint32_t counter = curridx; + WSContentSend_P(PSTR("{")); + bool cflg = false; + do { + char* tmp; + size_t len; + GetLog(counter, &tmp, &len); + if (len) { + + char* JSON = (char*)memchr(tmp, '{', len); + if (JSON) { + size_t JSONlen = len - (JSON - tmp); + if (JSONlen > sizeof(mqtt_data)) { JSONlen = sizeof(mqtt_data); } + char stemp[JSONlen]; + strlcpy(stemp, JSON +1, JSONlen -2); + WSContentSend_P(PSTR("%s%s"), (cflg) ? "," : "", stemp); + cflg = true; + } + } + counter++; + counter &= 0xFF; + if (!counter) counter++; + } while (counter != web_log_index); + WSContentSend_P(PSTR("}")); + } else { + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENABLE_WEBLOG_FOR_RESPONSE "\"}")); + } + } else { + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENTER_COMMAND " cmnd=\"}")); + } + } else { + WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_NEED_USER_AND_PASSWORD "\"}")); + } + WSContentEnd(); +} + + + +void HandleConsole(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + if (WebServer->hasArg("c2")) { + HandleConsoleRefresh(); + return; + } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONSOLE); + + WSContentStart_P(S_CONSOLE); + WSContentSend_P(HTTP_SCRIPT_CONSOL, Settings.web_refresh); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_CMND); + WSContentSpaceButton(BUTTON_MAIN); + WSContentStop(); +} + +void HandleConsoleRefresh(void) +{ + bool cflg = true; + uint32_t counter = 0; + + String svalue = WebServer->arg("c1"); + if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str()); + ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE); + } + + char stmp[8]; + WebGetArg("c2", stmp, sizeof(stmp)); + if (strlen(stmp)) { counter = atoi(stmp); } + + WSContentBegin(200, CT_PLAIN); + WSContentSend_P(PSTR("%d}1%d}1"), web_log_index, Web.reset_web_log_flag); + if (!Web.reset_web_log_flag) { + counter = 0; + Web.reset_web_log_flag = true; + } + if (counter != web_log_index) { + if (!counter) { + counter = web_log_index; + cflg = false; + } + do { + char* tmp; + size_t len; + GetLog(counter, &tmp, &len); + if (len) { + if (len > sizeof(mqtt_data) -2) { len = sizeof(mqtt_data); } + char stemp[len +1]; + strlcpy(stemp, tmp, len); + WSContentSend_P(PSTR("%s%s"), (cflg) ? "\n" : "", stemp); + cflg = true; + } + counter++; + counter &= 0xFF; + if (!counter) { counter++; } + } while (counter != web_log_index); + } + WSContentSend_P(PSTR("}1")); + WSContentEnd(); +} + + + +void HandleNotFound(void) +{ + + + if (CaptivePortal()) { return; } + +#ifdef USE_EMULATION +#ifdef USE_EMULATION_HUE + String path = WebServer->uri(); + if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) { + HandleHueApi(&path); + } else +#endif +#endif + { + WSContentBegin(404, CT_PLAIN); + WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), WebServer->uri().c_str(), (WebServer->method() == HTTP_GET) ? "GET" : "POST", WebServer->args()); + for (uint32_t i = 0; i < WebServer->args(); i++) { + WSContentSend_P(PSTR(" %s: %s\n"), WebServer->argName(i).c_str(), WebServer->arg(i).c_str()); + } + WSContentEnd(); + } +} + + +bool CaptivePortal(void) +{ + + if ((WifiIsInManagerMode()) && !ValidIpAddress(WebServer->hostHeader().c_str())) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_REDIRECTED)); + + WebServer->sendHeader(F("Location"), String("http://") + WebServer->client().localIP().toString(), true); + WSSend(302, CT_PLAIN, ""); + WebServer->client().stop(); + return true; + } + return false; +} + + + +String UrlEncode(const String& text) +{ + const char hex[] = "0123456789ABCDEF"; + + String encoded = ""; + int len = text.length(); + int i = 0; + while (i < len) { + char decodedChar = text.charAt(i++); +# 2672 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_01_webserver.ino" + if ((' ' == decodedChar) || ('+' == decodedChar)) { + encoded += '%'; + encoded += hex[decodedChar >> 4]; + encoded += hex[decodedChar & 0xF]; + } else { + encoded += decodedChar; + } + + } + return encoded; +} + +int WebSend(char *buffer) +{ + + + + + + char *host; + char *user; + char *password; + char *command; + int status = 1; + + + host = strtok_r(buffer, "]", &command); + if (host && command) { + RemoveSpace(host); + host++; + host = strtok_r(host, ",", &user); + String url = F("http://"); + url += host; + + command = Trim(command); + if (command[0] != '/') { + url += F("/cm?"); + if (user) { + user = strtok_r(user, ":", &password); + if (user && password) { + char userpass[200]; + snprintf_P(userpass, sizeof(userpass), PSTR("user=%s&password=%s&"), user, password); + url += userpass; + } + } + url += F("cmnd="); + } + url += command; + + DEBUG_CORE_LOG(PSTR("WEB: Uri |%s|"), url.c_str()); + +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) + HTTPClient http; + if (http.begin(UrlEncode(url))) { +#else + WiFiClient http_client; + HTTPClient http; + if (http.begin(http_client, UrlEncode(url))) { +#endif + int http_code = http.GET(); + if (http_code > 0) { + if (http_code == HTTP_CODE_OK || http_code == HTTP_CODE_MOVED_PERMANENTLY) { +#ifdef USE_WEBSEND_RESPONSE + + const char* read = http.getString().c_str(); + uint32_t j = 0; + char text = '.'; + while (text != '\0') { + text = *read++; + if (text > 31) { + mqtt_data[j++] = text; + if (j == sizeof(mqtt_data) -2) { break; } + } + } + mqtt_data[j] = '\0'; + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WEBSEND)); +#ifdef USE_SCRIPT +extern uint8_t tasm_cmd_activ; + + tasm_cmd_activ=0; + XdrvRulesProcess(); +#endif +#endif + } + status = 0; + } else { + status = 2; + } + http.end(); + } else { + status = 3; + } + } + return status; +} + +bool JsonWebColor(const char* dataBuf) +{ + + + + + + char dataBufLc[strlen(dataBuf) +1]; + LowerCase(dataBufLc, dataBuf); + RemoveSpace(dataBufLc); + if (strlen(dataBufLc) < 9) { return false; } + + StaticJsonBuffer<450> jb; + JsonObject& obj = jb.parseObject(dataBufLc); + if (!obj.success()) { return false; } + + char parm_lc[10]; + if (obj[LowerCase(parm_lc, D_CMND_WEBCOLOR)].success()) { + for (uint32_t i = 0; i < COL_LAST; i++) { + const char* color = obj[parm_lc][i]; + if (color != nullptr) { + WebHexCode(i, color); + } + } + } + return true; +} + +const char kWebSendStatus[] PROGMEM = D_JSON_DONE "|" D_JSON_WRONG_PARAMETERS "|" D_JSON_CONNECT_FAILED "|" D_JSON_HOST_NOT_FOUND "|" D_JSON_MEMORY_ERROR; + +const char kWebCommands[] PROGMEM = "|" +#ifdef USE_EMULATION + D_CMND_EMULATION "|" +#endif +#ifdef USE_SENDMAIL + D_CMND_SENDMAIL "|" +#endif + D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBCOLOR "|" + D_CMND_WEBSENSOR "|" D_CMND_WEBBUTTON "|" D_CMND_CORS; + +void (* const WebCommand[])(void) PROGMEM = { +#ifdef USE_EMULATION + &CmndEmulation, +#endif +#ifdef USE_SENDMAIL + &CmndSendmail, +#endif + &CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, + &CmndWebSensor, &CmndWebButton, &CmndCors }; + + + + + +#ifdef USE_EMULATION +void CmndEmulation(void) +{ +#if defined(USE_EMULATION_WEMO) && defined(USE_EMULATION_HUE) + if ((XdrvMailbox.payload >= EMUL_NONE) && (XdrvMailbox.payload < EMUL_MAX)) { +#else +#ifndef USE_EMULATION_WEMO + if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_HUE == XdrvMailbox.payload)) { +#endif +#ifndef USE_EMULATION_HUE + if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_WEMO == XdrvMailbox.payload)) { +#endif +#endif + Settings.flag2.emulation = XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndNumber(Settings.flag2.emulation); +} +#endif + +#ifdef USE_SENDMAIL +void CmndSendmail(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t result = SendMail(XdrvMailbox.data); + char stemp1[20]; + ResponseCmndChar(GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); + } +} +#endif + + +void CmndWebServer(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + Settings.webserver = XdrvMailbox.payload; + } + if (Settings.webserver) { + Response_P(PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); + } else { + ResponseCmndStateText(0); + } +} + +void CmndWebPassword(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_WEBPWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data); + ResponseCmndChar(SettingsText(SET_WEBPWD)); + } else { + Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); + } +} + +void CmndWeblog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { + Settings.weblog_level = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.weblog_level); +} + +void CmndWebRefresh(void) +{ + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload <= 10000)) { + Settings.web_refresh = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.web_refresh); +} + +void CmndWebSend(void) +{ + if (XdrvMailbox.data_len > 0) { + uint32_t result = WebSend(XdrvMailbox.data); + char stemp1[20]; + ResponseCmndChar(GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); + } +} + +void CmndWebColor(void) +{ + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, "{") == nullptr) { + if ((XdrvMailbox.data_len > 3) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= COL_LAST)) { + WebHexCode(XdrvMailbox.index -1, XdrvMailbox.data); + } + else if (0 == XdrvMailbox.payload) { + SettingsDefaultWebColor(); + } + } + else { + JsonWebColor(XdrvMailbox.data); + } + } + Response_P(PSTR("{\"" D_CMND_WEBCOLOR "\":[")); + for (uint32_t i = 0; i < COL_LAST; i++) { + if (i) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"#%06x\""), WebColor(i)); + } + ResponseAppend_P(PSTR("]}")); +} + +void CmndWebSensor(void) +{ + if (XdrvMailbox.index < MAX_XSNS_DRIVERS) { + if (XdrvMailbox.payload >= 0) { + bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); + } + } + Response_P(PSTR("{\"" D_CMND_WEBSENSOR "\":")); + XsnsSensorState(); + ResponseJsonEnd(); +} + +void CmndWebButton(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_BUTTON_TEXT)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_BUTTON1, MAX_BUTTON_TEXT); + } else { + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_BUTTON1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); + } + ResponseCmndIdxChar(SettingsText(SET_BUTTON1 + XdrvMailbox.index -1)); + } + } +} + +void CmndCors(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_CORS, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data); + } + ResponseCmndChar(SettingsText(SET_CORS)); +} + + + + + +bool Xdrv01(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_LOOP: + PollDnsWebserver(); +#ifdef USE_EMULATION + if (Settings.flag2.emulation) { PollUdp(); } +#endif + break; + case FUNC_COMMAND: + result = DecodeCommand(kWebCommands, WebCommand); + break; + } + return result; +} +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_02_mqtt.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_02_mqtt.ino" +#define XDRV_02 2 + + + +#ifdef USE_MQTT_TLS + #include "WiFiClientSecureLightBearSSL.h" + BearSSL::WiFiClientSecure_light *tlsClient; +#else + WiFiClient EspClient; +#endif + +const char kMqttCommands[] PROGMEM = "|" +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) + D_CMND_MQTTFINGERPRINT "|" +#endif + D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + D_CMND_TLSKEY "|" +#endif + D_CMND_MQTTHOST "|" D_CMND_MQTTPORT "|" D_CMND_MQTTRETRY "|" D_CMND_STATETEXT "|" D_CMND_MQTTCLIENT "|" + D_CMND_FULLTOPIC "|" D_CMND_PREFIX "|" D_CMND_GROUPTOPIC "|" D_CMND_TOPIC "|" D_CMND_PUBLISH "|" D_CMND_MQTTLOG "|" + D_CMND_BUTTONTOPIC "|" D_CMND_SWITCHTOPIC "|" D_CMND_BUTTONRETAIN "|" D_CMND_SWITCHRETAIN "|" D_CMND_POWERRETAIN "|" D_CMND_SENSORRETAIN ; + +void (* const MqttCommand[])(void) PROGMEM = { +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) + &CmndMqttFingerprint, +#endif + &CmndMqttUser, &CmndMqttPassword, +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + &CmndTlsKey, +#endif + &CmndMqttHost, &CmndMqttPort, &CmndMqttRetry, &CmndStateText, &CmndMqttClient, + &CmndFullTopic, &CmndPrefix, &CmndGroupTopic, &CmndTopic, &CmndPublish, &CmndMqttlog, + &CmndButtonTopic, &CmndSwitchTopic, &CmndButtonRetain, &CmndSwitchRetain, &CmndPowerRetain, &CmndSensorRetain }; + +struct MQTT { + uint16_t connect_count = 0; + uint16_t retry_counter = 1; + uint8_t initial_connection_state = 2; + bool connected = false; + bool allowed = false; +} Mqtt; + +#ifdef USE_MQTT_TLS + +#ifdef USE_MQTT_AWS_IOT +#include + +const br_ec_private_key *AWS_IoT_Private_Key = nullptr; +const br_x509_certificate *AWS_IoT_Client_Certificate = nullptr; + +class tls_entry_t { +public: + uint32_t name; + uint16_t start; + uint16_t len; +}; + +const static uint32_t TLS_NAME_SKEY = 0x2079656B; +const static uint32_t TLS_NAME_CRT = 0x20747263; + +class tls_dir_t { +public: + tls_entry_t entry[4]; +}; + +tls_dir_t tls_dir; + +#endif + + + + +bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value) { + for (uint32_t i = 0; i<20; i++) { + if (finger[i] != value) { + return false; + } + } + return true; +} +#endif + +void MakeValidMqtt(uint32_t option, char* str) +{ + + + uint32_t i = 0; + while (str[i] > 0) { + + if ((str[i] == '+') || (str[i] == '#') || (str[i] == ' ')) { + if (option) { + uint32_t j = i; + while (str[j] > 0) { + str[j] = str[j +1]; + j++; + } + i--; + } else { + str[i] = '_'; + } + } + i++; + } +} + +#ifdef USE_DISCOVERY +#ifdef MQTT_HOST_DISCOVERY +void MqttDiscoverServer(void) +{ + if (!Wifi.mdns_begun) { return; } + + int n = MDNS.queryService("mqtt", "tcp"); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n); + + if (n > 0) { + uint32_t i = 0; +#ifdef MDNS_HOSTNAME + for (i = n; i > 0; i--) { + if (!strcmp(MDNS.hostname(i).c_str(), MDNS_HOSTNAME)) { + break; + } + } +#endif + SettingsUpdateText(SET_MQTT_HOST, MDNS.IP(i).toString().c_str()); + Settings.mqtt_port = MDNS.port(i); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"), MDNS.hostname(i).c_str(), SettingsText(SET_MQTT_HOST), Settings.mqtt_port); + } +} +#endif +#endif +# 163 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_02_mqtt.ino" +#include + + +#if (MQTT_MAX_PACKET_SIZE -TOPSZ -7) < MIN_MESSZ + #error "MQTT_MAX_PACKET_SIZE is too small in libraries/PubSubClient/src/PubSubClient.h, increase it to at least 1200" +#endif + +#ifdef USE_MQTT_TLS +PubSubClient MqttClient; +#else +PubSubClient MqttClient(EspClient); +#endif + +void MqttInit(void) +{ +#ifdef USE_MQTT_TLS + tlsClient = new BearSSL::WiFiClientSecure_light(1024,1024); + +#ifdef USE_MQTT_AWS_IOT + loadTlsDir(); + tlsClient->setClientECCert(AWS_IoT_Client_Certificate, + AWS_IoT_Private_Key, + 0xFFFF , 0); +#endif + +#ifdef USE_MQTT_TLS_CA_CERT +#ifdef USE_MQTT_AWS_IOT + tlsClient->setTrustAnchor(&AmazonRootCA1_TA); +#else + tlsClient->setTrustAnchor(&LetsEncryptX3CrossSigned_TA); +#endif +#endif + + MqttClient.setClient(*tlsClient); +#endif +} + +bool MqttIsConnected(void) +{ + return MqttClient.connected(); +} + +void MqttDisconnect(void) +{ + MqttClient.disconnect(); +} + +void MqttSubscribeLib(const char *topic) +{ + MqttClient.subscribe(topic); + MqttClient.loop(); +} + +void MqttUnsubscribeLib(const char *topic) +{ + MqttClient.unsubscribe(topic); + MqttClient.loop(); +} + +bool MqttPublishLib(const char* topic, bool retained) +{ + + if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { + char *str = strstr(topic, SettingsText(SET_MQTTPREFIX1)); + if (str == topic) { + mqtt_cmnd_blocked_reset = 4; + mqtt_cmnd_blocked++; + } + } + + bool result = MqttClient.publish(topic, mqtt_data, retained); + yield(); + return result; +} + +void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len) +{ +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("MqttDataHandler")); +#endif + + + if (data_len >= MQTT_MAX_PACKET_SIZE) { return; } + + + if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { + char *str = strstr(mqtt_topic, SettingsText(SET_MQTTPREFIX1)); + if ((str == mqtt_topic) && mqtt_cmnd_blocked) { + mqtt_cmnd_blocked--; + return; + } + } + + + char topic[TOPSZ]; + strlcpy(topic, mqtt_topic, sizeof(topic)); + mqtt_data[data_len] = 0; + char data[data_len +1]; + memcpy(data, mqtt_data, sizeof(data)); + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_MQTT D_RECEIVED_TOPIC " \"%s\", " D_DATA_SIZE " %d, " D_DATA " \"%s\""), topic, data_len, data); + + + + XdrvMailbox.index = strlen(topic); + XdrvMailbox.data_len = data_len; + XdrvMailbox.topic = topic; + XdrvMailbox.data = (char*)data; + if (XdrvCall(FUNC_MQTT_DATA)) { return; } + + ShowSource(SRC_MQTT); + + CommandHandler(topic, data, data_len); +} + + + +void MqttRetryCounter(uint8_t value) +{ + Mqtt.retry_counter = value; +} + +void MqttSubscribe(const char *topic) +{ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_SUBSCRIBE_TO " %s"), topic); + MqttSubscribeLib(topic); +} + +void MqttUnsubscribe(const char *topic) +{ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_UNSUBSCRIBE_FROM " %s"), topic); + MqttUnsubscribeLib(topic); +} + +void MqttPublishLogging(const char *mxtime) +{ + char saved_mqtt_data[strlen(mqtt_data) +1]; + memcpy(saved_mqtt_data, mqtt_data, sizeof(saved_mqtt_data)); + + + Response_P(PSTR("%s%s"), mxtime, log_data); + char stopic[TOPSZ]; + GetTopic_P(stopic, STAT, mqtt_topic, PSTR("LOGGING")); + MqttPublishLib(stopic, false); + + memcpy(mqtt_data, saved_mqtt_data, sizeof(saved_mqtt_data)); +} + +void MqttPublish(const char* topic, bool retained) +{ +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("MqttPublish")); +#endif + +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) || defined(MQTT_NO_RETAIN) + + + + retained = false; +#endif + + char sretained[CMDSZ]; + sretained[0] = '\0'; + char slog_type[20]; + snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_RESULT)); + + if (Settings.flag.mqtt_enabled) { + if (MqttPublishLib(topic, retained)) { + snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_MQTT)); + if (retained) { + snprintf_P(sretained, sizeof(sretained), PSTR(" (" D_RETAINED ")")); + } + } + } + + snprintf_P(log_data, sizeof(log_data), PSTR("%s%s = %s"), slog_type, (Settings.flag.mqtt_enabled) ? topic : strrchr(topic,'/')+1, mqtt_data); + if (strlen(log_data) >= (sizeof(log_data) - strlen(sretained) -1)) { + log_data[sizeof(log_data) - strlen(sretained) -5] = '\0'; + snprintf_P(log_data, sizeof(log_data), PSTR("%s ..."), log_data); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s%s"), log_data, sretained); + AddLog(LOG_LEVEL_INFO); + + if (Settings.ledstate &0x04) { + blinks++; + } +} + +void MqttPublish(const char* topic) +{ + MqttPublish(topic, false); +} + +void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retained) +{ + + + + + + + + char romram[64]; + char stopic[TOPSZ]; + + snprintf_P(romram, sizeof(romram), ((prefix > 3) && !Settings.flag.mqtt_response) ? S_RSLT_RESULT : subtopic); + for (uint32_t i = 0; i < strlen(romram); i++) { + romram[i] = toupper(romram[i]); + } + prefix &= 3; + GetTopic_P(stopic, prefix, mqtt_topic, romram); + MqttPublish(stopic, retained); + +#ifdef USE_MQTT_AWS_IOT + if ((prefix > 0) && (Settings.flag4.awsiot_shadow)) { + + char *topic = SettingsText(SET_MQTT_TOPIC); + char topic2[strlen(topic)+1]; + strcpy(topic2, topic); + + char *s = topic2; + while (*s) { + if ('/' == *s) { + *s = '_'; + } + s++; + } + + snprintf_P(romram, sizeof(romram), PSTR("$aws/things/%s/shadow/update"), topic2); + + + char *mqtt_save = (char*) malloc(strlen(mqtt_data)+1); + if (!mqtt_save) { return; } + strcpy(mqtt_save, mqtt_data); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"state\":{\"reported\":%s}}"), mqtt_save); + free(mqtt_save); + + bool result = MqttClient.publish(romram, mqtt_data, false); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Updated shadow: %s"), romram); + yield(); + } +#endif +} + +void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic) +{ + MqttPublishPrefixTopic_P(prefix, subtopic, false); +} + +void MqttPublishTeleSensor(void) +{ + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + XdrvRulesProcess(); +} + +void MqttPublishPowerState(uint32_t device) +{ + char stopic[TOPSZ]; + char scommand[33]; + + if ((device < 1) || (device > devices_present)) { device = 1; } + +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (device > 1)) { + if (GetFanspeed() < MaxFanspeed()) { +#ifdef USE_DOMOTICZ + DomoticzUpdateFanState(); +#endif + snprintf_P(scommand, sizeof(scommand), PSTR(D_CMND_FANSPEED)); + GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); + Response_P(S_JSON_COMMAND_NVALUE, scommand, GetFanspeed()); + MqttPublish(stopic); + } + } else { +#endif + GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable); + GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); + Response_P(S_JSON_COMMAND_SVALUE, scommand, GetStateText(bitRead(power, device -1))); + MqttPublish(stopic); + + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P(GetStateText(bitRead(power, device -1))); + MqttPublish(stopic, Settings.flag.mqtt_power_retain); +#ifdef USE_SONOFF_IFAN + } +#endif +} + +void MqttPublishAllPowerState(void) +{ + for (uint32_t i = 1; i <= devices_present; i++) { + MqttPublishPowerState(i); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan()) { break; } +#endif + } +} + +void MqttPublishPowerBlinkState(uint32_t device) +{ + char scommand[33]; + + if ((device < 1) || (device > devices_present)) { + device = 1; + } + Response_P(PSTR("{\"%s\":\"" D_JSON_BLINK " %s\"}"), + GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable), GetStateText(bitRead(blink_mask, device -1))); + + MqttPublishPrefixTopic_P(RESULT_OR_STAT, S_RSLT_POWER); +} + + + +uint16_t MqttConnectCount(void) +{ + return Mqtt.connect_count; +} + +void MqttDisconnected(int state) +{ + Mqtt.connected = false; + Mqtt.retry_counter = Settings.mqtt_retry; + + MqttClient.disconnect(); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), SettingsText(SET_MQTT_HOST), Settings.mqtt_port, state, Mqtt.retry_counter); + rules_flag.mqtt_disconnected = 1; +} + +void MqttConnected(void) +{ + char stopic[TOPSZ]; + + if (Mqtt.allowed) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_CONNECTED)); + Mqtt.connected = true; + Mqtt.retry_counter = 0; + Mqtt.connect_count++; + + GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); + Response_P(PSTR(D_ONLINE)); + MqttPublish(stopic, true); + + + mqtt_data[0] = '\0'; + MqttPublishPrefixTopic_P(CMND, S_RSLT_POWER); + + GetTopic_P(stopic, CMND, mqtt_topic, PSTR("#")); + MqttSubscribe(stopic); + if (strstr_P(SettingsText(SET_MQTT_FULLTOPIC), MQTT_TOKEN_TOPIC) != nullptr) { + GetGroupTopic_P(stopic, PSTR("#")); + MqttSubscribe(stopic); + GetFallbackTopic_P(stopic, PSTR("#")); + MqttSubscribe(stopic); + } + + XdrvCall(FUNC_MQTT_SUBSCRIBE); + } + + if (Mqtt.initial_connection_state) { + char stopic2[TOPSZ]; + Response_P(PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), + ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, ""), GetGroupTopic_P(stopic2, "")); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); +#ifdef USE_WEBSERVER + if (Settings.webserver) { +#if LWIP_IPV6 + Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str(),WifiGetIPv6().c_str()); +#else + Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"), + (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); +#endif + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); + } +#endif + Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":")); + if (CrashFlag()) { + CrashDump(); + } else { + ResponseAppend_P(PSTR("\"%s\""), GetResetReason().c_str()); + } + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); + MqttPublishAllPowerState(); + if (Settings.tele_period) { + tele_period = Settings.tele_period -5; + } + rules_flag.system_boot = 1; + XdrvCall(FUNC_MQTT_INIT); + } + Mqtt.initial_connection_state = 0; + + global_state.mqtt_down = 0; + if (Settings.flag.mqtt_enabled) { + rules_flag.mqtt_connected = 1; + } +} + +void MqttReconnect(void) +{ + char stopic[TOPSZ]; + + Mqtt.allowed = Settings.flag.mqtt_enabled; + if (Mqtt.allowed) { +#ifdef USE_DISCOVERY +#ifdef MQTT_HOST_DISCOVERY + MqttDiscoverServer(); +#endif +#endif + if (!strlen(SettingsText(SET_MQTT_HOST)) || !Settings.mqtt_port) { + Mqtt.allowed = false; + } +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + + if (!AWS_IoT_Private_Key || !AWS_IoT_Client_Certificate) { + Mqtt.allowed = false; + } +#endif + } + if (!Mqtt.allowed) { + MqttConnected(); + return; + } + +#ifdef USE_EMULATION + UdpDisconnect(); +#endif + + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_ATTEMPTING_CONNECTION)); + + Mqtt.connected = false; + Mqtt.retry_counter = Settings.mqtt_retry; + global_state.mqtt_down = 1; + + char *mqtt_user = nullptr; + char *mqtt_pwd = nullptr; + if (strlen(SettingsText(SET_MQTT_USER))) { + mqtt_user = SettingsText(SET_MQTT_USER); + } + if (strlen(SettingsText(SET_MQTT_PWD))) { + mqtt_pwd = SettingsText(SET_MQTT_PWD); + } + + GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); + Response_P(S_OFFLINE); + + if (MqttClient.connected()) { MqttClient.disconnect(); } +#ifdef USE_MQTT_TLS + tlsClient->stop(); +#else + EspClient = WiFiClient(); + MqttClient.setClient(EspClient); +#endif + + if (2 == Mqtt.initial_connection_state) { + Mqtt.initial_connection_state = 1; + } + + MqttClient.setCallback(MqttDataHandler); +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + + tlsClient->setClientECCert(AWS_IoT_Client_Certificate, + AWS_IoT_Private_Key, + 0xFFFF , 0); +#endif + MqttClient.setServer(SettingsText(SET_MQTT_HOST), Settings.mqtt_port); + + uint32_t mqtt_connect_time = millis(); +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) + bool allow_all_fingerprints = false; + bool learn_fingerprint1 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0x00); + bool learn_fingerprint2 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0x00); + allow_all_fingerprints |= is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0xff); + allow_all_fingerprints |= is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0xff); + allow_all_fingerprints |= learn_fingerprint1; + allow_all_fingerprints |= learn_fingerprint2; + tlsClient->setPubKeyFingerprint(Settings.mqtt_fingerprint[0], Settings.mqtt_fingerprint[1], allow_all_fingerprints); +#endif +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "AWS IoT endpoint: %s"), SettingsText(SET_MQTT_HOST)); + if (MqttClient.connect(mqtt_client, nullptr, nullptr, stopic, 1, false, mqtt_data, MQTT_CLEAN_SESSION)) { +#else + if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd, stopic, 1, true, mqtt_data, MQTT_CLEAN_SESSION)) { +#endif +#ifdef USE_MQTT_TLS + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connected in %d ms, max ThunkStack used %d"), + millis() - mqtt_connect_time, tlsClient->getMaxThunkStackUse()); + if (!tlsClient->getMFLNStatus()) { + AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR("MFLN not supported by TLS server")); + } +#ifndef USE_MQTT_TLS_CA_CERT + + char buf_fingerprint[64]; + ToHex_P((unsigned char *)tlsClient->getRecvPubKeyFingerprint(), 20, buf_fingerprint, sizeof(buf_fingerprint), ' '); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Server fingerprint: %s"), buf_fingerprint); + + if (learn_fingerprint1 || learn_fingerprint2) { + + bool fingerprint_matched = false; + const uint8_t *recv_fingerprint = tlsClient->getRecvPubKeyFingerprint(); + if (0 == memcmp(recv_fingerprint, Settings.mqtt_fingerprint[0], 20)) { + fingerprint_matched = true; + } + if (0 == memcmp(recv_fingerprint, Settings.mqtt_fingerprint[1], 20)) { + fingerprint_matched = true; + } + if (!fingerprint_matched) { + + if (learn_fingerprint1) { + memcpy(Settings.mqtt_fingerprint[0], recv_fingerprint, 20); + } + if (learn_fingerprint2) { + memcpy(Settings.mqtt_fingerprint[1], recv_fingerprint, 20); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Fingerprint learned: %s"), buf_fingerprint); + + SettingsSaveAll(); + } + } +#endif +#endif + MqttConnected(); + } else { +#ifdef USE_MQTT_TLS + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connection error: %d"), tlsClient->getLastError()); +#endif + MqttDisconnected(MqttClient.state()); + } +} + +void MqttCheck(void) +{ + if (Settings.flag.mqtt_enabled) { + if (!MqttIsConnected()) { + global_state.mqtt_down = 1; + if (!Mqtt.retry_counter) { + MqttReconnect(); + } else { + Mqtt.retry_counter--; + } + } else { + global_state.mqtt_down = 0; + } + } else { + global_state.mqtt_down = 0; + if (Mqtt.initial_connection_state) { + MqttReconnect(); + } + } +} + + + + + +#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) +void CmndMqttFingerprint(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + char fingerprint[60]; + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(fingerprint))) { + strlcpy(fingerprint, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? MQTT_FINGERPRINT1 : MQTT_FINGERPRINT2 : XdrvMailbox.data, sizeof(fingerprint)); + char *p = fingerprint; + for (uint32_t i = 0; i < 20; i++) { + Settings.mqtt_fingerprint[XdrvMailbox.index -1][i] = strtol(p, &p, 16); + } + restart_flag = 2; + } + ResponseCmndIdxChar(ToHex_P((unsigned char *)Settings.mqtt_fingerprint[XdrvMailbox.index -1], 20, fingerprint, sizeof(fingerprint), ' ')); + } +} +#endif + +void CmndMqttUser(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndChar(SettingsText(SET_MQTT_USER)); +} + +void CmndMqttPassword(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_MQTT_PWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_PASS : XdrvMailbox.data); + ResponseCmndChar(SettingsText(SET_MQTT_PWD)); + restart_flag = 2; + } else { + Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); + } +} + +void CmndMqttlog(void) +{ + if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { + Settings.mqttlog_level = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.mqttlog_level); +} + +void CmndMqttHost(void) +{ + if (XdrvMailbox.data_len > 0) { + SettingsUpdateText(SET_MQTT_HOST, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndChar(SettingsText(SET_MQTT_HOST)); +} + +void CmndMqttPort(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { + Settings.mqtt_port = (1 == XdrvMailbox.payload) ? MQTT_PORT : XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndNumber(Settings.mqtt_port); +} + +void CmndMqttRetry(void) +{ + if ((XdrvMailbox.payload >= MQTT_RETRY_SECS) && (XdrvMailbox.payload < 32001)) { + Settings.mqtt_retry = XdrvMailbox.payload; + Mqtt.retry_counter = Settings.mqtt_retry; + } + ResponseCmndNumber(Settings.mqtt_retry); +} + +void CmndStateText(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_STATE_TEXT)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_STATE_TXT1, MAX_STATE_TEXT); + } else { + if (XdrvMailbox.data_len > 0) { + for (uint32_t i = 0; i <= XdrvMailbox.data_len; i++) { + if (XdrvMailbox.data[i] == ' ') XdrvMailbox.data[i] = '_'; + } + SettingsUpdateText(SET_STATE_TXT1 + XdrvMailbox.index -1, XdrvMailbox.data); + } + ResponseCmndIdxChar(GetStateText(XdrvMailbox.index -1)); + } + } +} + +void CmndMqttClient(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndChar(SettingsText(SET_MQTT_CLIENT)); +} + +void CmndFullTopic(void) +{ + if (XdrvMailbox.data_len > 0) { + MakeValidMqtt(1, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + char stemp1[TOPSZ]; + strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_FULLTOPIC : XdrvMailbox.data, sizeof(stemp1)); + if (strcmp(stemp1, SettingsText(SET_MQTT_FULLTOPIC))) { + Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); + MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); + SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp1); + restart_flag = 2; + } + } + ResponseCmndChar(SettingsText(SET_MQTT_FULLTOPIC)); +} + +void CmndPrefix(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_MQTT_PREFIXES)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_MQTTPREFIX1, MAX_MQTT_PREFIXES); + } else { + if (XdrvMailbox.data_len > 0) { + MakeValidMqtt(0, XdrvMailbox.data); + SettingsUpdateText(SET_MQTTPREFIX1 + XdrvMailbox.index -1, + (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index) ? SUB_PREFIX : (2==XdrvMailbox.index) ? PUB_PREFIX : PUB_PREFIX2 : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndIdxChar(SettingsText(SET_MQTTPREFIX1 + XdrvMailbox.index -1)); + } + } +} + +void CmndPublish(void) +{ + if (XdrvMailbox.data_len > 0) { + char *payload_part; + char *mqtt_part = strtok_r(XdrvMailbox.data, " ", &payload_part); + if (mqtt_part) { + char stemp1[TOPSZ]; + strlcpy(stemp1, mqtt_part, sizeof(stemp1)); + if ((payload_part != nullptr) && strlen(payload_part)) { + strlcpy(mqtt_data, payload_part, sizeof(mqtt_data)); + } else { + mqtt_data[0] = '\0'; + } + MqttPublish(stemp1, (XdrvMailbox.index == 2)); + + mqtt_data[0] = '\0'; + } + } +} + +void CmndGroupTopic(void) +{ + if (XdrvMailbox.data_len > 0) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + SettingsUpdateText(SET_MQTT_GRP_TOPIC, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); + restart_flag = 2; + } + ResponseCmndChar(SettingsText(SET_MQTT_GRP_TOPIC)); +} + +void CmndTopic(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + char stemp1[TOPSZ]; + strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_TOPIC : XdrvMailbox.data, sizeof(stemp1)); + if (strcmp(stemp1, SettingsText(SET_MQTT_TOPIC))) { + Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); + MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); + SettingsUpdateText(SET_MQTT_TOPIC, stemp1); + restart_flag = 2; + } + } + ResponseCmndChar(SettingsText(SET_MQTT_TOPIC)); +} + +void CmndButtonTopic(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + switch (Shortcut()) { + case SC_CLEAR: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, ""); break; + case SC_DEFAULT: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, mqtt_topic); break; + case SC_USER: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC); break; + default: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, XdrvMailbox.data); + } + } + ResponseCmndChar(SettingsText(SET_MQTT_BUTTON_TOPIC)); +} + +void CmndSwitchTopic(void) +{ + if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { + MakeValidMqtt(0, XdrvMailbox.data); + if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } + switch (Shortcut()) { + case SC_CLEAR: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, ""); break; + case SC_DEFAULT: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, mqtt_topic); break; + case SC_USER: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); break; + default: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, XdrvMailbox.data); + } + } + ResponseCmndChar(SettingsText(SET_MQTT_SWITCH_TOPIC)); +} + +void CmndButtonRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + for (uint32_t i = 1; i <= MAX_KEYS; i++) { + SendKey(KEY_BUTTON, i, CLEAR_RETAIN); + } + } + Settings.flag.mqtt_button_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_button_retain); +} + +void CmndSwitchRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + for (uint32_t i = 1; i <= MAX_SWITCHES; i++) { + SendKey(KEY_SWITCH, i, CLEAR_RETAIN); + } + } + Settings.flag.mqtt_switch_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_switch_retain); +} + +void CmndPowerRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + char stemp1[TOPSZ]; + char scommand[CMDSZ]; + for (uint32_t i = 1; i <= devices_present; i++) { + GetTopic_P(stemp1, STAT, mqtt_topic, GetPowerDevice(scommand, i, sizeof(scommand), Settings.flag.device_index_enable)); + mqtt_data[0] = '\0'; + MqttPublish(stemp1, Settings.flag.mqtt_power_retain); + } + } + Settings.flag.mqtt_power_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_power_retain); +} + +void CmndSensorRetain(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + if (!XdrvMailbox.payload) { + mqtt_data[0] = '\0'; + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_ENERGY), Settings.flag.mqtt_sensor_retain); + } + Settings.flag.mqtt_sensor_retain = XdrvMailbox.payload; + } + ResponseCmndStateText(Settings.flag.mqtt_sensor_retain); +} + + + + +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + + + +const static uint16_t tls_spi_start_sector = 0xFF; +const static uint8_t* tls_spi_start = (uint8_t*) 0x402FF000; +const static size_t tls_spi_len = 0x1000; +const static size_t tls_block_offset = 0x0400; +const static size_t tls_block_len = 0x0400; +const static size_t tls_obj_store_offset = tls_block_offset + sizeof(tls_dir_t); + + +inline void TlsEraseBuffer(uint8_t *buffer) { + memset(buffer + tls_block_offset, 0xFF, tls_block_len); +} + + + +static br_ec_private_key EC = { + 23, + nullptr, 0 +}; + +static br_x509_certificate CHAIN[] = { + { nullptr, 0 } +}; + + + +void loadTlsDir(void) { + memcpy_P(&tls_dir, tls_spi_start + tls_block_offset, sizeof(tls_dir)); + + + if ((TLS_NAME_SKEY == tls_dir.entry[0].name) && (tls_dir.entry[0].len > 0)) { + EC.x = (unsigned char *)(tls_spi_start + tls_obj_store_offset + tls_dir.entry[0].start); + EC.xlen = tls_dir.entry[0].len; + AWS_IoT_Private_Key = &EC; + } else { + AWS_IoT_Private_Key = nullptr; + } + if ((TLS_NAME_CRT == tls_dir.entry[1].name) && (tls_dir.entry[1].len > 0)) { + CHAIN[0].data = (unsigned char *) (tls_spi_start + tls_obj_store_offset + tls_dir.entry[1].start); + CHAIN[0].data_len = tls_dir.entry[1].len; + AWS_IoT_Client_Certificate = CHAIN; + } else { + AWS_IoT_Client_Certificate = nullptr; + } + +} + +const char ALLOCATE_ERROR[] PROGMEM = "TLSKey " D_JSON_ERROR ": cannot allocate buffer."; + +void CmndTlsKey(void) { +#ifdef DEBUG_DUMP_TLS + if (0 == XdrvMailbox.index){ + CmndTlsDump(); + } +#endif + if ((XdrvMailbox.index >= 1) && (XdrvMailbox.index <= 2)) { + tls_dir_t *tls_dir_write; + + if (XdrvMailbox.data_len > 0) { + + uint8_t *spi_buffer = (uint8_t*) malloc(tls_spi_len); + if (!spi_buffer) { + AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR); + return; + } + memcpy_P(spi_buffer, tls_spi_start, tls_spi_len); + + + RemoveAllSpaces(XdrvMailbox.data); + + + uint32_t bin_len = decode_base64_length((unsigned char*)XdrvMailbox.data); + uint8_t *bin_buf = nullptr; + if (bin_len > 0) { + bin_buf = (uint8_t*) malloc(bin_len + 4); + if (!bin_buf) { + AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR); + free(spi_buffer); + return; + } + } + + + if (bin_len > 0) { + decode_base64((unsigned char*)XdrvMailbox.data, bin_buf); + } + + + tls_dir_write = (tls_dir_t*) (spi_buffer + tls_block_offset); + + if (1 == XdrvMailbox.index) { + + + TlsEraseBuffer(spi_buffer); + if (bin_len > 0) { + if (bin_len != 32) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate must be 32 bytes: %d."), bin_len); + free(spi_buffer); + free(bin_buf); + return; + } + tls_entry_t *entry = &tls_dir_write->entry[0]; + entry->name = TLS_NAME_SKEY; + entry->start = 0; + entry->len = bin_len; + memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len); + } else { + + } + } else if (2 == XdrvMailbox.index) { + + if (TLS_NAME_SKEY != tls_dir.entry[0].name) { + + AddLog_P(LOG_LEVEL_INFO, PSTR("TLSKey: cannot store Cert if no Key previously stored.")); + free(spi_buffer); + free(bin_buf); + return; + } + if (bin_len <= 256) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate length too short: %d."), bin_len); + free(spi_buffer); + free(bin_buf); + return; + } + tls_entry_t *entry = &tls_dir_write->entry[1]; + entry->name = TLS_NAME_CRT; + entry->start = (tls_dir_write->entry[0].start + tls_dir_write->entry[0].len + 3) & ~0x03; + entry->len = bin_len; + memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len); + } + + if (ESP.flashEraseSector(tls_spi_start_sector)) { + ESP.flashWrite(tls_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + } + free(spi_buffer); + free(bin_buf); + } + + loadTlsDir(); + Response_P(PSTR("{\"%s1\":%d,\"%s2\":%d}"), + XdrvMailbox.command, AWS_IoT_Private_Key ? tls_dir.entry[0].len : -1, + XdrvMailbox.command, AWS_IoT_Client_Certificate ? tls_dir.entry[1].len : -1); + } +} + +#ifdef DEBUG_DUMP_TLS + +uint32_t bswap32(uint32_t x) { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +void CmndTlsDump(void) { + uint32_t start = (uint32_t)tls_spi_start + tls_block_offset; + uint32_t end = start + tls_block_len -1; + for (uint32_t pos = start; pos < end; pos += 0x10) { + uint32_t* values = (uint32_t*)(pos); +#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 + Serial.printf("%08x: %08x %08x %08x %08x\n", pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3])); +#else + Serial.printf_P(PSTR("%08x: %08x %08x %08x %08x\n"), pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3])); +#endif + } +} +#endif +#endif + + + + + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_MQTT "mq" + +const char S_CONFIGURE_MQTT[] PROGMEM = D_CONFIGURE_MQTT; + +const char HTTP_BTN_MENU_MQTT[] PROGMEM = + "

"; + +const char HTTP_FORM_MQTT1[] PROGMEM = + "
 " D_MQTT_PARAMETERS " " + "
" + "

" D_HOST " (" MQTT_HOST ")

" + "

" D_PORT " (" STR(MQTT_PORT) ")

" + "

" D_CLIENT " (%s)

"; +const char HTTP_FORM_MQTT2[] PROGMEM = + "

" D_USER " (" MQTT_USER ")

" + "

" D_PASSWORD "

" + "

" D_TOPIC " = %%topic%% (%s)

" + "

" D_FULL_TOPIC " (%s)

"; + +void HandleMqttConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MQTT); + + if (WebServer->hasArg("save")) { + MqttSaveSettings(); + WebRestart(1); + return; + } + + char str[TOPSZ]; + + WSContentStart_P(S_CONFIGURE_MQTT); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_MQTT1, + SettingsText(SET_MQTT_HOST), + Settings.mqtt_port, + Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, SettingsText(SET_MQTT_CLIENT)); + WSContentSend_P(HTTP_FORM_MQTT2, + (!strlen(SettingsText(SET_MQTT_USER))) ? "0" : SettingsText(SET_MQTT_USER), + Format(str, MQTT_TOPIC, sizeof(str)), MQTT_TOPIC, SettingsText(SET_MQTT_TOPIC), + MQTT_FULLTOPIC, MQTT_FULLTOPIC, SettingsText(SET_MQTT_FULLTOPIC)); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void MqttSaveSettings(void) +{ + char tmp[TOPSZ]; + char stemp[TOPSZ]; + char stemp2[TOPSZ]; + + WebGetArg("mt", tmp, sizeof(tmp)); + strlcpy(stemp, (!strlen(tmp)) ? MQTT_TOPIC : tmp, sizeof(stemp)); + MakeValidMqtt(0, stemp); + WebGetArg("mf", tmp, sizeof(tmp)); + strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2)); + MakeValidMqtt(1, stemp2); + if ((strcmp(stemp, SettingsText(SET_MQTT_TOPIC))) || (strcmp(stemp2, SettingsText(SET_MQTT_FULLTOPIC)))) { + Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); + MqttPublishPrefixTopic_P(TELE, S_LWT, true); + } + SettingsUpdateText(SET_MQTT_TOPIC, stemp); + SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp2); + WebGetArg("mh", tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_HOST, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp); + WebGetArg("ml", tmp, sizeof(tmp)); + Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp); + WebGetArg("mc", tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp); +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), + SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); +#else + WebGetArg("mu", tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp); + WebGetArg("mp", tmp, sizeof(tmp)); + SettingsUpdateText(SET_MQTT_PWD, (!strlen(tmp)) ? "" : (!strcmp(tmp, D_ASTERISK_PWD)) ? SettingsText(SET_MQTT_PWD) : tmp); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), + SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_USER), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); +#endif +} +#endif + + + + + +bool Xdrv02(uint8_t function) +{ + bool result = false; + + if (Settings.flag.mqtt_enabled) { + switch (function) { + case FUNC_PRE_INIT: + MqttInit(); + break; + case FUNC_EVERY_50_MSECOND: + MqttClient.loop(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_MQTT); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer->on("/" WEB_HANDLE_MQTT, HandleMqttConfiguration); + break; +#endif + case FUNC_COMMAND: + result = DecodeCommand(kMqttCommands, MqttCommand); + break; + } + } + return result; +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_03_energy.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_03_energy.ino" +#ifdef USE_ENERGY_SENSOR + + + + +#define XDRV_03 3 +#define XSNS_03 3 + + + + +#define ENERGY_NONE 0 +#define ENERGY_WATCHDOG 4 + +#include + +#define D_CMND_POWERCAL "PowerCal" +#define D_CMND_VOLTAGECAL "VoltageCal" +#define D_CMND_CURRENTCAL "CurrentCal" +#define D_CMND_TARIFF "Tariff" +#define D_CMND_MODULEADDRESS "ModuleAddress" + +enum EnergyCommands { + CMND_POWERCAL, CMND_VOLTAGECAL, CMND_CURRENTCAL, + CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_MODULEADDRESS }; + +const char kEnergyCommands[] PROGMEM = "|" + D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|" + D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" D_CMND_MODULEADDRESS "|" +#ifdef USE_ENERGY_MARGIN_DETECTION + D_CMND_POWERDELTA "|" D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" +#ifdef USE_ENERGY_POWER_LIMIT + D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|" + D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" + D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW "|" +#endif +#endif + D_CMND_ENERGYRESET "|" D_CMND_TARIFF ; + +void (* const EnergyCommand[])(void) PROGMEM = { + &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, + &CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, &CmndModuleAddress, +#ifdef USE_ENERGY_MARGIN_DETECTION + &CmndPowerDelta, &CmndPowerLow, &CmndPowerHigh, &CmndVoltageLow, &CmndVoltageHigh, &CmndCurrentLow, &CmndCurrentHigh, +#ifdef USE_ENERGY_POWER_LIMIT + &CmndMaxEnergy, &CmndMaxEnergyStart, + &CmndMaxPower, &CmndMaxPowerHold, &CmndMaxPowerWindow, + &CmndSafePower, &CmndSafePowerHold, &CmndSafePowerWindow, +#endif +#endif + &CmndEnergyReset, &CmndTariff }; + +const char kEnergyPhases[] PROGMEM = "|%s / %s|%s / %s / %s||[%s,%s]|[%s,%s,%s]"; + +struct ENERGY { + float voltage[3] = { 0, 0, 0 }; + float current[3] = { 0, 0, 0 }; + float active_power[3] = { 0, 0, 0 }; + float apparent_power[3] = { NAN, NAN, NAN }; + float reactive_power[3] = { NAN, NAN, NAN }; + float power_factor[3] = { NAN, NAN, NAN }; + float frequency[3] = { NAN, NAN, NAN }; + + float start_energy = 0; + float daily = 0; + float total = 0; + float export_active = NAN; + + unsigned long kWhtoday_delta = 0; + unsigned long kWhtoday_offset = 0; + unsigned long kWhtoday; + unsigned long period = 0; + + uint8_t fifth_second = 0; + uint8_t command_code = 0; + uint8_t data_valid[3] = { 0, 0, 0 }; + + uint8_t phase_count = 1; + bool voltage_common = false; + + bool voltage_available = true; + bool current_available = true; + + bool type_dc = false; + bool power_on = true; + +#ifdef USE_ENERGY_MARGIN_DETECTION + uint16_t power_history[3] = { 0 }; + uint8_t power_steady_counter = 8; + bool power_delta = false; + bool min_power_flag = false; + bool max_power_flag = false; + bool min_voltage_flag = false; + bool max_voltage_flag = false; + bool min_current_flag = false; + bool max_current_flag = false; + +#ifdef USE_ENERGY_POWER_LIMIT + uint16_t mplh_counter = 0; + uint16_t mplw_counter = 0; + uint8_t mplr_counter = 0; + uint8_t max_energy_state = 0; +#endif +#endif +} Energy; + +Ticker ticker_energy; + + + +bool EnergyTariff1Active() +{ + uint8_t dst = 0; + if (IsDst() && (Settings.tariff[0][1] != Settings.tariff[1][1])) { + dst = 1; + } + if (Settings.tariff[0][dst] != Settings.tariff[1][dst]) { + if (Settings.flag3.energy_weekend && ((RtcTime.day_of_week == 1) || + (RtcTime.day_of_week == 7))) { + return true; + } + uint32_t minutes = MinutesPastMidnight(); + if (Settings.tariff[0][dst] > Settings.tariff[1][dst]) { + + return ((minutes >= Settings.tariff[0][dst]) || (minutes < Settings.tariff[1][dst])); + } else { + + return ((minutes >= Settings.tariff[0][dst]) && (minutes < Settings.tariff[1][dst])); + } + } else { + return false; + } +} + +void EnergyUpdateToday(void) +{ + if (Energy.kWhtoday_delta > 1000) { + unsigned long delta = Energy.kWhtoday_delta / 1000; + Energy.kWhtoday_delta -= (delta * 1000); + Energy.kWhtoday += delta; + } + + RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset + Energy.kWhtoday; + Energy.daily = (float)(RtcSettings.energy_kWhtoday) / 100000; + Energy.total = (float)(RtcSettings.energy_kWhtotal + RtcSettings.energy_kWhtoday) / 100000; + + if (RtcTime.valid){ + + uint32_t energy_diff = (uint32_t)(Energy.total * 100000) - RtcSettings.energy_usage.last_usage_kWhtotal; + RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 100000); + + uint32_t return_diff = 0; + if (!isnan(Energy.export_active)) { + return_diff = (uint32_t)(Energy.export_active * 100000) - RtcSettings.energy_usage.last_return_kWhtotal; + RtcSettings.energy_usage.last_return_kWhtotal = (uint32_t)(Energy.export_active * 100000); + } + + if (EnergyTariff1Active()) { + RtcSettings.energy_usage.usage1_kWhtotal += energy_diff; + RtcSettings.energy_usage.return1_kWhtotal += return_diff; + } else { + RtcSettings.energy_usage.usage2_kWhtotal += energy_diff; + RtcSettings.energy_usage.return2_kWhtotal += return_diff; + } + } +} + +void EnergyUpdateTotal(float value, bool kwh) +{ + + + + + uint32_t multiplier = (kwh) ? 100000 : 100; + + if (0 == Energy.start_energy || (value < Energy.start_energy)) { + Energy.start_energy = value; + } + else if (value != Energy.start_energy) { + Energy.kWhtoday = (unsigned long)((value - Energy.start_energy) * multiplier); + } + + if ((Energy.total < (value - 0.01)) && + Settings.flag3.hardware_energy_total) { + RtcSettings.energy_kWhtotal = (unsigned long)((value * multiplier) - Energy.kWhtoday_offset - Energy.kWhtoday); + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; + Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); + + } + EnergyUpdateToday(); +} + + + +void Energy200ms(void) +{ + Energy.power_on = (power != 0) | Settings.flag.no_power_on_check; + + Energy.fifth_second++; + if (5 == Energy.fifth_second) { + Energy.fifth_second = 0; + + XnrgCall(FUNC_ENERGY_EVERY_SECOND); + + if (RtcTime.valid) { + if (LocalTime() == Midnight()) { + Settings.energy_kWhyesterday = RtcSettings.energy_kWhtoday; + + RtcSettings.energy_kWhtotal += RtcSettings.energy_kWhtoday; + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + Energy.kWhtoday = 0; + Energy.kWhtoday_offset = 0; + RtcSettings.energy_kWhtoday = 0; + Energy.start_energy = 0; + + Energy.kWhtoday_delta = 0; + Energy.period = Energy.kWhtoday; + EnergyUpdateToday(); +#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) + Energy.max_energy_state = 3; +#endif + } +#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) + if ((RtcTime.hour == Settings.energy_max_energy_start) && (3 == Energy.max_energy_state )) { + Energy.max_energy_state = 0; + } +#endif + + } + } + + XnrgCall(FUNC_EVERY_200_MSECOND); +} + +void EnergySaveState(void) +{ + Settings.energy_kWhdoy = (RtcTime.valid) ? RtcTime.day_of_year : 0; + + Settings.energy_kWhtoday = RtcSettings.energy_kWhtoday; + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + + Settings.energy_usage = RtcSettings.energy_usage; +} + +#ifdef USE_ENERGY_MARGIN_DETECTION +bool EnergyMargin(bool type, uint16_t margin, uint16_t value, bool &flag, bool &save_flag) +{ + bool change; + + if (!margin) return false; + change = save_flag; + if (type) { + flag = (value > margin); + } else { + flag = (value < margin); + } + save_flag = flag; + return (change != save_flag); +} + +void EnergyMarginCheck(void) +{ + if (Energy.power_steady_counter) { + Energy.power_steady_counter--; + return; + } + + uint16_t energy_power_u = (uint16_t)(Energy.active_power[0]); + + if (Settings.energy_power_delta) { + uint16_t delta = abs(Energy.power_history[0] - energy_power_u); + if (delta > 0) { + if (Settings.energy_power_delta < 101) { + uint16_t min_power = (Energy.power_history[0] > energy_power_u) ? energy_power_u : Energy.power_history[0]; + if (0 == min_power) { min_power++; } + if (((delta * 100) / min_power) > Settings.energy_power_delta) { + Energy.power_delta = true; + } + } else { + if (delta > (Settings.energy_power_delta -100)) { + Energy.power_delta = true; + } + } + if (Energy.power_delta) { + Energy.power_history[1] = Energy.active_power[0]; + Energy.power_history[2] = Energy.active_power[0]; + } + } + } + Energy.power_history[0] = Energy.power_history[1]; + Energy.power_history[1] = Energy.power_history[2]; + Energy.power_history[2] = energy_power_u; + + if (Energy.power_on && (Settings.energy_min_power || Settings.energy_max_power || Settings.energy_min_voltage || Settings.energy_max_voltage || Settings.energy_min_current || Settings.energy_max_current)) { + uint16_t energy_voltage_u = (uint16_t)(Energy.voltage[0]); + uint16_t energy_current_u = (uint16_t)(Energy.current[0] * 1000); + + DEBUG_DRIVER_LOG(PSTR("NRG: W %d, U %d, I %d"), energy_power_u, energy_voltage_u, energy_current_u); + + Response_P(PSTR("{")); + bool flag; + bool jsonflg = false; + if (EnergyMargin(false, Settings.energy_min_power, energy_power_u, flag, Energy.min_power_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_POWERLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(true, Settings.energy_max_power, energy_power_u, flag, Energy.max_power_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_POWERHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(false, Settings.energy_min_voltage, energy_voltage_u, flag, Energy.min_voltage_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_VOLTAGELOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(true, Settings.energy_max_voltage, energy_voltage_u, flag, Energy.max_voltage_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_VOLTAGEHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(false, Settings.energy_min_current, energy_current_u, flag, Energy.min_current_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_CURRENTLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (EnergyMargin(true, Settings.energy_max_current, energy_current_u, flag, Energy.max_current_flag)) { + ResponseAppend_P(PSTR("%s\"" D_CMND_CURRENTHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); + jsonflg = true; + } + if (jsonflg) { + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_MARGINS), MQTT_TELE_RETAIN); + EnergyMqttShow(); + } + } + +#ifdef USE_ENERGY_POWER_LIMIT + + if (Settings.energy_max_power_limit) { + if (Energy.active_power[0] > Settings.energy_max_power_limit) { + if (!Energy.mplh_counter) { + Energy.mplh_counter = Settings.energy_max_power_limit_hold; + } else { + Energy.mplh_counter--; + if (!Energy.mplh_counter) { + ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHED "\":%d}"), energy_power_u); + MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); + EnergyMqttShow(); + SetAllPower(POWER_ALL_OFF, SRC_MAXPOWER); + if (!Energy.mplr_counter) { + Energy.mplr_counter = Settings.param[P_MAX_POWER_RETRY] +1; + } + Energy.mplw_counter = Settings.energy_max_power_limit_window; + } + } + } + else if (power && (energy_power_u <= Settings.energy_max_power_limit)) { + Energy.mplh_counter = 0; + Energy.mplr_counter = 0; + Energy.mplw_counter = 0; + } + if (!power) { + if (Energy.mplw_counter) { + Energy.mplw_counter--; + } else { + if (Energy.mplr_counter) { + Energy.mplr_counter--; + if (Energy.mplr_counter) { + ResponseTime_P(PSTR(",\"" D_JSON_POWERMONITOR "\":\"%s\"}"), GetStateText(1)); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_POWERMONITOR)); + RestorePower(true, SRC_MAXPOWER); + } else { + ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHEDRETRY "\":\"%s\"}"), GetStateText(0)); + MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); + EnergyMqttShow(); + SetAllPower(POWER_ALL_OFF, SRC_MAXPOWER); + } + } + } + } + } + + + if (Settings.energy_max_energy) { + uint16_t energy_daily_u = (uint16_t)(Energy.daily * 1000); + if (!Energy.max_energy_state && (RtcTime.hour == Settings.energy_max_energy_start)) { + Energy.max_energy_state = 1; + ResponseTime_P(PSTR(",\"" D_JSON_ENERGYMONITOR "\":\"%s\"}"), GetStateText(1)); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_ENERGYMONITOR)); + RestorePower(true, SRC_MAXENERGY); + } + else if ((1 == Energy.max_energy_state ) && (energy_daily_u >= Settings.energy_max_energy)) { + Energy.max_energy_state = 2; + char stemp[FLOATSZ]; + dtostrfd(Energy.daily, 3, stemp); + ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":%s}"), stemp); + MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); + EnergyMqttShow(); + SetAllPower(POWER_ALL_OFF, SRC_MAXENERGY); + } + } +#endif + + if (Energy.power_delta) { EnergyMqttShow(); } +} + +void EnergyMqttShow(void) +{ + + int tele_period_save = tele_period; + tele_period = 2; + mqtt_data[0] = '\0'; + ResponseAppendTime(); + EnergyShow(true); + tele_period = tele_period_save; + ResponseJsonEnd(); + MqttPublishTeleSensor(); + Energy.power_delta = false; +} +#endif + +void EnergyEverySecond(void) +{ + + if (global_update) { + if (power && (global_temperature != 9999) && (global_temperature > Settings.param[P_OVER_TEMP])) { + SetAllPower(POWER_ALL_OFF, SRC_OVERTEMP); + } + } + + + uint32_t data_valid = Energy.phase_count; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + if (Energy.data_valid[i] <= ENERGY_WATCHDOG) { + Energy.data_valid[i]++; + if (Energy.data_valid[i] > ENERGY_WATCHDOG) { + + Energy.voltage[i] = 0; + Energy.current[i] = 0; + Energy.active_power[i] = 0; + if (!isnan(Energy.apparent_power[i])) { Energy.apparent_power[i] = 0; } + if (!isnan(Energy.reactive_power[i])) { Energy.reactive_power[i] = 0; } + if (!isnan(Energy.frequency[i])) { Energy.frequency[i] = 0; } + if (!isnan(Energy.power_factor[i])) { Energy.power_factor[i] = 0; } + + data_valid--; + } + } + } + if (!data_valid) { + if (!isnan(Energy.export_active)) { Energy.export_active = 0; } + Energy.start_energy = 0; + + XnrgCall(FUNC_ENERGY_RESET); + } + +#ifdef USE_ENERGY_MARGIN_DETECTION + EnergyMarginCheck(); +#endif +} + + + + + +void EnergyCommandCalResponse(uint32_t nvalue) +{ + snprintf_P(XdrvMailbox.command, CMDSZ, PSTR("%sCal"), XdrvMailbox.command); + ResponseCmndNumber(nvalue); +} + +void CmndEnergyReset(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { + char *p; + unsigned long lnum = strtoul(XdrvMailbox.data, &p, 10); + if (p != XdrvMailbox.data) { + switch (XdrvMailbox.index) { + case 1: + + Energy.kWhtoday_offset = lnum *100; + Energy.kWhtoday = 0; + Energy.kWhtoday_delta = 0; + Energy.start_energy = 0; + Energy.period = Energy.kWhtoday_offset; + Settings.energy_kWhtoday = Energy.kWhtoday_offset; + RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset; + Energy.daily = (float)Energy.kWhtoday_offset / 100000; + if (!RtcSettings.energy_kWhtotal && !Energy.kWhtoday_offset) { + Settings.energy_kWhtotal_time = LocalTime(); + } + break; + case 2: + + Settings.energy_kWhyesterday = lnum *100; + break; + case 3: + + RtcSettings.energy_kWhtotal = lnum *100; + Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; + + Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); + RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 1000); + break; + } + } + } + else if ((XdrvMailbox.index > 3) && (XdrvMailbox.index <= 5)) { + uint32_t values[2] = { 0 }; + uint32_t position = ParseParameters(2, values); + values[0] *= 100; + values[1] *= 100; + + switch (XdrvMailbox.index) + { + case 4: + + if (position > 0) { + RtcSettings.energy_usage.usage1_kWhtotal = values[0]; + } + if (position > 1) { + RtcSettings.energy_usage.usage2_kWhtotal = values[1]; + } + Settings.energy_usage.usage1_kWhtotal = RtcSettings.energy_usage.usage1_kWhtotal; + Settings.energy_usage.usage2_kWhtotal = RtcSettings.energy_usage.usage2_kWhtotal; + break; + case 5: + + if (position > 0) { + RtcSettings.energy_usage.return1_kWhtotal = values[0]; + } + if (position > 1) { + RtcSettings.energy_usage.return2_kWhtotal = values[1]; + } + Settings.energy_usage.return1_kWhtotal = RtcSettings.energy_usage.return1_kWhtotal; + Settings.energy_usage.return2_kWhtotal = RtcSettings.energy_usage.return2_kWhtotal; + break; + } + } + + Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; + + char energy_total_chr[FLOATSZ]; + dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr); + char energy_daily_chr[FLOATSZ]; + dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); + char energy_yesterday_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); + + char energy_usage1_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage1_chr); + char energy_usage2_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage2_chr); + char energy_return1_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return1_chr); + char energy_return2_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return2_chr); + + Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s,\"" D_JSON_USAGE "\":[%s,%s],\"" D_JSON_EXPORT "\":[%s,%s]}}"), + XdrvMailbox.command, energy_total_chr, energy_yesterday_chr, energy_daily_chr, energy_usage1_chr, energy_usage2_chr, energy_return1_chr, energy_return2_chr); +} + +void CmndTariff(void) +{ + + + + + + + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + uint32_t tariff = XdrvMailbox.index -1; + uint32_t time_type = 0; + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + while ((str != nullptr) && (time_type < 2)) { + char *q; + uint32_t value = strtol(str, &q, 10); + Settings.tariff[tariff][time_type] = value; + if (value < 24) { + Settings.tariff[tariff][time_type] *= 60; + char *minute = strtok_r(nullptr, ":", &q); + if (minute) { + value = strtol(minute, nullptr, 10); + if (value > 59) { + value = 59; + } + Settings.tariff[tariff][time_type] += value; + } + } + if (Settings.tariff[tariff][time_type] > 1439) { + Settings.tariff[tariff][time_type] = 1439; + } + str = strtok_r(nullptr, ", ", &p); + time_type++; + } + } + else if (XdrvMailbox.index == 9) { + Settings.flag3.energy_weekend = XdrvMailbox.payload & 1; + } + Response_P(PSTR("{\"%s\":{\"Off-Peak\":{\"STD\":\"%s\",\"DST\":\"%s\"},\"Standard\":{\"STD\":\"%s\",\"DST\":\"%s\"},\"Weekend\":\"%s\"}}"), + XdrvMailbox.command, + GetMinuteTime(Settings.tariff[0][0]).c_str(),GetMinuteTime(Settings.tariff[0][1]).c_str(), + GetMinuteTime(Settings.tariff[1][0]).c_str(),GetMinuteTime(Settings.tariff[1][1]).c_str(), + GetStateText(Settings.flag3.energy_weekend)); +} + +void CmndPowerCal(void) +{ + Energy.command_code = CMND_POWERCAL; + if (XnrgCall(FUNC_COMMAND)) { + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { + Settings.energy_power_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_power_calibration); + } +} + +void CmndVoltageCal(void) +{ + Energy.command_code = CMND_VOLTAGECAL; + if (XnrgCall(FUNC_COMMAND)) { + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { + Settings.energy_voltage_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_voltage_calibration); + } +} + +void CmndCurrentCal(void) +{ + Energy.command_code = CMND_CURRENTCAL; + if (XnrgCall(FUNC_COMMAND)) { + if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { + Settings.energy_current_calibration = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_current_calibration); + } +} + +void CmndPowerSet(void) +{ + Energy.command_code = CMND_POWERSET; + if (XnrgCall(FUNC_COMMAND)) { + EnergyCommandCalResponse(Settings.energy_power_calibration); + } +} + +void CmndVoltageSet(void) +{ + Energy.command_code = CMND_VOLTAGESET; + if (XnrgCall(FUNC_COMMAND)) { + EnergyCommandCalResponse(Settings.energy_voltage_calibration); + } +} + +void CmndCurrentSet(void) +{ + Energy.command_code = CMND_CURRENTSET; + if (XnrgCall(FUNC_COMMAND)) { + EnergyCommandCalResponse(Settings.energy_current_calibration); + } +} + +void CmndFrequencySet(void) +{ + Energy.command_code = CMND_FREQUENCYSET; + if (XnrgCall(FUNC_COMMAND)) { + EnergyCommandCalResponse(Settings.energy_frequency_calibration); + } +} + +void CmndModuleAddress(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4) && (1 == Energy.phase_count)) { + Energy.command_code = CMND_MODULEADDRESS; + if (XnrgCall(FUNC_COMMAND)) { + ResponseCmndDone(); + } + } +} + +#ifdef USE_ENERGY_MARGIN_DETECTION +void CmndPowerDelta(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32000)) { + Settings.energy_power_delta = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_power_delta); +} + +void CmndPowerLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_min_power = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_min_power); +} + +void CmndPowerHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power); +} + +void CmndVoltageLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { + Settings.energy_min_voltage = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_min_voltage); +} + +void CmndVoltageHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { + Settings.energy_max_voltage = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_voltage); +} + +void CmndCurrentLow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { + Settings.energy_min_current = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_min_current); +} + +void CmndCurrentHigh(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { + Settings.energy_max_current = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_current); +} + +#ifdef USE_ENERGY_POWER_LIMIT +void CmndMaxPower(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_limit = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_limit); +} + +void CmndMaxPowerHold(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_limit_hold = (1 == XdrvMailbox.payload) ? MAX_POWER_HOLD : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_limit_hold); +} + +void CmndMaxPowerWindow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_limit_window = (1 == XdrvMailbox.payload) ? MAX_POWER_WINDOW : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_limit_window); +} + +void CmndSafePower(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_safe_limit = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_safe_limit); +} + +void CmndSafePowerHold(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_power_safe_limit_hold = (1 == XdrvMailbox.payload) ? SAFE_POWER_HOLD : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_safe_limit_hold); +} + +void CmndSafePowerWindow(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 1440)) { + Settings.energy_max_power_safe_limit_window = (1 == XdrvMailbox.payload) ? SAFE_POWER_WINDOW : XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_power_safe_limit_window); +} + +void CmndMaxEnergy(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.energy_max_energy = XdrvMailbox.payload; + Energy.max_energy_state = 3; + } + ResponseCmndNumber(Settings.energy_max_energy); +} + +void CmndMaxEnergyStart(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 24)) { + Settings.energy_max_energy_start = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.energy_max_energy_start); +} +#endif +#endif + +void EnergySnsInit(void) +{ + XnrgCall(FUNC_INIT); + + if (energy_flg) { + if (RtcSettingsValid()) { + Energy.kWhtoday_offset = RtcSettings.energy_kWhtoday; + } + else if (RtcTime.day_of_year == Settings.energy_kWhdoy) { + Energy.kWhtoday_offset = Settings.energy_kWhtoday; + } + else { + Energy.kWhtoday_offset = 0; + } + Energy.kWhtoday = 0; + Energy.kWhtoday_delta = 0; + Energy.period = Energy.kWhtoday_offset; + EnergyUpdateToday(); + ticker_energy.attach_ms(200, Energy200ms); + } +} + +#ifdef USE_WEBSERVER +const char HTTP_ENERGY_SNS1[] PROGMEM = + "{s}" D_POWERUSAGE_APPARENT "{m}%s " D_UNIT_VA "{e}" + "{s}" D_POWERUSAGE_REACTIVE "{m}%s " D_UNIT_VAR "{e}" + "{s}" D_POWER_FACTOR "{m}%s{e}"; + +const char HTTP_ENERGY_SNS2[] PROGMEM = + "{s}" D_ENERGY_TODAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" + "{s}" D_ENERGY_YESTERDAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" + "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; + +const char HTTP_ENERGY_SNS3[] PROGMEM = + "{s}" D_EXPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; +#endif + +char* EnergyFormatIndex(char* result, char* input, bool json, uint32_t index, bool single = false) +{ + char layout[16]; + GetTextIndexed(layout, sizeof(layout), (index -1) + (3 * json), kEnergyPhases); + switch (index) { + case 2: + snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ); + break; + case 3: + snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ, input + FLOATSZ + FLOATSZ); + break; + default: + snprintf_P(result, FLOATSZ *3, input); + } + return result; +} + +char* EnergyFormat(char* result, char* input, bool json, bool single = false) +{ + uint8_t index = (single) ? 1 : Energy.phase_count; + return EnergyFormatIndex(result, input, json, index, single); +} + +void EnergyShow(bool json) +{ + for (uint32_t i = 0; i < Energy.phase_count; i++) { + if (Energy.voltage_common) { + Energy.voltage[i] = Energy.voltage[0]; + } + } + + float power_factor_knx = Energy.power_factor[0]; + + char apparent_power_chr[Energy.phase_count][FLOATSZ]; + char reactive_power_chr[Energy.phase_count][FLOATSZ]; + char power_factor_chr[Energy.phase_count][FLOATSZ]; + char frequency_chr[Energy.phase_count][FLOATSZ]; + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { + for (uint32_t i = 0; i < Energy.phase_count; i++) { + float apparent_power = Energy.apparent_power[i]; + if (isnan(apparent_power)) { + apparent_power = Energy.voltage[i] * Energy.current[i]; + } + if (apparent_power < Energy.active_power[i]) { + Energy.active_power[i] = apparent_power; + } + + float power_factor = Energy.power_factor[i]; + if (isnan(power_factor)) { + power_factor = (Energy.active_power[i] && apparent_power) ? Energy.active_power[i] / apparent_power : 0; + if (power_factor > 1) { + power_factor = 1; + } + } + if (0 == i) { power_factor_knx = power_factor; } + + float reactive_power = Energy.reactive_power[i]; + if (isnan(reactive_power)) { + reactive_power = 0; + uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(Energy.active_power[i] * 100)) / 10; + if ((Energy.current[i] > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) { + + + reactive_power = (float)(RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(Energy.active_power[i] * Energy.active_power[i] * 100))) / 10; + } + } + + dtostrfd(apparent_power, Settings.flag2.wattage_resolution, apparent_power_chr[i]); + dtostrfd(reactive_power, Settings.flag2.wattage_resolution, reactive_power_chr[i]); + dtostrfd(power_factor, 2, power_factor_chr[i]); + } + } + for (uint32_t i = 0; i < Energy.phase_count; i++) { + float frequency = Energy.frequency[i]; + if (isnan(Energy.frequency[i])) { + frequency = 0; + } + dtostrfd(frequency, Settings.flag2.frequency_resolution, frequency_chr[i]); + } + } + + char voltage_chr[Energy.phase_count][FLOATSZ]; + char current_chr[Energy.phase_count][FLOATSZ]; + char active_power_chr[Energy.phase_count][FLOATSZ]; + for (uint32_t i = 0; i < Energy.phase_count; i++) { + dtostrfd(Energy.voltage[i], Settings.flag2.voltage_resolution, voltage_chr[i]); + dtostrfd(Energy.current[i], Settings.flag2.current_resolution, current_chr[i]); + dtostrfd(Energy.active_power[i], Settings.flag2.wattage_resolution, active_power_chr[i]); + } + + char energy_daily_chr[FLOATSZ]; + dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); + char energy_yesterday_chr[FLOATSZ]; + dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); + + char energy_total_chr[3][FLOATSZ]; + dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr[0]); + char export_active_chr[3][FLOATSZ]; + dtostrfd(Energy.export_active, Settings.flag2.energy_resolution, export_active_chr[0]); + uint8_t energy_total_fields = 1; + + if (Settings.tariff[0][0] != Settings.tariff[1][0]) { + dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_total_chr[1]); + dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_total_chr[2]); + dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, export_active_chr[1]); + dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, export_active_chr[2]); + energy_total_fields = 3; + } + + char value_chr[FLOATSZ *3]; + char value2_chr[FLOATSZ *3]; + char value3_chr[FLOATSZ *3]; + + if (json) { + bool show_energy_period = (0 == tele_period); + + ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL_START_TIME "\":\"%s\",\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s"), + GetDateAndTime(DT_ENERGY).c_str(), + EnergyFormatIndex(value_chr, energy_total_chr[0], json, energy_total_fields), + energy_yesterday_chr, + energy_daily_chr); + + if (!isnan(Energy.export_active)) { + ResponseAppend_P(PSTR(",\"" D_JSON_EXPORT_ACTIVE "\":%s"), + EnergyFormatIndex(value_chr, export_active_chr[0], json, energy_total_fields)); + } + + if (show_energy_period) { + float energy = 0; + if (Energy.period) { + energy = (float)(RtcSettings.energy_kWhtoday - Energy.period) / 100; + } + Energy.period = RtcSettings.energy_kWhtoday; + char energy_period_chr[FLOATSZ]; + dtostrfd(energy, Settings.flag2.wattage_resolution, energy_period_chr); + ResponseAppend_P(PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr); + } + ResponseAppend_P(PSTR(",\"" D_JSON_POWERUSAGE "\":%s"), + EnergyFormat(value_chr, active_power_chr[0], json)); + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { + ResponseAppend_P(PSTR(",\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_POWERFACTOR "\":%s"), + EnergyFormat(value_chr, apparent_power_chr[0], json), + EnergyFormat(value2_chr, reactive_power_chr[0], json), + EnergyFormat(value3_chr, power_factor_chr[0], json)); + } + if (!isnan(Energy.frequency[0])) { + ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"), + EnergyFormat(value_chr, frequency_chr[0], json, Energy.voltage_common)); + } + } + if (Energy.voltage_available) { + ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s"), + EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); + } + if (Energy.current_available) { + ResponseAppend_P(PSTR(",\"" D_JSON_CURRENT "\":%s"), + EnergyFormat(value_chr, current_chr[0], json)); + } + XnrgCall(FUNC_JSON_APPEND); + ResponseJsonEnd(); + +#ifdef USE_DOMOTICZ + if (show_energy_period) { + dtostrfd(Energy.total * 1000, 1, energy_total_chr[0]); + DomoticzSensorPowerEnergy((int)Energy.active_power[0], energy_total_chr[0]); + + dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100, 1, energy_total_chr[1]); + dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100, 1, energy_total_chr[2]); + dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100, 1, export_active_chr[1]); + dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100, 1, export_active_chr[2]); + DomoticzSensorP1SmartMeter(energy_total_chr[1], energy_total_chr[2], export_active_chr[1], export_active_chr[2], (int)Energy.active_power[0]); + + if (Energy.voltage_available) { + DomoticzSensor(DZ_VOLTAGE, voltage_chr[0]); + } + if (Energy.current_available) { + DomoticzSensor(DZ_CURRENT, current_chr[0]); + } + } +#endif +#ifdef USE_KNX + if (show_energy_period) { + if (Energy.voltage_available) { + KnxSensor(KNX_ENERGY_VOLTAGE, Energy.voltage[0]); + } + if (Energy.current_available) { + KnxSensor(KNX_ENERGY_CURRENT, Energy.current[0]); + } + KnxSensor(KNX_ENERGY_POWER, Energy.active_power[0]); + if (!Energy.type_dc) { + KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor_knx); + } + KnxSensor(KNX_ENERGY_DAILY, Energy.daily); + KnxSensor(KNX_ENERGY_TOTAL, Energy.total); + KnxSensor(KNX_ENERGY_START, Energy.start_energy); + } +#endif +#ifdef USE_WEBSERVER + } else { + if (Energy.voltage_available) { + WSContentSend_PD(HTTP_SNS_VOLTAGE, EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); + } + if (Energy.current_available) { + WSContentSend_PD(HTTP_SNS_CURRENT, EnergyFormat(value_chr, current_chr[0], json)); + } + WSContentSend_PD(HTTP_SNS_POWER, EnergyFormat(value_chr, active_power_chr[0], json)); + if (!Energy.type_dc) { + if (Energy.current_available && Energy.voltage_available) { + WSContentSend_PD(HTTP_ENERGY_SNS1, EnergyFormat(value_chr, apparent_power_chr[0], json), + EnergyFormat(value2_chr, reactive_power_chr[0], json), + EnergyFormat(value3_chr, power_factor_chr[0], json)); + } + if (!isnan(Energy.frequency[0])) { + WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"), + EnergyFormat(value_chr, frequency_chr[0], json, Energy.voltage_common)); + } + } + WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr[0]); + if (!isnan(Energy.export_active)) { + WSContentSend_PD(HTTP_ENERGY_SNS3, export_active_chr[0]); + } + + XnrgCall(FUNC_WEB_SENSOR); +#endif + } +} + + + + + +bool Xdrv03(uint8_t function) +{ + bool result = false; + + if (FUNC_PRE_INIT == function) { + energy_flg = ENERGY_NONE; + XnrgCall(FUNC_PRE_INIT); + } + else if (energy_flg) { + switch (function) { + case FUNC_LOOP: + XnrgCall(FUNC_LOOP); + break; + case FUNC_EVERY_250_MSECOND: + XnrgCall(FUNC_EVERY_250_MSECOND); + break; + case FUNC_SERIAL: + result = XnrgCall(FUNC_SERIAL); + break; +#ifdef USE_ENERGY_MARGIN_DETECTION + case FUNC_SET_POWER: + Energy.power_steady_counter = 2; + break; +#endif + case FUNC_COMMAND: + result = DecodeCommand(kEnergyCommands, EnergyCommand); + break; + } + } + return result; +} + +bool Xsns03(uint8_t function) +{ + bool result = false; + + if (energy_flg) { + switch (function) { + case FUNC_EVERY_SECOND: + EnergyEverySecond(); + break; + case FUNC_JSON_APPEND: + EnergyShow(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + EnergyShow(false); + break; +#endif + case FUNC_SAVE_BEFORE_RESTART: + EnergySaveState(); + break; + case FUNC_INIT: + EnergySnsInit(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" +#ifdef USE_LIGHT +# 124 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" +#define XDRV_04 4 + + +enum LightSchemes { LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX }; + +const uint8_t LIGHT_COLOR_SIZE = 25; + +const char kLightCommands[] PROGMEM = "|" + D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_DIMMER_RANGE "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" + D_CMND_RGBWWTABLE "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" + D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR "|UNDOCA" ; + +void (* const LightCommand[])(void) PROGMEM = { + &CmndColor, &CmndColorTemperature, &CmndDimmer, &CmndDimmerRange, &CmndLedTable, &CmndFade, + &CmndRgbwwTable, &CmndScheme, &CmndSpeed, &CmndWakeup, &CmndWakeupDuration, + &CmndWhite, &CmndChannel, &CmndHsbColor, &CmndUndocA }; + + +enum LightColorModes { + LCM_RGB = 1, LCM_CT = 2, LCM_BOTH = 3 }; + +struct LRgbColor { + uint8_t R, G, B; +}; +const uint8_t MAX_FIXED_COLOR = 12; +const LRgbColor kFixedColor[MAX_FIXED_COLOR] PROGMEM = + { 255,0,0, 0,255,0, 0,0,255, 228,32,0, 0,228,32, 0,32,228, 188,64,0, 0,160,96, 160,32,240, 255,255,0, 255,0,170, 255,255,255 }; + +struct LWColor { + uint8_t W; +}; +const uint8_t MAX_FIXED_WHITE = 4; +const LWColor kFixedWhite[MAX_FIXED_WHITE] PROGMEM = { 0, 255, 128, 32 }; + +struct LCwColor { + uint8_t C, W; +}; +const uint8_t MAX_FIXED_COLD_WARM = 4; +const LCwColor kFixedColdWarm[MAX_FIXED_COLD_WARM] PROGMEM = { 0,0, 255,0, 0,255, 128,128 }; + + +const uint16_t CT_MIN = 153; +const uint16_t CT_MAX = 500; + +const uint16_t CT_MIN_ALEXA = 200; +const uint16_t CT_MAX_ALEXA = 380; + + + + + + +typedef struct gamma_table_t { + uint16_t to_src; + uint16_t to_gamma; +} gamma_table_t; + +const gamma_table_t gamma_table[] = { + { 1, 1 }, + { 4, 1 }, + { 209, 13 }, + { 312, 41 }, + { 457, 106 }, + { 626, 261 }, + { 762, 450 }, + { 895, 703 }, + { 1023, 1023 }, + { 0xFFFF, 0xFFFF } +}; + + +const gamma_table_t gamma_table_fast[] = { + { 384, 192 }, + { 768, 576 }, + { 1023, 1023 }, + { 0xFFFF, 0xFFFF } +}; +# 248 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" +struct LIGHT { + uint32_t strip_timer_counter = 0; + power_t power = 0; + + uint16_t wakeup_counter = 0; + + uint8_t entry_color[LST_MAX]; + uint8_t current_color[LST_MAX]; + uint8_t new_color[LST_MAX]; + uint8_t last_color[LST_MAX]; + uint8_t color_remap[LST_MAX]; + + uint8_t wheel = 0; + uint8_t random = 0; + uint8_t subtype = 0; + uint8_t device = 0; + uint8_t old_power = 1; + uint8_t wakeup_active = 0; + uint8_t wakeup_dimmer = 0; + uint8_t fixed_color_index = 1; + uint8_t pwm_offset = 0; + uint8_t max_scheme = LS_MAX -1; + + bool update = true; + bool pwm_multi_channels = false; + + bool fade_initialized = false; + bool fade_running = false; + uint16_t fade_start_10[LST_MAX] = {0,0,0,0,0}; + uint16_t fade_cur_10[LST_MAX]; + uint16_t fade_end_10[LST_MAX]; + uint16_t fade_duration = 0; + uint32_t fade_start = 0; +} Light; + +power_t LightPower(void) +{ + return Light.power; +} + + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 +power_t LightPowerIRAM(void) ICACHE_RAM_ATTR; +#endif + +power_t LightPowerIRAM(void) +{ + return Light.power; +} + +uint8_t LightDevice(void) +{ + return Light.device; +} + +static uint32_t min3(uint32_t a, uint32_t b, uint32_t c) { + return (a < b && a < c) ? a : (b < c) ? b : c; +} +# 344 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" +class LightStateClass { + private: + uint16_t _hue = 0; + uint8_t _sat = 255; + uint8_t _briRGB = 255; + + uint8_t _r = 255; + uint8_t _g = 255; + uint8_t _b = 255; + + uint8_t _subtype = 0; + uint16_t _ct = CT_MIN; + uint8_t _wc = 255; + uint8_t _ww = 0; + uint8_t _briCT = 255; + + uint8_t _color_mode = LCM_RGB; + + + + + + uint16_t _ct_min_range = CT_MIN; + uint16_t _ct_max_range = CT_MAX; + + public: + LightStateClass() { + + } + + void setSubType(uint8_t sub_type) { + _subtype = sub_type; + } +# 386 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" + uint8_t setColorMode(uint8_t cm) { + uint8_t prev_cm = _color_mode; + if (cm < LCM_RGB) { cm = LCM_RGB; } + if (cm > LCM_BOTH) { cm = LCM_BOTH; } + uint8_t maxbri = (_briRGB >= _briCT) ? _briRGB : _briCT; + + switch (_subtype) { + case LST_COLDWARM: + _color_mode = LCM_CT; + break; + + case LST_NONE: + case LST_SINGLE: + case LST_RGB: + default: + _color_mode = LCM_RGB; + break; + + case LST_RGBW: + case LST_RGBCW: + _color_mode = cm; + break; + } + if (LCM_RGB == _color_mode) { + _briCT = 0; + if (0 == _briRGB) { _briRGB = maxbri; } + } + if (LCM_CT == _color_mode) { + _briRGB = 0; + if (0 == _briCT) { _briCT = maxbri; } + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setColorMode prev_cm (%d) req_cm (%d) new_cm (%d)", prev_cm, cm, _color_mode); +#endif + return prev_cm; + } + + inline uint8_t getColorMode() { + return _color_mode; + } + + void addRGBMode() { + setColorMode(_color_mode | LCM_RGB); + } + void addCTMode() { + setColorMode(_color_mode | LCM_CT); + } + + + void getRGB(uint8_t *r, uint8_t *g, uint8_t *b) { + if (r) { *r = _r; } + if (g) { *g = _g; } + if (b) { *b = _b; } + } + + + + void getCW(uint8_t *rc, uint8_t *rw) { + if (rc) { *rc = _wc; } + if (rw) { *rw = _ww; } + } + + + void getActualRGBCW(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *c, uint8_t *w) { + bool rgb_channels_on = _color_mode & LCM_RGB; + bool ct_channels_on = _color_mode & LCM_CT; + + if (r) { *r = rgb_channels_on ? changeUIntScale(_r, 0, 255, 0, _briRGB) : 0; } + if (g) { *g = rgb_channels_on ? changeUIntScale(_g, 0, 255, 0, _briRGB) : 0; } + if (b) { *b = rgb_channels_on ? changeUIntScale(_b, 0, 255, 0, _briRGB) : 0; } + + if (c) { *c = ct_channels_on ? changeUIntScale(_wc, 0, 255, 0, _briCT) : 0; } + if (w) { *w = ct_channels_on ? changeUIntScale(_ww, 0, 255, 0, _briCT) : 0; } + } + + uint8_t getChannels(uint8_t *channels) { + getActualRGBCW(&channels[0], &channels[1], &channels[2], &channels[3], &channels[4]); + } + + void getChannelsRaw(uint8_t *channels) { + channels[0] = _r; + channels[1] = _g; + channels[2] = _b; + channels[3] = _wc; + channels[4] = _ww; + } + + void getHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) { + if (hue) { *hue = _hue; } + if (sat) { *sat = _sat; } + if (bri) { *bri = _briRGB; } + } + + + uint8_t getBri(void) { + + return (_briRGB >= _briCT) ? _briRGB : _briCT; + } + + + inline uint8_t getBriCT() { + return _briCT; + } + + static inline uint8_t DimmerToBri(uint8_t dimmer) { + return changeUIntScale(dimmer, 0, 100, 0, 255); + } + static uint8_t BriToDimmer(uint8_t bri) { + uint8_t dimmer = changeUIntScale(bri, 0, 255, 0, 100); + + if ((dimmer == 0) && (bri > 0)) { dimmer = 1; } + return dimmer; + } + + uint8_t getDimmer(uint32_t mode = 0) { + uint8_t bri; + switch (mode) { + case 1: + bri = getBriRGB(); + break; + case 2: + bri = getBriCT(); + break; + default: + bri = getBri(); + break; + } + return BriToDimmer(bri); + } + + inline uint16_t getCT() const { + return _ct; + } + + + uint16_t getCT10bits() const { + return changeUIntScale(_ct, _ct_min_range, _ct_max_range, 0, 1023); + } + + inline void setCTRange(uint16_t ct_min_range, uint16_t ct_max_range) { + _ct_min_range = ct_min_range; + _ct_max_range = ct_max_range; + } + + inline void getCTRange(uint16_t *ct_min_range, uint16_t *ct_max_range) const { + if (ct_min_range) { *ct_min_range = _ct_min_range; } + if (ct_max_range) { *ct_max_range = _ct_max_range; } + } + + + void getXY(float *x, float *y) { + RgbToXy(_r, _g, _b, x, y); + } + + + + void setBri(uint8_t bri) { + setBriRGB(_color_mode & LCM_RGB ? bri : 0); + setBriCT(_color_mode & LCM_CT ? bri : 0); +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setBri RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); +#endif + } + + + uint8_t setBriRGB(uint8_t bri_rgb) { + uint8_t prev_bri = _briRGB; + _briRGB = bri_rgb; + if (bri_rgb > 0) { addRGBMode(); } + return prev_bri; + } + + + uint8_t setBriCT(uint8_t bri_ct) { + uint8_t prev_bri = _briCT; + _briCT = bri_ct; + if (bri_ct > 0) { addCTMode(); } + return prev_bri; + } + + inline uint8_t getBriRGB() { + return _briRGB; + } + + void setDimmer(uint8_t dimmer) { + setBri(DimmerToBri(dimmer)); + } + + void setCT(uint16_t ct) { + if (0 == ct) { + + setColorMode(LCM_RGB); + } else { + ct = (ct < CT_MIN ? CT_MIN : (ct > CT_MAX ? CT_MAX : ct)); + _ww = changeUIntScale(ct, _ct_min_range, _ct_max_range, 0, 255); + _wc = 255 - _ww; + _ct = ct; + addCTMode(); + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCT RGB raw (%d %d %d) HS (%d %d) briRGB (%d) briCT (%d) CT (%d)", _r, _g, _b, _hue, _sat, _briRGB, _briCT, _ct); +#endif + } +# 604 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" + void setCW(uint8_t c, uint8_t w, bool free_range = false) { + uint16_t max = (w > c) ? w : c; + uint16_t sum = c + w; + + if (0 == max) { + _briCT = 0; + setColorMode(LCM_RGB); + } else { + if (!free_range) { + + _ww = changeUIntScale(w, 0, sum, 0, 255); + _wc = 255 - _ww; + } else { + _ww = changeUIntScale(w, 0, max, 0, 255); + _wc = changeUIntScale(c, 0, max, 0, 255); + } + _ct = changeUIntScale(w, 0, sum, _ct_min_range, _ct_max_range); + addCTMode(); + if (_color_mode & LCM_CT) { _briCT = free_range ? max : (sum > 255 ? 255 : sum); } + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCW CW (%d %d) CT (%d) briCT (%d)", c, w, _ct, _briCT); +#endif + } + + + uint8_t setRGB(uint8_t r, uint8_t g, uint8_t b, bool keep_bri = false) { + uint16_t hue; + uint8_t sat; +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB input (%d %d %d)", r, g, b); +#endif + + uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; + + if (0 == max) { + r = g = b = 255; + setColorMode(LCM_CT); + } else { + if (255 > max) { + + r = changeUIntScale(r, 0, max, 0, 255); + g = changeUIntScale(g, 0, max, 0, 255); + b = changeUIntScale(b, 0, max, 0, 255); + } + addRGBMode(); + } + if (!keep_bri) { + _briRGB = (_color_mode & LCM_RGB) ? max : 0; + } + + RgbToHsb(r, g, b, &hue, &sat, nullptr); + _r = r; + _g = g; + _b = b; + _hue = hue; + _sat = sat; +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); +#endif + return max; + } + + void setHS(uint16_t hue, uint8_t sat) { + uint8_t r, g, b; + HsToRgb(hue, sat, &r, &g, &b); + _r = r; + _g = g; + _b = b; + _hue = hue; + _sat = sat; + addRGBMode(); +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS HS (%d %d) rgb (%d %d %d)", hue, sat, r, g, b); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); +#endif + } + + + + void setChannelsRaw(uint8_t *channels) { + _r = channels[0]; + _g = channels[1]; + _b = channels[2]; + _wc = channels[3]; + _ww = channels[4]; +} + + + + + void setChannels(uint8_t *channels) { + setRGB(channels[0], channels[1], channels[2]); + setCW(channels[3], channels[4], true); +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels (%d %d %d %d %d)", + channels[0], channels[1], channels[2], channels[3], channels[4]); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels CT (%d) briRGB (%d) briCT (%d)", _ct, _briRGB, _briCT); +#endif + } + + + static void RgbToHsb(uint8_t r, uint8_t g, uint8_t b, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri); + static void HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b); + static void RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y); + static void XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb); + +}; +# 720 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" +void LightStateClass::RgbToHsb(uint8_t ir, uint8_t ig, uint8_t ib, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri) { + uint32_t r = ir; + uint32_t g = ig; + uint32_t b = ib; + uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; + uint32_t min = (r < g && r < b) ? r : (g < b) ? g : b; + uint32_t d = max - min; + + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = max; + + if (d != 0) { + sat = changeUIntScale(d, 0, max, 0, 255); + if (r == max) { + hue = (g > b) ? changeUIntScale(g-b,0,d,0,60) : 360 - changeUIntScale(b-g,0,d,0,60); + } else if (g == max) { + hue = (b > r) ? 120 + changeUIntScale(b-r,0,d,0,60) : 120 - changeUIntScale(r-b,0,d,0,60); + } else { + hue = (r > g) ? 240 + changeUIntScale(r-g,0,d,0,60) : 240 - changeUIntScale(g-r,0,d,0,60); + } + hue = hue % 360; + } + + if (r_hue) *r_hue = hue; + if (r_sat) *r_sat = sat; + if (r_bri) *r_bri = bri; + +} + +void LightStateClass::HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { + uint32_t r = 255; + uint32_t g = 255; + uint32_t b = 255; + + hue = hue % 360; + + if (sat > 0) { + uint32_t i = hue / 60; + uint32_t f = hue % 60; + uint32_t q = 255 - changeUIntScale(f, 0, 60, 0, sat); + uint32_t p = 255 - sat; + uint32_t t = 255 - changeUIntScale(60 - f, 0, 60, 0, sat); + + switch (i) { + case 0: + + g = t; + b = p; + break; + case 1: + r = q; + + b = p; + break; + case 2: + r = p; + + b = t; + break; + case 3: + r = p; + g = q; + + break; + case 4: + r = t; + g = p; + + break; + default: + + g = p; + b = q; + break; + } + } + if (r_r) *r_r = r; + if (r_g) *r_g = g; + if (r_b) *r_b = b; +} + +#define POW FastPrecisePowf + +void LightStateClass::RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y) { + float x = 0.31271f; + float y = 0.32902f; + + if (i_r + i_b + i_g > 0) { + float r = (float)i_r / 255.0f; + float g = (float)i_g / 255.0f; + float b = (float)i_b / 255.0f; + + + r = (r > 0.04045f) ? POW((r + 0.055f) / (1.0f + 0.055f), 2.4f) : (r / 12.92f); + g = (g > 0.04045f) ? POW((g + 0.055f) / (1.0f + 0.055f), 2.4f) : (g / 12.92f); + b = (b > 0.04045f) ? POW((b + 0.055f) / (1.0f + 0.055f), 2.4f) : (b / 12.92f); + + + + float X = r * 0.649926f + g * 0.103455f + b * 0.197109f; + float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; + float Z = r * 0.000000f + g * 0.053077f + b * 1.035763f; + + x = X / (X + Y + Z); + y = Y / (X + Y + Z); + + } + if (r_x) *r_x = x; + if (r_y) *r_y = y; +} + +void LightStateClass::XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb) +{ + x = (x > 0.99f ? 0.99f : (x < 0.01f ? 0.01f : x)); + y = (y > 0.99f ? 0.99f : (y < 0.01f ? 0.01f : y)); + float z = 1.0f - x - y; + + float X = x / y; + float Z = z / y; + + + + float r = X * 3.2406f - 1.5372f - Z * 0.4986f; + float g = -X * 0.9689f + 1.8758f + Z * 0.0415f; + float b = X * 0.0557f - 0.2040f + Z * 1.0570f; + float max = (r > g && r > b) ? r : (g > b) ? g : b; + r = r / max; + g = g / max; + b = b / max; + r = (r <= 0.0031308f) ? 12.92f * r : 1.055f * POW(r, (1.0f / 2.4f)) - 0.055f; + g = (g <= 0.0031308f) ? 12.92f * g : 1.055f * POW(g, (1.0f / 2.4f)) - 0.055f; + b = (b <= 0.0031308f) ? 12.92f * b : 1.055f * POW(b, (1.0f / 2.4f)) - 0.055f; + + + + + + int32_t ir = r * 255.0f + 0.5f; + int32_t ig = g * 255.0f + 0.5f; + int32_t ib = b * 255.0f + 0.5f; + if (rr) { *rr = (ir > 255 ? 255: (ir < 0 ? 0 : ir)); } + if (rg) { *rg = (ig > 255 ? 255: (ig < 0 ? 0 : ig)); } + if (rb) { *rb = (ib > 255 ? 255: (ib < 0 ? 0 : ib)); } +} + +class LightControllerClass { +private: + LightStateClass *_state; + + + bool _ct_rgb_linked = true; + bool _pwm_multi_channels = false; + +public: + LightControllerClass(LightStateClass& state) { + _state = &state; + } + + void setSubType(uint8_t sub_type) { + _state->setSubType(sub_type); + } + + inline bool setCTRGBLinked(bool ct_rgb_linked) { + bool prev = _ct_rgb_linked; + if (_pwm_multi_channels) { + _ct_rgb_linked = false; + } else { + _ct_rgb_linked = ct_rgb_linked; + } + return prev; + } + + void setAlexaCTRange(bool alexa_ct_range) { + + if (alexa_ct_range) { + _state->setCTRange(CT_MIN_ALEXA, CT_MAX_ALEXA); + } else { + _state->setCTRange(CT_MIN, CT_MAX); + } + } + + inline bool isCTRGBLinked() { + return _ct_rgb_linked; + } + + inline bool setPWMMultiChannel(bool pwm_multi_channels) { + bool prev = _pwm_multi_channels; + _pwm_multi_channels = pwm_multi_channels; + if (pwm_multi_channels) setCTRGBLinked(false); + return prev; + } + + inline bool isPWMMultiChannel(void) { + return _pwm_multi_channels; + } + +#ifdef DEBUG_LIGHT + void debugLogs() { + uint8_t r,g,b,c,w; + _state->getActualRGBCW(&r,&g,&b,&c,&w); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs rgb (%d %d %d) cw (%d %d)", + r, g, b, c, w); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs lightCurrent (%d %d %d %d %d)", + Light.current_color[0], Light.current_color[1], Light.current_color[2], + Light.current_color[3], Light.current_color[4]); + } +#endif + + void loadSettings() { +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings Settings.light_color (%d %d %d %d %d - %d)", + Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], + Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings light_type/sub (%d %d)", + light_type, Light.subtype); +#endif + if (_pwm_multi_channels) { + _state->setChannelsRaw(Settings.light_color); + } else { + + _state->setCW(Settings.light_color[3], Settings.light_color[4], true); + _state->setRGB(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]); + + + + uint8_t bri = _state->DimmerToBri(Settings.light_dimmer); + if (Settings.light_color[0] + Settings.light_color[1] + Settings.light_color[2] > 0) { + + + if ( (DEFAULT_LIGHT_COMPONENT == Settings.light_color[0]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[1]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[2]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[3]) && + (DEFAULT_LIGHT_COMPONENT == Settings.light_color[4]) && + (DEFAULT_LIGHT_DIMMER == Settings.light_dimmer) ) { + _state->setColorMode(LCM_RGB); + } + _state->setBriRGB(bri); + } else { + _state->setBriCT(bri); + } + } + } + + void changeCTB(uint16_t new_ct, uint8_t briCT) { + + + + + + + if ((LST_COLDWARM != Light.subtype) && (LST_RGBW > Light.subtype)) { + return; + } + _state->setCT(new_ct); + _state->setBriCT(briCT); + if (_ct_rgb_linked) { _state->setColorMode(LCM_CT); } + saveSettings(); + calcLevels(); + + } + + void changeDimmer(uint8_t dimmer, uint32_t mode = 0) { + uint8_t bri = changeUIntScale(dimmer, 0, 100, 0, 255); + switch (mode) { + case 1: + changeBriRGB(bri); + if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } + break; + case 2: + changeBriCT(bri); + if (_ct_rgb_linked) { _state->setColorMode(LCM_CT); } + break; + default: + changeBri(bri); + break; + } + } + + void changeBri(uint8_t bri) { + _state->setBri(bri); + saveSettings(); + calcLevels(); + } + + void changeBriRGB(uint8_t bri) { + _state->setBriRGB(bri); + saveSettings(); + calcLevels(); + } + + void changeBriCT(uint8_t bri) { + _state->setBriCT(bri); + saveSettings(); + calcLevels(); + } + + void changeRGB(uint8_t r, uint8_t g, uint8_t b, bool keep_bri = false) { + _state->setRGB(r, g, b, keep_bri); + if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } + saveSettings(); + calcLevels(); + } + + + + void calcLevels(uint8_t *current_color = nullptr) { + uint8_t r,g,b,c,w,briRGB,briCT; + if (current_color == nullptr) { current_color = Light.current_color; } + + if (_pwm_multi_channels) { + _state->getChannelsRaw(current_color); + return; + } + + _state->getActualRGBCW(&r,&g,&b,&c,&w); + briRGB = _state->getBriRGB(); + briCT = _state->getBriCT(); + + current_color[0] = current_color[1] = current_color[2] = 0; + current_color[3] = current_color[4] = 0; + switch (Light.subtype) { + case LST_NONE: + current_color[0] = 255; + break; + case LST_SINGLE: + current_color[0] = briRGB; + break; + case LST_COLDWARM: + current_color[0] = c; + current_color[1] = w; + break; + case LST_RGBW: + case LST_RGBCW: + if (LST_RGBCW == Light.subtype) { + current_color[3] = c; + current_color[4] = w; + } else { + current_color[3] = briCT; + } + + case LST_RGB: + current_color[0] = r; + current_color[1] = g; + current_color[2] = b; + break; + } + } + + void changeHSB(uint16_t hue, uint8_t sat, uint8_t briRGB) { + _state->setHS(hue, sat); + _state->setBriRGB(briRGB); + if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } + saveSettings(); + calcLevels(); + } + + + void saveSettings() { + if (Light.pwm_multi_channels) { + + _state->getChannelsRaw(Settings.light_color); + Settings.light_dimmer = 100; + } else { + uint8_t cm = _state->getColorMode(); + + memset(&Settings.light_color[0], 0, sizeof(Settings.light_color)); + if (LCM_RGB & cm) { + _state->getRGB(&Settings.light_color[0], &Settings.light_color[1], &Settings.light_color[2]); + Settings.light_dimmer = _state->BriToDimmer(_state->getBriRGB()); + + if (LCM_BOTH == cm) { + + _state->getActualRGBCW(nullptr, nullptr, nullptr, &Settings.light_color[3], &Settings.light_color[4]); + } + } else if (LCM_CT == cm) { + _state->getCW(&Settings.light_color[3], &Settings.light_color[4]); + Settings.light_dimmer = _state->BriToDimmer(_state->getBriCT()); + } + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::saveSettings Settings.light_color (%d %d %d %d %d - %d)", + Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], + Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer); +#endif + } + + + + + void changeChannels(uint8_t *channels) { + if (Light.pwm_multi_channels) { + _state->setChannelsRaw(channels); + } else if (LST_COLDWARM == Light.subtype) { + + uint8_t remapped_channels[5] = {0,0,0,channels[0],channels[1]}; + _state->setChannels(remapped_channels); + } else { + _state->setChannels(channels); + } + + saveSettings(); + calcLevels(); + } +}; + + + +LightStateClass light_state = LightStateClass(); +LightControllerClass light_controller = LightControllerClass(light_state); + + + + + +uint16_t change8to10(uint8_t v) { + return changeUIntScale(v, 0, 255, 0, 1023); +} + +uint8_t change10to8(uint16_t v) { + return (0 == v) ? 0 : changeUIntScale(v, 4, 1023, 1, 255); +} + + + + + +uint16_t ledGamma_internal(uint16_t v, const struct gamma_table_t *gt_ptr) { + uint16_t from_src = 0; + uint16_t from_gamma = 0; + + for (const gamma_table_t *gt = gt_ptr; ; gt++) { + uint16_t to_src = gt->to_src; + uint16_t to_gamma = gt->to_gamma; + if (v <= to_src) { + return changeUIntScale(v, from_src, to_src, from_gamma, to_gamma); + } + from_src = to_src; + from_gamma = to_gamma; + } +} + +uint16_t ledGammaReverse_internal(uint16_t vg, const struct gamma_table_t *gt_ptr) { + uint16_t from_src = 0; + uint16_t from_gamma = 0; + + for (const gamma_table_t *gt = gt_ptr; ; gt++) { + uint16_t to_src = gt->to_src; + uint16_t to_gamma = gt->to_gamma; + if (vg <= to_gamma) { + return changeUIntScale(vg, from_gamma, to_gamma, from_src, to_src); + } + from_src = to_src; + from_gamma = to_gamma; + } +} + + +uint16_t ledGamma10_10(uint16_t v) { + return ledGamma_internal(v, gamma_table); +} + +uint16_t ledGamma10(uint8_t v) { + return ledGamma10_10(change8to10(v)); +} + + +uint8_t ledGamma(uint8_t v) { + return change10to8(ledGamma10(v)); +} + + + +void LightPwmOffset(uint32_t offset) +{ + Light.pwm_offset = offset; +} + +bool LightModuleInit(void) +{ + light_type = LT_BASIC; + + if (Settings.flag.pwm_control) { + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (pin[GPIO_PWM1 +i] < 99) { light_type++; } + } + } + + light_flg = 0; + if (XlgtCall(FUNC_MODULE_INIT)) { + + } + else if (SONOFF_BN == my_module_type) { + light_type = LT_PWM1; + } + else if (SONOFF_LED == my_module_type) { + if (!my_module.io[4]) { + pinMode(4, OUTPUT); + digitalWrite(4, LOW); + } + if (!my_module.io[5]) { + pinMode(5, OUTPUT); + digitalWrite(5, LOW); + } + if (!my_module.io[14]) { + pinMode(14, OUTPUT); + digitalWrite(14, LOW); + } + light_type = LT_PWM2; + } + + if (light_type > LT_BASIC) { + devices_present++; + } + + + if (Settings.flag3.pwm_multi_channels) { + uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + if (0 == pwm_channels) { pwm_channels = 1; } + devices_present += pwm_channels - 1; + } else if ((Settings.param[P_RGB_REMAP] & 128) && (LST_RGBW <= (light_type & 7))) { + + devices_present++; + } + + return (light_type > LT_BASIC); +} + +void LightInit(void) +{ + Light.device = devices_present; + Light.subtype = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + Light.pwm_multi_channels = Settings.flag3.pwm_multi_channels; + + if (LST_RGBW <= Light.subtype) { + + + bool ct_rgb_linked = !(Settings.param[P_RGB_REMAP] & 128); + light_controller.setCTRGBLinked(ct_rgb_linked); + } + + if ((LST_SINGLE <= Light.subtype) && Light.pwm_multi_channels) { + + light_controller.setPWMMultiChannel(true); + Light.device = devices_present - Light.subtype + 1; + } else if (!light_controller.isCTRGBLinked()) { + + Light.device--; + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightInit Light.pwm_multi_channels=%d Light.subtype=%d Light.device=%d devices_present=%d", + Light.pwm_multi_channels, Light.subtype, Light.device, devices_present); +#endif + + light_controller.setSubType(Light.subtype); + light_controller.loadSettings(); + light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); + + if (LST_SINGLE == Light.subtype) { + Settings.light_color[0] = 255; + } + if (light_type < LT_PWM6) { + for (uint32_t i = 0; i < light_type; i++) { + Settings.pwm_value[i] = 0; + if (pin[GPIO_PWM1 +i] < 99) { + pinMode(pin[GPIO_PWM1 +i], OUTPUT); + } + } + if (pin[GPIO_ARIRFRCV] < 99) { + if (pin[GPIO_ARIRFSEL] < 99) { + pinMode(pin[GPIO_ARIRFSEL], OUTPUT); + digitalWrite(pin[GPIO_ARIRFSEL], 1); + } + } + } + + uint32_t max_scheme = Light.max_scheme; + if (Light.subtype < LST_RGB) { + max_scheme = LS_POWER; + } + if ((LS_WAKEUP == Settings.light_scheme) || (Settings.light_scheme > max_scheme)) { + Settings.light_scheme = LS_POWER; + } + Light.power = 0; + Light.update = true; + Light.wakeup_active = 0; + + LightUpdateColorMapping(); +} + +void LightUpdateColorMapping(void) +{ + uint8_t param = Settings.param[P_RGB_REMAP] & 127; + if (param > 119){ param = 0; } + + uint8_t tmp[] = {0,1,2,3,4}; + Light.color_remap[0] = tmp[param / 24]; + for (uint32_t i = param / 24; i<4; ++i){ + tmp[i] = tmp[i+1]; + } + param = param % 24; + Light.color_remap[1] = tmp[(param / 6)]; + for (uint32_t i = param / 6; i<3; ++i){ + tmp[i] = tmp[i+1]; + } + param = param % 6; + Light.color_remap[2] = tmp[(param / 2)]; + for (uint32_t i = param / 2; i<2; ++i){ + tmp[i] = tmp[i+1]; + } + param = param % 2; + Light.color_remap[3] = tmp[param]; + Light.color_remap[4] = tmp[1-param]; + + Light.update = true; + +} + +uint8_t LightGetDimmer(uint8_t dimmer) { + return light_state.getDimmer(dimmer); +} + +void LightSetDimmer(uint8_t dimmer) { + light_controller.changeDimmer(dimmer); +} + +void LightGetHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) { + light_state.getHSB(hue, sat, bri); +} + +void LightHsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { + light_state.HsToRgb(hue, sat, r_r, r_g, r_b); +} + + +uint8_t LightGetBri(uint8_t device) { + uint8_t bri = 254; + if (Light.pwm_multi_channels) { + if ((device >= Light.device) && (device < Light.device + LST_MAX) && (device <= devices_present)) { + bri = Light.current_color[device - Light.device]; + } + } else if (light_controller.isCTRGBLinked()) { + if (device == Light.device) { + bri = light_state.getBri(); + } + } else { + if (device == Light.device) { + bri = light_state.getBriRGB(); + } else if (device == Light.device + 1) { + bri = light_state.getBriCT(); + } + } + return bri; +} + + +void LightSetBri(uint8_t device, uint8_t bri) { + if (Light.pwm_multi_channels) { + if ((device >= Light.device) && (device < Light.device + LST_MAX) && (device <= devices_present)) { + Light.current_color[device - Light.device] = bri; + light_controller.changeChannels(Light.current_color); + } + } else if (light_controller.isCTRGBLinked()) { + if (device == Light.device) { + light_controller.changeBri(bri); + } + } else { + if (device == Light.device) { + light_controller.changeBriRGB(bri); + } else if (device == Light.device + 1) { + light_controller.changeBriCT(bri); + } + } +} + +void LightSetColorTemp(uint16_t ct) +{ + + + + + + + if ((LST_COLDWARM != Light.subtype) && (LST_RGBCW != Light.subtype)) { + return; + } + light_controller.changeCTB(ct, light_state.getBriCT()); +} + +uint16_t LightGetColorTemp(void) +{ + + if ((LST_COLDWARM != Light.subtype) && (LST_RGBCW != Light.subtype)) { + return 0; + } + return (light_state.getColorMode() & LCM_CT) ? light_state.getCT() : 0; +} + +void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value) +{ + + + + if (Settings.flag.light_signal) { + uint16_t signal = changeUIntScale(value, lo, hi, 0, 255); + + light_controller.changeRGB(signal, 255 - signal, 0, true); + Settings.light_scheme = 0; + if (0 == light_state.getBri()) { + light_controller.changeBri(50); + } + } +} + + +char* LightGetColor(char* scolor, boolean force_hex = false) +{ + light_controller.calcLevels(); + scolor[0] = '\0'; + for (uint32_t i = 0; i < Light.subtype; i++) { + if (!force_hex && Settings.flag.decimal_text) { + snprintf_P(scolor, LIGHT_COLOR_SIZE, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Light.current_color[i]); + } else { + snprintf_P(scolor, LIGHT_COLOR_SIZE, PSTR("%s%02X"), scolor, Light.current_color[i]); + } + } + return scolor; +} + +void LightPowerOn(void) +{ + if (light_state.getBri() && !(Light.power)) { + ExecuteCommandPower(Light.device, POWER_ON, SRC_LIGHT); + } +} + +void LightState(uint8_t append) +{ + char scolor[LIGHT_COLOR_SIZE]; + char scommand[33]; + bool unlinked = !light_controller.isCTRGBLinked() && (Light.subtype >= LST_RGBW); + + if (append) { + ResponseAppend_P(PSTR(",")); + } else { + Response_P(PSTR("{")); + } + if (!Light.pwm_multi_channels) { + if (unlinked) { + + ResponseAppend_P(PSTR("\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "%d\":%d" + ",\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "%d\":%d"), + Light.device, GetStateText(Light.power & 1), Light.device, light_state.getDimmer(1), + Light.device + 1, GetStateText(Light.power & 2 ? 1 : 0), Light.device + 1, light_state.getDimmer(2)); + } else { + GetPowerDevice(scommand, Light.device, sizeof(scommand), Settings.flag.device_index_enable); + ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_DIMMER "\":%d"), scommand, GetStateText(Light.power & 1), + light_state.getDimmer()); + } + + + if (Light.subtype > LST_SINGLE) { + ResponseAppend_P(PSTR(",\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); + if (LST_RGB <= Light.subtype) { + uint16_t hue; + uint8_t sat, bri; + light_state.getHSB(&hue, &sat, &bri); + sat = changeUIntScale(sat, 0, 255, 0, 100); + bri = changeUIntScale(bri, 0, 255, 0, 100); + + ResponseAppend_P(PSTR(",\"" D_CMND_HSBCOLOR "\":\"%d,%d,%d\""), hue,sat,bri); + } + + if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) { + ResponseAppend_P(PSTR(",\"" D_CMND_WHITE "\":%d"), light_state.getDimmer(2)); + } + + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + ResponseAppend_P(PSTR(",\"" D_CMND_COLORTEMPERATURE "\":%d"), light_state.getCT()); + } + + ResponseAppend_P(PSTR(",\"" D_CMND_CHANNEL "\":[" )); + for (uint32_t i = 0; i < Light.subtype; i++) { + uint8_t channel_raw = Light.current_color[i]; + uint8_t channel = changeUIntScale(channel_raw,0,255,0,100); + + if ((0 == channel) && (channel_raw > 0)) { channel = 1; } + ResponseAppend_P(PSTR("%s%d" ), (i > 0 ? "," : ""), channel); + } + ResponseAppend_P(PSTR("]")); + } + + if (append) { + if (Light.subtype >= LST_RGB) { + ResponseAppend_P(PSTR(",\"" D_CMND_SCHEME "\":%d"), Settings.light_scheme); + } + if (Light.max_scheme > LS_MAX) { + ResponseAppend_P(PSTR(",\"" D_CMND_WIDTH "\":%d"), Settings.light_width); + } + ResponseAppend_P(PSTR(",\"" D_CMND_FADE "\":\"%s\",\"" D_CMND_SPEED "\":%d,\"" D_CMND_LEDTABLE "\":\"%s\""), + GetStateText(Settings.light_fade), Settings.light_speed, GetStateText(Settings.light_correction)); + } + } else { + for (uint32_t i = 0; i < Light.subtype; i++) { + GetPowerDevice(scommand, Light.device + i, sizeof(scommand), 1); + uint32_t light_power_masked = Light.power & (1 << i); + light_power_masked = light_power_masked ? 1 : 0; + ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_CHANNEL "%d\":%d,"), scommand, GetStateText(light_power_masked), Light.device + i, + changeUIntScale(Light.current_color[i], 0, 255, 0, 100)); + } + ResponseAppend_P(PSTR("\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); + } + + if (!append) { + ResponseJsonEnd(); + } +} + +void LightPreparePower(power_t channels = 0xFFFFFFFF) { +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower power=%d Light.power=%d", power, Light.power); +#endif + + if (Light.pwm_multi_channels) { + for (uint32_t i = 0; i < Light.subtype; i++) { + if (bitRead(channels, i)) { + + if ((Light.current_color[i]) && (!bitRead(Light.power, i))) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device + i, POWER_ON_NO_STATE, SRC_LIGHT); + } + } else { + + if ((0 == Light.current_color[i]) && bitRead(Light.power, i)) { + ExecuteCommandPower(Light.device + i, POWER_OFF_NO_STATE, SRC_LIGHT); + } + } + #ifdef USE_DOMOTICZ + DomoticzUpdatePowerState(Light.device + i); + #endif + } + } + } else { + if (light_controller.isCTRGBLinked()) { + if (light_state.getBri() && !(Light.power)) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device, POWER_ON_NO_STATE, SRC_LIGHT); + } + } else if (!light_state.getBri() && Light.power) { + ExecuteCommandPower(Light.device, POWER_OFF_NO_STATE, SRC_LIGHT); + } + } else { + + if (channels & 1) { + if (light_state.getBriRGB() && !(Light.power & 1)) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device, POWER_ON_NO_STATE, SRC_LIGHT); + } + } else if (!light_state.getBriRGB() && (Light.power & 1)) { + ExecuteCommandPower(Light.device, POWER_OFF_NO_STATE, SRC_LIGHT); + } + } + + if (channels & 2) { + if (light_state.getBriCT() && !(Light.power & 2)) { + if (!Settings.flag.not_power_linked) { + ExecuteCommandPower(Light.device + 1, POWER_ON_NO_STATE, SRC_LIGHT); + } + } else if (!light_state.getBriCT() && (Light.power & 2)) { + ExecuteCommandPower(Light.device + 1, POWER_OFF_NO_STATE, SRC_LIGHT); + } + } + } +#ifdef USE_DOMOTICZ + DomoticzUpdatePowerState(Light.device); +#endif + } + + if (Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } + +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower End power=%d Light.power=%d", power, Light.power); +#endif + Light.power = power >> (Light.device - 1); + LightState(0); +} + +void LightCycleColor(int8_t direction) +{ + if (Light.strip_timer_counter % (Settings.light_speed * 2)) { + return; + } + + if (0 == direction) { + if (Light.random == Light.wheel) { + Light.random = random(255); + + uint8_t my_dir = (Light.random < Light.wheel -128) ? 1 : + (Light.random < Light.wheel ) ? 0 : + (Light.random > Light.wheel +128) ? 0 : 1; + Light.random = (Light.random & 0xFE) | my_dir; + + + } + + direction = (Light.random &0x01) ? 1 : -1; + } + Light.wheel += direction; + uint16_t hue = changeUIntScale(Light.wheel, 0, 255, 0, 359); + + + + uint8_t sat; + light_state.getHSB(nullptr, &sat, nullptr); + light_state.setHS(hue, sat); + light_controller.calcLevels(Light.new_color); +} + +void LightSetPower(void) +{ + + Light.old_power = Light.power; + + uint32_t mask = 1; + if (Light.pwm_multi_channels) { + mask = (1 << Light.subtype) - 1; + } else if (!light_controller.isCTRGBLinked()) { + mask = 3; + } + uint32_t shift = Light.device - 1; + + + + + + Light.power = (XdrvMailbox.index & (mask << shift)) >> shift; + if (Light.wakeup_active) { + Light.wakeup_active--; + } +#ifdef DEBUG_LIGHT + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightSetPower XdrvMailbox.index=%d Light.old_power=%d Light.power=%d mask=%d shift=%d", + XdrvMailbox.index, Light.old_power, Light.power, mask, shift); +#endif + if (Light.power != Light.old_power) { + Light.update = true; + } + LightAnimate(); +} + + + + +void LightAnimate(void) +{ + uint16_t light_still_on = 0; + bool power_off = false; + + + light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); + Light.strip_timer_counter++; + + + + if (Light.power || Light.fade_running) { + if (Settings.sleep > PWM_MAX_SLEEP) { + sleep = PWM_MAX_SLEEP; + } else { + sleep = Settings.sleep; + } + } else { + sleep = Settings.sleep; + } + + if (!Light.power) { + Light.strip_timer_counter = 0; + if (Settings.light_scheme >= LS_MAX) { + power_off = true; + } + } else { + switch (Settings.light_scheme) { + case LS_POWER: + light_controller.calcLevels(Light.new_color); + break; + case LS_WAKEUP: + if (2 == Light.wakeup_active) { + Light.wakeup_active = 1; + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.new_color[i] = 0; + } + Light.wakeup_counter = 0; + Light.wakeup_dimmer = 0; + } + Light.wakeup_counter++; + if (Light.wakeup_counter > ((Settings.light_wakeup * STATES) / Settings.light_dimmer)) { + Light.wakeup_counter = 0; + Light.wakeup_dimmer++; + if (Light.wakeup_dimmer <= Settings.light_dimmer) { + light_state.setDimmer(Light.wakeup_dimmer); + light_controller.calcLevels(); + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.new_color[i] = Light.current_color[i]; + } + } else { + + + + + Response_P(PSTR("{\"" D_CMND_WAKEUP "\":\"" D_JSON_DONE "\"")); + LightState(1); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WAKEUP)); + XdrvRulesProcess(); + + Light.wakeup_active = 0; + Settings.light_scheme = LS_POWER; + } + } + break; + case LS_CYCLEUP: + LightCycleColor(1); + break; + case LS_CYCLEDN: + LightCycleColor(-1); + break; + case LS_RANDOM: + LightCycleColor(0); + break; + default: + XlgtCall(FUNC_SET_SCHEME); + } + } + + if ((Settings.light_scheme < LS_MAX) || power_off) { + + + LightApplyPower(Light.new_color, Light.power); + + + + + + + + if (memcmp(Light.last_color, Light.new_color, Light.subtype)) { + Light.update = true; + } + if (Light.update) { + uint16_t cur_col_10[LST_MAX]; + Light.update = false; + + + for (uint32_t i = 0; i < LST_MAX; i++) { + Light.last_color[i] = Light.new_color[i]; + + cur_col_10[i] = change8to10(Light.new_color[i]); + } + + if (Light.pwm_multi_channels) { + calcGammaMultiChannels(cur_col_10); + } else { + calcGammaBulbs(cur_col_10); + + + + if ((LST_RGBW <= Light.subtype) && (0 == Settings.rgbwwTable[4]) && (0 == cur_col_10[3]+cur_col_10[4])) { + uint32_t min_rgb_10 = min3(cur_col_10[0], cur_col_10[1], cur_col_10[2]); + for (uint32_t i=0; i<3; i++) { + + uint32_t adjust10 = change8to10(Settings.rgbwwTable[i]); + cur_col_10[i] = changeUIntScale(cur_col_10[i] - min_rgb_10, 0, 1023, 0, adjust10); + } + + + uint32_t adjust_w_10 = changeUIntScale(Settings.rgbwwTable[3], 0, 255, 0, 1023); + uint32_t white_10 = changeUIntScale(min_rgb_10, 0, 1023, 0, adjust_w_10); + if (LST_RGBW == Light.subtype) { + + cur_col_10[3] = white_10; + } else { + + uint32_t ct = light_state.getCT10bits(); + cur_col_10[4] = changeUIntScale(ct, 0, 1023, 0, white_10); + cur_col_10[3] = white_10 - cur_col_10[4]; + } + } + } + + + if (0 != Settings.rgbwwTable[4]) { + for (uint32_t i = 0; i 0) ? changeUIntScale(cur_col_10[i], 1, 1023, 1, Settings.pwm_range) : 0; + } + + + uint16_t orig_col_10bits[LST_MAX]; + memcpy(orig_col_10bits, cur_col_10, sizeof(orig_col_10bits)); + for (uint32_t i = 0; i < LST_MAX; i++) { + cur_col_10[i] = orig_col_10bits[Light.color_remap[i]]; + } + + if (!Settings.light_fade || power_off || (!Light.fade_initialized)) { + + memcpy(Light.fade_start_10, cur_col_10, sizeof(Light.fade_start_10)); + + LightSetOutputs(cur_col_10); + Light.fade_initialized = true; + } else { + if (Light.fade_running) { + + memcpy(Light.fade_start_10, Light.fade_cur_10, sizeof(Light.fade_start_10)); + } + memcpy(Light.fade_end_10, cur_col_10, sizeof(Light.fade_start_10)); + Light.fade_running = true; + Light.fade_duration = 0; + Light.fade_start = 0; + + } + } + if (Light.fade_running) { + if (LightApplyFade()) { + + + + LightSetOutputs(Light.fade_cur_10); + } + } + } +} + +bool isChannelGammaCorrected(uint32_t channel) { + if (!Settings.light_correction) { return false; } + if (channel >= Light.subtype) { return false; } + + if (PHILIPS == my_module_type) { + if ((LST_COLDWARM == Light.subtype) && (1 == channel)) { return false; } + if ((LST_RGBCW == Light.subtype) && (4 == channel)) { return false; } + } + return true; +} + + +uint16_t fadeGamma(uint32_t channel, uint16_t v) { + if (isChannelGammaCorrected(channel)) { + return ledGamma_internal(v, gamma_table_fast); + } else { + return v; + } +} +uint16_t fadeGammaReverse(uint32_t channel, uint16_t vg) { + if (isChannelGammaCorrected(channel)) { + return ledGammaReverse_internal(vg, gamma_table_fast); + } else { + return vg; + } +} + +bool LightApplyFade(void) { + static uint32_t last_millis = 0; + uint32_t now = millis(); + + if ((now - last_millis) <= 5) { + return false; + } + last_millis = now; + + + if (0 == Light.fade_duration) { + Light.fade_start = now; + + uint32_t distance = 0; + for (uint32_t i = 0; i < Light.subtype; i++) { + int32_t channel_distance = fadeGammaReverse(i, Light.fade_end_10[i]) - fadeGammaReverse(i, Light.fade_start_10[i]); + if (channel_distance < 0) { channel_distance = - channel_distance; } + if (channel_distance > distance) { distance = channel_distance; } + } + if (distance > 0) { + + + + Light.fade_duration = (distance * Settings.light_speed * 500) / 1023; + if (Settings.save_data) { + + uint32_t delay_seconds = 1 + (Light.fade_duration + 999) / 1000; + + if (save_data_counter < delay_seconds) { + save_data_counter = delay_seconds; + } + } + } else { + + Light.fade_running = false; + } + } + + uint16_t fade_current = now - Light.fade_start; + if (fade_current <= Light.fade_duration) { + + for (uint32_t i = 0; i < Light.subtype; i++) { + Light.fade_cur_10[i] = fadeGamma(i, + changeUIntScale(fadeGammaReverse(i, fade_current), + 0, Light.fade_duration, + fadeGammaReverse(i, Light.fade_start_10[i]), + fadeGammaReverse(i, Light.fade_end_10[i]))); + + + + } + } else { + + + Light.fade_running = false; + Light.fade_start = 0; + Light.fade_duration = 0; + + memcpy(Light.fade_cur_10, Light.fade_end_10, sizeof(Light.fade_end_10)); + + memcpy(Light.fade_start_10, Light.fade_end_10, sizeof(Light.fade_start_10)); + } + return true; +} + + + +void LightApplyPower(uint8_t new_color[LST_MAX], power_t power) { + + if (Light.pwm_multi_channels) { + + for (uint32_t i = 0; i < LST_MAX; i++) { + if (0 == bitRead(power,i)) { + new_color[i] = 0; + } + } + + + + + + } else { + if (!light_controller.isCTRGBLinked()) { + + if (0 == (power & 1)) { + new_color[0] = new_color[1] = new_color[2] = 0; + } + if (0 == (power & 2)) { + new_color[3] = new_color[4] = 0; + } + } else if (!power) { + for (uint32_t i = 0; i < LST_MAX; i++) { + new_color[i] = 0; + } + } + } +} + +void LightSetOutputs(const uint16_t *cur_col_10) { + + if (light_type < LT_PWM6) { + for (uint32_t i = 0; i < (Light.subtype - Light.pwm_offset); i++) { + if (pin[GPIO_PWM1 +i] < 99) { + + analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col_10[(i + Light.pwm_offset)] : cur_col_10[(i + Light.pwm_offset)]); + } + } + } + + + + + uint8_t cur_col[LST_MAX]; + for (uint32_t i = 0; i < LST_MAX; i++) { + cur_col[i] = change10to8(cur_col_10[i]); + } + + + uint8_t scale_col[3]; + uint32_t max = (cur_col[0] > cur_col[1] && cur_col[0] > cur_col[2]) ? cur_col[0] : (cur_col[1] > cur_col[2]) ? cur_col[1] : cur_col[2]; + for (uint32_t i = 0; i < 3; i++) { + scale_col[i] = (0 == max) ? 255 : (255 > max) ? changeUIntScale(cur_col[i], 0, max, 0, 255) : cur_col[i]; + } + + char *tmp_data = XdrvMailbox.data; + char *tmp_topic = XdrvMailbox.topic; + XdrvMailbox.data = (char*)cur_col; + XdrvMailbox.topic = (char*)scale_col; + if (XlgtCall(FUNC_SET_CHANNELS)) { } + else if (XdrvCall(FUNC_SET_CHANNELS)) { } + XdrvMailbox.data = tmp_data; + XdrvMailbox.topic = tmp_topic; +} + + +void calcGammaMultiChannels(uint16_t cur_col_10[5]) { + + if (Settings.light_correction) { + for (uint32_t i = 0; i < LST_MAX; i++) { + cur_col_10[i] = ledGamma10_10(cur_col_10[i]); + } + } +} + +void calcGammaBulbs(uint16_t cur_col_10[5]) { + + + + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + + uint32_t cw1 = Light.subtype - 1; + uint32_t cw0 = Light.subtype - 2; + uint16_t white_bri10 = cur_col_10[cw0] + cur_col_10[cw1]; + uint16_t white_bri10_1023 = (white_bri10 > 1023) ? 1023 : white_bri10; + + if (PHILIPS == my_module_type) { + + cur_col_10[cw1] = light_state.getCT10bits(); + + if (Settings.light_correction) { + cur_col_10[cw0] = ledGamma10_10(white_bri10_1023); + } else { + cur_col_10[cw0] = white_bri10_1023; + } + } else if (Settings.light_correction) { + + if (white_bri10 <= 1031) { + + uint16_t white_bri_gamma10 = ledGamma10_10(white_bri10_1023); + + cur_col_10[cw0] = changeUIntScale(cur_col_10[cw0], 0, white_bri10_1023, 0, white_bri_gamma10); + cur_col_10[cw1] = changeUIntScale(cur_col_10[cw1], 0, white_bri10_1023, 0, white_bri_gamma10); + } else { + cur_col_10[cw0] = ledGamma10_10(cur_col_10[cw0]); + cur_col_10[cw1] = ledGamma10_10(cur_col_10[cw1]); + } + } + } + + if (Settings.light_correction) { + + if (LST_RGB <= Light.subtype) { + for (uint32_t i = 0; i < 3; i++) { + cur_col_10[i] = ledGamma10_10(cur_col_10[i]); + } + } + + if ((LST_SINGLE == Light.subtype) || (LST_RGBW == Light.subtype)) { + cur_col_10[Light.subtype - 1] = ledGamma10_10(cur_col_10[Light.subtype - 1]); + } + } +} + + + + + +bool LightColorEntry(char *buffer, uint32_t buffer_length) +{ + char scolor[10]; + char *p; + char *str; + uint32_t entry_type = 0; + uint8_t value = Light.fixed_color_index; + + if (buffer[0] == '#') { + buffer++; + buffer_length--; + } + + if (Light.subtype >= LST_RGB) { + char option = (1 == buffer_length) ? buffer[0] : '\0'; + if (('+' == option) && (Light.fixed_color_index < MAX_FIXED_COLOR)) { + value++; + } + else if (('-' == option) && (Light.fixed_color_index > 1)) { + value--; + } else { + value = atoi(buffer); + } + } + + memset(&Light.entry_color, 0x00, sizeof(Light.entry_color)); + + while ((buffer_length > 0) && ('=' == buffer[buffer_length - 1])) { + buffer_length--; + memcpy(&Light.entry_color, &Light.current_color, sizeof(Light.entry_color)); + } + if (strstr(buffer, ",") != nullptr) { + int8_t i = 0; + for (str = strtok_r(buffer, ",", &p); str && i < 6; str = strtok_r(nullptr, ",", &p)) { + if (i < LST_MAX) { + Light.entry_color[i++] = atoi(str); + } + } + entry_type = 2; + } + else if (((2 * Light.subtype) == buffer_length) || (buffer_length > 3)) { + for (uint32_t i = 0; i < tmin((uint)(buffer_length / 2), sizeof(Light.entry_color)); i++) { + strlcpy(scolor, buffer + (i *2), 3); + Light.entry_color[i] = (uint8_t)strtol(scolor, &p, 16); + } + entry_type = 1; + } + else if ((Light.subtype >= LST_RGB) && (value > 0) && (value <= MAX_FIXED_COLOR)) { + Light.fixed_color_index = value; + memcpy_P(&Light.entry_color, &kFixedColor[value -1], 3); + entry_type = 1; + } + else if ((value > 199) && (value <= 199 + MAX_FIXED_COLD_WARM)) { + if (LST_RGBW == Light.subtype) { + memcpy_P(&Light.entry_color[3], &kFixedWhite[value -200], 1); + entry_type = 1; + } + else if (LST_COLDWARM == Light.subtype) { + memcpy_P(&Light.entry_color, &kFixedColdWarm[value -200], 2); + entry_type = 1; + } + else if (LST_RGBCW == Light.subtype) { + memcpy_P(&Light.entry_color[3], &kFixedColdWarm[value -200], 2); + entry_type = 1; + } + } + if (entry_type) { + Settings.flag.decimal_text = entry_type -1; + } + return (entry_type); +} + + + +void CmndSupportColor(void) +{ + bool valid_entry = false; + bool coldim = false; + + if (XdrvMailbox.data_len > 0) { + valid_entry = LightColorEntry(XdrvMailbox.data, XdrvMailbox.data_len); + if (valid_entry) { + if (XdrvMailbox.index <= 2) { + uint32_t old_bri = light_state.getBri(); + + light_controller.changeChannels(Light.entry_color); + if (2 == XdrvMailbox.index) { + + light_controller.changeBri(old_bri); + } + + Settings.light_scheme = 0; + coldim = true; + } else { + for (uint32_t i = 0; i < LST_RGB; i++) { + Settings.ws_color[XdrvMailbox.index -3][i] = Light.entry_color[i]; + } + } + } + } + char scolor[LIGHT_COLOR_SIZE]; + if (!valid_entry && (XdrvMailbox.index <= 2)) { + ResponseCmndChar(LightGetColor(scolor)); + } + if (XdrvMailbox.index >= 3) { + scolor[0] = '\0'; + for (uint32_t i = 0; i < LST_RGB; i++) { + if (Settings.flag.decimal_text) { + snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.ws_color[XdrvMailbox.index -3][i]); + } else { + snprintf_P(scolor, sizeof(scolor), PSTR("%s%02X"), scolor, Settings.ws_color[XdrvMailbox.index -3][i]); + } + } + ResponseCmndIdxChar(scolor); + } + if (coldim) { + LightPreparePower(); + } +} + +void CmndColor(void) +{ + + + + + + + + if ((Light.subtype > LST_SINGLE) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 6)) { + CmndSupportColor(); + } +} + +void CmndWhite(void) +{ + + + if (Light.pwm_multi_channels) { return; } + if ( ((Light.subtype >= LST_RGBW) || (LST_COLDWARM == Light.subtype)) && (XdrvMailbox.index == 1)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + light_controller.changeDimmer(XdrvMailbox.payload, 2); + LightPreparePower(2); + } else { + ResponseCmndNumber(light_state.getDimmer(2)); + } + } +} + +void CmndChannel(void) +{ + + + + + if ((XdrvMailbox.index >= Light.device) && (XdrvMailbox.index < Light.device + Light.subtype )) { + uint32_t light_index = XdrvMailbox.index - Light.device; + power_t coldim = 0; + + + if (1 == XdrvMailbox.data_len) { + uint8_t channel = changeUIntScale(Light.current_color[light_index],0,255,0,100); + if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (channel > 89) ? 100 : channel + 10; + } else if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (channel < 11) ? 1 : channel - 10; + } + } + + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + Light.current_color[light_index] = changeUIntScale(XdrvMailbox.payload,0,100,0,255); + if (Light.pwm_multi_channels) { + coldim = 1 << light_index; + } else { + if (light_controller.isCTRGBLinked()) { + + if ((light_index < 3) && (light_controller.isCTRGBLinked())) { + Light.current_color[3] = Light.current_color[4] = 0; + } else { + Light.current_color[0] = Light.current_color[1] = Light.current_color[2] = 0; + } + coldim = 1; + } else { + if (light_index < 3) { coldim = 1; } + else { coldim = 2; } + } + } + light_controller.changeChannels(Light.current_color); + } + ResponseCmndIdxNumber(changeUIntScale(Light.current_color[light_index],0,255,0,100)); + if (coldim) { + LightPreparePower(coldim); + } + } +} + +void CmndHsbColor(void) +{ + + + + + + + + if (Light.subtype >= LST_RGB) { + if (XdrvMailbox.data_len > 0) { + uint16_t c_hue; + uint8_t c_sat; + light_state.getHSB(&c_hue, &c_sat, nullptr); + uint32_t HSB[3]; + HSB[0] = c_hue; + HSB[1] = c_sat; + HSB[2] = light_state.getBriRGB(); + if ((2 == XdrvMailbox.index) || (3 == XdrvMailbox.index)) { + if ((uint32_t)XdrvMailbox.payload > 100) { XdrvMailbox.payload = 100; } + HSB[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload, 0, 100, 0, 255); + } else { + uint32_t paramcount = ParseParameters(3, HSB); + if (HSB[0] > 360) { HSB[0] = 360; } + for (uint32_t i = 1; i < paramcount; i++) { + if (HSB[i] > 100) { HSB[i] == 100; } + HSB[i] = changeUIntScale(HSB[i], 0, 100, 0, 255); + } + } + light_controller.changeHSB(HSB[0], HSB[1], HSB[2]); + LightPreparePower(1); + } else { + LightState(0); + } + } +} + +void CmndScheme(void) +{ + + + + + + if (Light.subtype >= LST_RGB) { + uint32_t max_scheme = Light.max_scheme; + + if (1 == XdrvMailbox.data_len) { + if (('+' == XdrvMailbox.data[0]) && (Settings.light_scheme < max_scheme)) { + XdrvMailbox.payload = Settings.light_scheme + ((0 == Settings.light_scheme) ? 2 : 1); + } + else if (('-' == XdrvMailbox.data[0]) && (Settings.light_scheme > 0)) { + XdrvMailbox.payload = Settings.light_scheme - ((2 == Settings.light_scheme) ? 2 : 1); + } + } + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= max_scheme)) { + uint32_t parm[2]; + if (ParseParameters(2, parm) > 1) { + Light.wheel = parm[1]; + } + Settings.light_scheme = XdrvMailbox.payload; + if (LS_WAKEUP == Settings.light_scheme) { + Light.wakeup_active = 3; + } + LightPowerOn(); + Light.strip_timer_counter = 0; + + if (Settings.flag3.hass_tele_on_power) { + MqttPublishTeleState(); + } + } + ResponseCmndNumber(Settings.light_scheme); + } +} + +void CmndWakeup(void) +{ + + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + light_controller.changeDimmer(XdrvMailbox.payload); + } + Light.wakeup_active = 3; + Settings.light_scheme = LS_WAKEUP; + LightPowerOn(); + ResponseCmndChar(D_JSON_STARTED); +} + +void CmndColorTemperature(void) +{ + + + + + if (Light.pwm_multi_channels) { return; } + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { + uint32_t ct = light_state.getCT(); + if (1 == XdrvMailbox.data_len) { + if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (ct > (CT_MAX-34)) ? CT_MAX : ct + 34; + } + else if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (ct < (CT_MIN+34)) ? CT_MIN : ct - 34; + } + } + if ((XdrvMailbox.payload >= CT_MIN) && (XdrvMailbox.payload <= CT_MAX)) { + light_controller.changeCTB(XdrvMailbox.payload, light_state.getBriCT()); + LightPreparePower(2); + } else { + ResponseCmndNumber(ct); + } + } +} + +void CmndDimmer(void) +{ + + + + + + + uint32_t dimmer; + if (XdrvMailbox.index > 2) { XdrvMailbox.index = 1; } + + if ((light_controller.isCTRGBLinked()) || (0 == XdrvMailbox.index)) { + dimmer = light_state.getDimmer(); + } else { + dimmer = light_state.getDimmer(XdrvMailbox.index); + } + + if (1 == XdrvMailbox.data_len) { + if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (dimmer > 89) ? 100 : dimmer + 10; + } else if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (dimmer < 11) ? 1 : dimmer - 10; + } + } + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + if (light_controller.isCTRGBLinked()) { + + light_controller.changeDimmer(XdrvMailbox.payload); + LightPreparePower(); + } else { + if (0 != XdrvMailbox.index) { + light_controller.changeDimmer(XdrvMailbox.payload, XdrvMailbox.index); + LightPreparePower(1 << (XdrvMailbox.index - 1)); + } else { + + light_controller.changeDimmer(XdrvMailbox.payload, 1); + light_controller.changeDimmer(XdrvMailbox.payload, 2); + LightPreparePower(); + } + } + Light.update = true; + } else { + ResponseCmndNumber(dimmer); + } +} + +void CmndDimmerRange(void) +{ + + + if (XdrvMailbox.data_len > 0) { + uint32_t parm[2]; + parm[0] = Settings.dimmer_hw_min; + parm[1] = Settings.dimmer_hw_max; + ParseParameters(2, parm); + if (parm[0] < parm[1]) { + Settings.dimmer_hw_min = parm[0]; + Settings.dimmer_hw_max = parm[1]; + } else { + Settings.dimmer_hw_min = parm[1]; + Settings.dimmer_hw_max = parm[0]; + } + restart_flag = 2; + } + Response_P(PSTR("{\"" D_CMND_DIMMER_RANGE "\":{\"Min\":%d,\"Max\":%d}}"), Settings.dimmer_hw_min, Settings.dimmer_hw_max); +} + +void CmndLedTable(void) +{ + + + + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { + switch (XdrvMailbox.payload) { + case 0: + case 1: + Settings.light_correction = XdrvMailbox.payload; + break; + case 2: + Settings.light_correction ^= 1; + break; + } + Light.update = true; + } + ResponseCmndStateText(Settings.light_correction); +} + +void CmndRgbwwTable(void) +{ + + + if ((XdrvMailbox.data_len > 0)) { + uint32_t parm[LST_RGBCW -1]; + uint32_t parmcount = ParseParameters(LST_RGBCW, parm); + for (uint32_t i = 0; i < parmcount; i++) { + Settings.rgbwwTable[i] = parm[i]; + } + Light.update = true; + } + char scolor[LIGHT_COLOR_SIZE]; + scolor[0] = '\0'; + for (uint32_t i = 0; i < LST_RGBCW; i++) { + snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.rgbwwTable[i]); + } + ResponseCmndChar(scolor); +} + +void CmndFade(void) +{ + + + + + switch (XdrvMailbox.payload) { + case 0: + case 1: + Settings.light_fade = XdrvMailbox.payload; + break; + case 2: + Settings.light_fade ^= 1; + break; + } + if (!Settings.light_fade) { Light.fade_running = false; } + ResponseCmndStateText(Settings.light_fade); +} + +void CmndSpeed(void) +{ + + + + + if (1 == XdrvMailbox.data_len) { + if (('+' == XdrvMailbox.data[0]) && (Settings.light_speed > 1)) { + XdrvMailbox.payload = Settings.light_speed - 1; + } + else if (('-' == XdrvMailbox.data[0]) && (Settings.light_speed < 40)) { + XdrvMailbox.payload = Settings.light_speed + 1; + } + } + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 40)) { + Settings.light_speed = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.light_speed); +} + +void CmndWakeupDuration(void) +{ + + + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3001)) { + Settings.light_wakeup = XdrvMailbox.payload; + Light.wakeup_active = 0; + } + ResponseCmndNumber(Settings.light_wakeup); +} + +void CmndUndocA(void) +{ + + char scolor[LIGHT_COLOR_SIZE]; + LightGetColor(scolor, true); + scolor[6] = '\0'; + Response_P(PSTR("%s,%d,%d,%d,%d,%d"), scolor, Settings.light_fade, Settings.light_correction, Settings.light_scheme, Settings.light_speed, Settings.light_width); + MqttPublishPrefixTopic_P(STAT, XdrvMailbox.topic); + mqtt_data[0] = '\0'; +} + + + + + +bool Xdrv04(uint8_t function) +{ + bool result = false; + + if (FUNC_MODULE_INIT == function) { + return LightModuleInit(); + } + else if (light_type) { + switch (function) { + case FUNC_SERIAL: + result = XlgtCall(FUNC_SERIAL); + break; + case FUNC_LOOP: + if (Light.fade_running) { + if (LightApplyFade()) { + LightSetOutputs(Light.fade_cur_10); + } + } + break; + case FUNC_EVERY_50_MSECOND: + LightAnimate(); + break; + case FUNC_SET_POWER: + LightSetPower(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kLightCommands, LightCommand); + if (!result) { + result = XlgtCall(FUNC_COMMAND); + } + break; + case FUNC_PRE_INIT: + LightInit(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_05_irremote.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_05_irremote.ino" +#if defined(USE_IR_REMOTE) && !defined(USE_IR_REMOTE_FULL) + + + + +#define XDRV_05 5 + +#include + +enum IrErrors { IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND }; + +const char kIrRemoteCommands[] PROGMEM = "|" D_CMND_IRSEND ; + + +void (* const IrRemoteCommand[])(void) PROGMEM = { + &CmndIrSend }; + + +static const uint8_t MAX_STANDARD_IR = NEC; +const char kIrRemoteProtocols[] PROGMEM = "UNKNOWN|RC5|RC6|NEC"; + + + + + +#include + +IRsend *irsend = nullptr; +bool irsend_active = false; + +void IrSendInit(void) +{ + irsend = new IRsend(pin[GPIO_IRSEND]); + irsend->begin(); +} + +#ifdef USE_IR_RECEIVE + + + + +const bool IR_RCV_SAVE_BUFFER = false; +const uint32_t IR_TIME_AVOID_DUPLICATE = 500; + +#include + +IRrecv *irrecv = nullptr; + +unsigned long ir_lasttime = 0; + +void IrReceiveUpdateThreshold(void) +{ + if (irrecv != nullptr) { + if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + } +} + +void IrReceiveInit(void) +{ + + irrecv = new IRrecv(pin[GPIO_IRRECV], IR_RCV_BUFFER_SIZE, IR_RCV_TIMEOUT, IR_RCV_SAVE_BUFFER); + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + irrecv->enableIRIn(); + + +} + +void IrReceiveCheck(void) +{ + char sirtype[8]; + int8_t iridx = 0; + + decode_results results; + + if (irrecv->decode(&results)) { + char hvalue[65]; + + iridx = results.decode_type; + if ((iridx < 0) || (iridx > MAX_STANDARD_IR)) { iridx = 0; } + + if (iridx) { + if (results.bits > 64) { + + uint32_t digits2 = results.bits / 8; + if (results.bits % 8) { digits2++; } + ToHex_P((unsigned char*)results.state, digits2, hvalue, sizeof(hvalue)); + } else { + Uint64toHex(results.value, hvalue, results.bits); + } + } else { + Uint64toHex(results.value, hvalue, 32); + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value 0x%s, Decode %d"), + irsend_active, results.rawlen, results.overflow, results.bits, hvalue, results.decode_type); + + unsigned long now = millis(); + + if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { + ir_lasttime = now; + + char svalue[64]; + if (Settings.flag.ir_receive_decimal) { + ulltoa(results.value, svalue, 10); + } else { + snprintf_P(svalue, sizeof(svalue), PSTR("\"0x%s\""), hvalue); + } + ResponseTime_P(PSTR(",\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d"), + GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits); + if (iridx) { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_DATA "\":%s"), svalue); + } else { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_HASH "\":%s"), svalue); + } + + if (Settings.flag3.receive_raw) { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":[")); + uint16_t i; + for (i = 1; i < results.rawlen; i++) { + if (i > 1) { ResponseAppend_P(PSTR(",")); } + uint32_t usecs; + for (usecs = results.rawbuf[i] * kRawTick; usecs > UINT16_MAX; usecs -= UINT16_MAX) { + ResponseAppend_P(PSTR("%d,0,"), UINT16_MAX); + } + ResponseAppend_P(PSTR("%d"), usecs); + if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } + } + uint16_t extended_length = results.rawlen - 1; + for (uint32_t j = 0; j < results.rawlen - 1; j++) { + uint32_t usecs = results.rawbuf[j] * kRawTick; + + extended_length += (usecs / (UINT16_MAX + 1)) * 2; + } + ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); + } + + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); + + XdrvRulesProcess(); +#ifdef USE_DOMOTICZ + if (iridx) { + unsigned long value = results.value | (iridx << 28); + DomoticzSensor(DZ_COUNT, value); + } +#endif + } + + irrecv->resume(); + } +} +#endif + + + + + +uint32_t IrRemoteCmndIrSendJson(void) +{ + + + + + char dataBufUc[XdrvMailbox.data_len + 1]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { + return IE_INVALID_JSON; + } + + StaticJsonBuffer<140> jsonBuf; + JsonObject &root = jsonBuf.parseObject(dataBufUc); + if (!root.success()) { + return IE_INVALID_JSON; + } + + + + char parm_uc[10]; + const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))]; + uint16_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))]; + uint64_t data = strtoull(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], nullptr, 0); + uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))]; + + if (XdrvMailbox.index > repeat + 1) { + repeat = XdrvMailbox.index - 1; + } + if (!(protocol && bits)) { + return IE_SYNTAX_IRSEND; + } + + char protocol_text[20]; + int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols); + + char dvalue[64]; + char hvalue[20]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (0x%s), repeat %d, protocol_code %d"), + protocol_text, protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat, protocol_code); + + irsend_active = true; + switch (protocol_code) { +#ifdef USE_IR_SEND_RC5 + case RC5: + irsend->sendRC5(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_RC6 + case RC6: + irsend->sendRC6(data, bits, repeat); break; +#endif +#ifdef USE_IR_SEND_NEC + case NEC: + irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits, repeat); break; +#endif + default: + irsend_active = false; + ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); + } + + return IE_NO_ERROR; +} + +void CmndIrSend(void) +{ + uint8_t error = IE_SYNTAX_IRSEND; + + if (XdrvMailbox.data_len) { + + if (strstr(XdrvMailbox.data, "{") == nullptr) { + error = IE_INVALID_JSON; + } else { + error = IrRemoteCmndIrSendJson(); + } + } + IrRemoteCmndResponse(error); +} + +void IrRemoteCmndResponse(uint32_t error) +{ + switch (error) { + case IE_INVALID_RAWDATA: + ResponseCmndChar(D_JSON_INVALID_RAWDATA); + break; + case IE_INVALID_JSON: + ResponseCmndChar(D_JSON_INVALID_JSON); + break; + case IE_SYNTAX_IRSEND: + Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_PROTOCOL ", " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); + break; + default: + ResponseCmndDone(); + } +} + + + + + +bool Xdrv05(uint8_t function) +{ + bool result = false; + + if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) { + switch (function) { + case FUNC_PRE_INIT: + if (pin[GPIO_IRSEND] < 99) { + IrSendInit(); + } +#ifdef USE_IR_RECEIVE + if (pin[GPIO_IRRECV] < 99) { + IrReceiveInit(); + } +#endif + break; + case FUNC_EVERY_50_MSECOND: +#ifdef USE_IR_RECEIVE + if (pin[GPIO_IRRECV] < 99) { + IrReceiveCheck(); + } +#endif + irsend_active = false; + break; + case FUNC_COMMAND: + if (pin[GPIO_IRSEND] < 99) { + result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); + } + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_05_irremote_full.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_05_irremote_full.ino" +#ifdef USE_IR_REMOTE_FULL + + + + +#define XDRV_05 5 + +#include +#include +#include +#include +#include + +enum IrErrors { IE_RESPONSE_PROVIDED, IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC, + IE_UNSUPPORTED_HVAC, IE_UNSUPPORTED_PROTOCOL }; + +const char kIrRemoteCommands[] PROGMEM = "|" + D_CMND_IRHVAC "|" D_CMND_IRSEND ; + +void (* const IrRemoteCommand[])(void) PROGMEM = { + &CmndIrHvac, &CmndIrSend }; + + + + + +IRsend *irsend = nullptr; +bool irsend_active = false; + +void IrSendInit(void) +{ + irsend = new IRsend(pin[GPIO_IRSEND]); + irsend->begin(); +} + + + +uint8_t reverseBitsInByte(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; +} + + +uint64_t reverseBitsInBytes64(uint64_t b) { + union { + uint8_t b[8]; + uint64_t i; + } a; + a.i = b; + for (uint32_t i=0; i<8; i++) { + a.b[i] = reverseBitsInByte(a.b[i]); + } + return a.i; +} + + + + + +const bool IR_FULL_RCV_SAVE_BUFFER = false; +const uint32_t IR_TIME_AVOID_DUPLICATE = 500; + + + + +const uint16_t IR_FULL_BUFFER_SIZE = 1024; + + + +const uint8_t IR__FULL_RCV_TIMEOUT = 50; + +IRrecv *irrecv = nullptr; + +unsigned long ir_lasttime = 0; + +void IrReceiveUpdateThreshold(void) +{ + if (irrecv != nullptr) { + if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + } +} + +void IrReceiveInit(void) +{ + + irrecv = new IRrecv(pin[GPIO_IRRECV], IR_FULL_BUFFER_SIZE, IR__FULL_RCV_TIMEOUT, IR_FULL_RCV_SAVE_BUFFER); + irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); + irrecv->enableIRIn(); +} + +String sendACJsonState(const stdAc::state_t &state) { + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + json[D_JSON_IRHVAC_VENDOR] = typeToString(state.protocol); + json[D_JSON_IRHVAC_MODEL] = state.model; + json[D_JSON_IRHVAC_POWER] = IRac::boolToString(state.power); + json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(state.mode); + + if (state.mode == stdAc::opmode_t::kOff || !state.power) { + json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(stdAc::opmode_t::kOff); + json[D_JSON_IRHVAC_POWER] = IRac::boolToString(false); + } + json[D_JSON_IRHVAC_CELSIUS] = IRac::boolToString(state.celsius); + if (floorf(state.degrees) == state.degrees) { + json[D_JSON_IRHVAC_TEMP] = floorf(state.degrees); + } else { + json[D_JSON_IRHVAC_TEMP] = RawJson(String(state.degrees, 1)); + } + json[D_JSON_IRHVAC_FANSPEED] = IRac::fanspeedToString(state.fanspeed); + json[D_JSON_IRHVAC_SWINGV] = IRac::swingvToString(state.swingv); + json[D_JSON_IRHVAC_SWINGH] = IRac::swinghToString(state.swingh); + json[D_JSON_IRHVAC_QUIET] = IRac::boolToString(state.quiet); + json[D_JSON_IRHVAC_TURBO] = IRac::boolToString(state.turbo); + json[D_JSON_IRHVAC_ECONO] = IRac::boolToString(state.econo); + json[D_JSON_IRHVAC_LIGHT] = IRac::boolToString(state.light); + json[D_JSON_IRHVAC_FILTER] = IRac::boolToString(state.filter); + json[D_JSON_IRHVAC_CLEAN] = IRac::boolToString(state.clean); + json[D_JSON_IRHVAC_BEEP] = IRac::boolToString(state.beep); + json[D_JSON_IRHVAC_SLEEP] = state.sleep; + + String payload = ""; + payload.reserve(200); + json.printTo(payload); + return payload; +} + +String sendIRJsonState(const struct decode_results &results) { + String json("{"); + json += "\"" D_JSON_IR_PROTOCOL "\":\""; + json += typeToString(results.decode_type); + json += "\",\"" D_JSON_IR_BITS "\":"; + json += results.bits; + + if (hasACState(results.decode_type)) { + json += ",\"" D_JSON_IR_DATA "\":\"0x"; + json += resultToHexidecimal(&results); + json += "\""; + } else { + if (UNKNOWN != results.decode_type) { + json += ",\"" D_JSON_IR_DATA "\":"; + } else { + json += ",\"" D_JSON_IR_HASH "\":"; + } + if (Settings.flag.ir_receive_decimal) { + char svalue[32]; + ulltoa(results.value, svalue, 10); + json += svalue; + } else { + char hvalue[64]; + if (UNKNOWN != results.decode_type) { + Uint64toHex(results.value, hvalue, results.bits); + json += "\"0x"; + json += hvalue; + json += "\",\"" D_JSON_IR_DATALSB "\":\"0x"; + Uint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); + json += hvalue; + json += "\""; + } else { + Uint64toHex(results.value, hvalue, 32); + json += "\"0x"; + json += hvalue; + json += "\""; + } + } + } + json += ",\"" D_JSON_IR_REPEAT "\":"; + json += results.repeat; + + stdAc::state_t ac_result; + if (IRAcUtils::decodeToState(&results, &ac_result, nullptr)) { + + json += ",\"" D_CMND_IRHVAC "\":"; + json += sendACJsonState(ac_result); + } + + return json; +} + +void IrReceiveCheck(void) +{ + decode_results results; + + if (irrecv->decode(&results)) { + uint32_t now = millis(); + + + if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { + ir_lasttime = now; + Response_P(PSTR("{\"" D_JSON_IRRECEIVED "\":%s"), sendIRJsonState(results).c_str()); + + if (Settings.flag3.receive_raw) { + ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":[")); + uint16_t i; + for (i = 1; i < results.rawlen; i++) { + if (i > 1) { ResponseAppend_P(PSTR(",")); } + uint32_t usecs; + for (usecs = results.rawbuf[i] * kRawTick; usecs > UINT16_MAX; usecs -= UINT16_MAX) { + ResponseAppend_P(PSTR("%d,0,"), UINT16_MAX); + } + ResponseAppend_P(PSTR("%d"), usecs); + if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } + } + uint16_t extended_length = results.rawlen - 1; + for (uint32_t j = 0; j < results.rawlen - 1; j++) { + uint32_t usecs = results.rawbuf[j] * kRawTick; + + extended_length += (usecs / (UINT16_MAX + 1)) * 2; + } + ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); + } + + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); + + XdrvRulesProcess(); + } + + irrecv->resume(); + } +} + + + + + + + +String listSupportedProtocols(bool hvac) { + String l(""); + bool first = true; + for (uint32_t i = UNUSED + 1; i <= kLastDecodeType; i++) { + bool found = false; + if (hvac) { + found = IRac::isProtocolSupported((decode_type_t)i); + } else { + found = (IRsend::defaultBits((decode_type_t)i) > 0) && (!IRac::isProtocolSupported((decode_type_t)i)); + } + if (found) { + if (first) { + first = false; + } else { + l += "|"; + } + l += typeToString((decode_type_t)i); + } + } + return l; +} + + +const stdAc::fanspeed_t IrHvacFanSpeed[] PROGMEM = { stdAc::fanspeed_t::kAuto, + stdAc::fanspeed_t::kMin, stdAc::fanspeed_t::kLow,stdAc::fanspeed_t::kMedium, + stdAc::fanspeed_t::kHigh, stdAc::fanspeed_t::kMax }; + +uint32_t IrRemoteCmndIrHvacJson(void) +{ + stdAc::state_t state, prev; + char parm_uc[12]; + + + char dataBufUc[XdrvMailbox.data_len + 1]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } + + DynamicJsonBuffer jsonBuf; + JsonObject &json = jsonBuf.parseObject(dataBufUc); + if (!json.success()) { return IE_INVALID_JSON; } + + + state.protocol = decode_type_t::UNKNOWN; + state.model = 1; + state.mode = stdAc::opmode_t::kAuto; + state.power = false; + state.celsius = true; + state.degrees = 21.0f; + state.fanspeed = stdAc::fanspeed_t::kMedium; + state.swingv = stdAc::swingv_t::kOff; + state.swingh = stdAc::swingh_t::kOff; + state.light = false; + state.beep = false; + state.econo = false; + state.filter = false; + state.turbo = false; + state.quiet = false; + state.sleep = -1; + state.clean = false; + state.clock = -1; + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); + if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); + if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } + if (decode_type_t::UNKNOWN == state.protocol) { return IE_UNSUPPORTED_HVAC; } + if (!IRac::isProtocolSupported(state.protocol)) { return IE_UNSUPPORTED_HVAC; } + + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FANSPEED)); + if (json.containsKey(parm_uc)) { + uint32_t fan_speed = json[parm_uc]; + if ((fan_speed >= 1) && (fan_speed <= 5)) { + state.fanspeed = (stdAc::fanspeed_t) pgm_read_byte(&IrHvacFanSpeed[fan_speed]); + } else { + state.fanspeed = IRac::strToFanspeed(json[parm_uc]); + } + } + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODEL)); + if (json.containsKey(parm_uc)) { state.model = IRac::strToModel(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODE)); + if (json.containsKey(parm_uc)) { state.mode = IRac::strToOpmode(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SWINGV)); + if (json.containsKey(parm_uc)) { state.swingv = IRac::strToSwingV(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SWINGH)); + if (json.containsKey(parm_uc)) { state.swingh = IRac::strToSwingH(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TEMP)); + if (json.containsKey(parm_uc)) { state.degrees = json[parm_uc]; } + + + + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_POWER)); + if (json.containsKey(parm_uc)) { state.power = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_CELSIUS)); + if (json.containsKey(parm_uc)) { state.celsius = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_LIGHT)); + if (json.containsKey(parm_uc)) { state.light = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_BEEP)); + if (json.containsKey(parm_uc)) { state.beep = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_ECONO)); + if (json.containsKey(parm_uc)) { state.econo = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FILTER)); + if (json.containsKey(parm_uc)) { state.filter = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TURBO)); + if (json.containsKey(parm_uc)) { state.turbo = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_QUIET)); + if (json.containsKey(parm_uc)) { state.quiet = IRac::strToBool(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_CLEAN)); + if (json.containsKey(parm_uc)) { state.clean = IRac::strToBool(json[parm_uc]); } + + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SLEEP)); + if (json[parm_uc]) { state.sleep = json[parm_uc]; } + + + IRac ac(pin[GPIO_IRSEND]); + bool success = ac.sendAc(state, &prev); + if (!success) { return IE_SYNTAX_IRHVAC; } + + Response_P(PSTR("{\"" D_CMND_IRHVAC "\":%s}"), sendACJsonState(state).c_str()); + return IE_RESPONSE_PROVIDED; +} + +void CmndIrHvac(void) +{ + uint8_t error = IE_SYNTAX_IRHVAC; + + if (XdrvMailbox.data_len) { + error = IrRemoteCmndIrHvacJson(); + } + if (error != IE_RESPONSE_PROVIDED) { IrRemoteCmndResponse(error); } +} + + + + + +uint32_t IrRemoteCmndIrSendJson(void) +{ + char parm_uc[12]; + + + + char dataBufUc[XdrvMailbox.data_len + 1]; + UpperCase(dataBufUc, XdrvMailbox.data); + RemoveSpace(dataBufUc); + if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } + + DynamicJsonBuffer jsonBuf; + JsonObject &json = jsonBuf.parseObject(dataBufUc); + if (!json.success()) { return IE_INVALID_JSON; } + + + + decode_type_t protocol = decode_type_t::UNKNOWN; + uint16_t bits = 0; + uint64_t data; + uint8_t repeat = 0; + + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); + if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } + UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); + if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } + if (decode_type_t::UNKNOWN == protocol) { return IE_UNSUPPORTED_PROTOCOL; } + + UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS)); + if (json.containsKey(parm_uc)) { bits = json[parm_uc]; } + UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT)); + if (json.containsKey(parm_uc)) { repeat = json[parm_uc]; } + UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATALSB)); + if (json.containsKey(parm_uc)) { data = reverseBitsInBytes64(strtoull(json[parm_uc], nullptr, 0)); } + UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA)); + if (json.containsKey(parm_uc)) { data = strtoull(json[parm_uc], nullptr, 0); } + if (0 == bits) { return IE_SYNTAX_IRSEND; } + + + if (XdrvMailbox.index > repeat + 1) { repeat = XdrvMailbox.index - 1; } + + char dvalue[32]; + char hvalue[32]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol %d, bits %d, data 0x%s (%s), repeat %d"), + protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat); + + irsend_active = true; + bool success = irsend->send(protocol, data, bits, repeat); + + if (!success) { + irsend_active = false; + ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); + } + return IE_NO_ERROR; +} + +uint32_t IrRemoteCmndIrSendRaw(void) +{ + + + + + + + + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + if (p == nullptr) { + return IE_INVALID_RAWDATA; + } + + + uint16_t repeat = XdrvMailbox.index > 0 ? XdrvMailbox.index - 1 : 0; + + uint16_t freq = atoi(str); + if (!freq && (*str != '0')) { + uint16_t count = 0; + char *q = p; + for (; *q; count += (*q++ == ',')); + if (count < 2) { + return IE_INVALID_RAWDATA; + } + + uint16_t parm[count]; + for (uint32_t i = 0; i < count; i++) { + parm[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); + if (!parm[i]) { + if (!i) { + parm[0] = 38000; + } else { + return IE_INVALID_RAWDATA; + } + } + } + + uint16_t i = 0; + if (count < 4) { + + uint16_t mark = parm[1] *2; + if (3 == count) { + if (parm[2] < parm[1]) { + + mark = parm[1] * parm[2]; + } else { + + mark = parm[2]; + } + } + uint16_t raw_array[strlen(p)]; + for (; *p; *p++) { + if (*p == '0') { + raw_array[i++] = parm[1]; + } + else if (*p == '1') { + raw_array[i++] = mark; + } + } + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, i, parm[0]); + if (r < repeat) { + irsend->space(40000); + } + } + } + else if (6 == count) { + + uint16_t raw_array[strlen(p)*2+3]; + raw_array[i++] = parm[1]; + raw_array[i++] = parm[2]; + uint32_t inter_message_32 = (parm[1] + parm[2]) * 3; + uint16_t inter_message = (inter_message_32 > 65000) ? 65000 : inter_message_32; + for (; *p; *p++) { + if (*p == '0') { + raw_array[i++] = parm[3]; + raw_array[i++] = parm[4]; + } + else if (*p == '1') { + raw_array[i++] = parm[3]; + raw_array[i++] = parm[5]; + } + } + raw_array[i++] = parm[3]; + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, i, parm[0]); + if (r < repeat) { + irsend->space(inter_message); + } + } + } + else { + return IE_INVALID_RAWDATA; + } + } else { + if (!freq) { freq = 38000; } + uint16_t count = 0; + char *q = p; + for (; *q; count += (*q++ == ',')); + if (0 == count) { + return IE_INVALID_RAWDATA; + } + + + count++; + if (count < 200) { + uint16_t raw_array[count]; + for (uint32_t i = 0; i < count; i++) { + raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); + } + + + + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, count, freq); + } + } else { + uint16_t *raw_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); + if (raw_array == nullptr) { + return IE_INVALID_RAWDATA; + } + + for (uint32_t i = 0; i < count; i++) { + raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); + } + + + + irsend_active = true; + for (uint32_t r = 0; r <= repeat; r++) { + irsend->sendRaw(raw_array, count, freq); + } + free(raw_array); + } + } + + return IE_NO_ERROR; +} + +void CmndIrSend(void) +{ + uint8_t error = IE_SYNTAX_IRSEND; + + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, "{") == nullptr) { + error = IrRemoteCmndIrSendRaw(); + } else { + error = IrRemoteCmndIrSendJson(); + } + } + IrRemoteCmndResponse(error); +} + +void IrRemoteCmndResponse(uint32_t error) +{ + switch (error) { + case IE_INVALID_RAWDATA: + ResponseCmndChar(D_JSON_INVALID_RAWDATA); + break; + case IE_INVALID_JSON: + ResponseCmndChar(D_JSON_INVALID_JSON); + break; + case IE_SYNTAX_IRSEND: + Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); + break; + case IE_SYNTAX_IRHVAC: + Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR ", " D_JSON_IRHVAC_MODE " " D_JSON_OR " " D_JSON_IRHVAC_FANSPEED "\"}")); + break; + case IE_UNSUPPORTED_HVAC: + Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR " (%s)\"}"), listSupportedProtocols(true).c_str()); + break; + case IE_UNSUPPORTED_PROTOCOL: + Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_PROTOCOL " (%s)\"}"), listSupportedProtocols(false).c_str()); + break; + default: + ResponseCmndDone(); + } +} + + + + + +bool Xdrv05(uint8_t function) +{ + bool result = false; + + if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) { + switch (function) { + case FUNC_PRE_INIT: + if (pin[GPIO_IRSEND] < 99) { + IrSendInit(); + } + if (pin[GPIO_IRRECV] < 99) { + IrReceiveInit(); + } + break; + case FUNC_EVERY_50_MSECOND: + if (pin[GPIO_IRRECV] < 99) { + IrReceiveCheck(); + } + irsend_active = false; + break; + case FUNC_COMMAND: + if (pin[GPIO_IRSEND] < 99) { + result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); + } + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_06_snfbridge.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_06_snfbridge.ino" +#ifdef USE_SONOFF_RF + + + + +#define XDRV_06 6 + +const uint32_t SFB_TIME_AVOID_DUPLICATE = 2000; + +enum SonoffBridgeCommands { + CMND_RFSYNC, CMND_RFLOW, CMND_RFHIGH, CMND_RFHOST, CMND_RFCODE }; + +const char kSonoffBridgeCommands[] PROGMEM = "|" + D_CMND_RFSYNC "|" D_CMND_RFLOW "|" D_CMND_RFHIGH "|" D_CMND_RFHOST "|" D_CMND_RFCODE "|" D_CMND_RFKEY "|" D_CMND_RFRAW; + +void (* const SonoffBridgeCommand[])(void) PROGMEM = { + &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfKey, &CmndRfRaw }; + +struct SONOFFBRIDGE { + uint32_t last_received_id = 0; + uint32_t last_send_code = 0; + uint32_t last_time = 0; + uint32_t last_learn_time = 0; + uint8_t receive_flag = 0; + uint8_t receive_raw_flag = 0; + uint8_t learn_key = 1; + uint8_t learn_active = 0; + uint8_t expected_bytes = 0; +} SnfBridge; + +#ifdef USE_RF_FLASH + + + + + + + +#include "ihx.h" +#include "c2.h" + +const ssize_t RF_RECORD_NO_START_FOUND = -1; +const ssize_t RF_RECORD_NO_END_FOUND = -2; + +ssize_t rf_find_hex_record_start(uint8_t *buf, size_t size) +{ + for (size_t i = 0; i < size; i++) { + if (buf[i] == ':') { + return i; + } + } + return RF_RECORD_NO_START_FOUND; +} + +ssize_t rf_find_hex_record_end(uint8_t *buf, size_t size) +{ + for (size_t i = 0; i < size; i++) { + if (buf[i] == '\n') { + return i; + } + } + return RF_RECORD_NO_END_FOUND; +} + +ssize_t rf_glue_remnant_with_new_data_and_write(const uint8_t *remnant_data, uint8_t *new_data, size_t new_data_len) +{ + ssize_t record_start; + ssize_t record_end; + ssize_t glue_record_sz; + uint8_t *glue_buf; + ssize_t result; + + if (remnant_data[0] != ':') { return -8; } + + + record_end = rf_find_hex_record_end(new_data, new_data_len); + record_start = rf_find_hex_record_start(new_data, new_data_len); + + + + + if ((record_start != RF_RECORD_NO_START_FOUND) && (record_start < record_end)) { + return -8; + } + + glue_record_sz = strlen((const char *) remnant_data) + record_end; + + glue_buf = (uint8_t *) malloc(glue_record_sz); + if (glue_buf == nullptr) { return -2; } + + + memcpy(glue_buf, remnant_data, strlen((const char *) remnant_data)); + memcpy(glue_buf + strlen((const char *) remnant_data), new_data, record_end); + + result = rf_decode_and_write(glue_buf, glue_record_sz); + free(glue_buf); + return result; +} + +ssize_t rf_decode_and_write(uint8_t *record, size_t size) +{ + uint8_t err = ihx_decode(record, size); + if (err != IHX_SUCCESS) { return -13; } + + ihx_t *h = (ihx_t *) record; + if (h->record_type == IHX_RT_DATA) { + int retries = 5; + uint16_t address = h->address_high * 0x100 + h->address_low; + + do { + err = c2_programming_init(); + err = c2_block_write(address, h->data, h->len); + } while (err != C2_SUCCESS && retries--); + } else if (h->record_type == IHX_RT_END_OF_FILE) { + + err = c2_reset(); + } + + if (err != C2_SUCCESS) { return -12; } + + return 0; +} + +ssize_t rf_search_and_write(uint8_t *buf, size_t size) +{ + + ssize_t rec_end; + ssize_t rec_start; + ssize_t err; + + for (size_t i = 0; i < size; i++) { + + rec_start = rf_find_hex_record_start(buf + i, size - i); + if (rec_start == RF_RECORD_NO_START_FOUND) { + + return -8; + } + + + rec_start += i; + rec_end = rf_find_hex_record_end(buf + rec_start, size - rec_start); + if (rec_end == RF_RECORD_NO_END_FOUND) { + + return rec_start; + } + + + rec_end += rec_start; + + err = rf_decode_and_write(buf + rec_start, rec_end - rec_start); + if (err < 0) { return err; } + i = rec_end; + } + + return 0; +} + +uint8_t rf_erase_flash(void) +{ + uint8_t err; + + for (uint32_t i = 0; i < 4; i++) { + err = c2_programming_init(); + if (err != C2_SUCCESS) { + return 10; + } + err = c2_device_erase(); + if (err != C2_SUCCESS) { + if (i < 3) { + c2_reset(); + } else { + return 11; + } + } else { + break; + } + } + return 0; +} + +uint8_t SnfBrUpdateInit(void) +{ + pinMode(PIN_C2CK, OUTPUT); + pinMode(PIN_C2D, INPUT); + + return rf_erase_flash(); +} +#endif + + + +void SonoffBridgeReceivedRaw(void) +{ + + uint8_t buckets = 0; + + if (0xB1 == serial_in_buffer[1]) { buckets = serial_in_buffer[2] << 1; } + + ResponseTime_P(PSTR(",\"" D_CMND_RFRAW "\":{\"" D_JSON_DATA "\":\"")); + for (uint32_t i = 0; i < serial_in_byte_counter; i++) { + ResponseAppend_P(PSTR("%02X"), serial_in_buffer[i]); + if (0xB1 == serial_in_buffer[1]) { + if ((i > 3) && buckets) { buckets--; } + if ((i < 3) || (buckets % 2) || (i == serial_in_byte_counter -2)) { + ResponseAppend_P(PSTR(" ")); + } + } + } + ResponseAppend_P(PSTR("\"}}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_CMND_RFRAW)); + + XdrvRulesProcess(); +} + + + +void SonoffBridgeLearnFailed(void) +{ + SnfBridge.learn_active = 0; + Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, SnfBridge.learn_key, D_JSON_LEARN_FAILED); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY)); +} + +void SonoffBridgeReceived(void) +{ + uint16_t sync_time = 0; + uint16_t low_time = 0; + uint16_t high_time = 0; + uint32_t received_id = 0; + char rfkey[8]; + char stemp[16]; + + AddLogSerial(LOG_LEVEL_DEBUG); + + if (0xA2 == serial_in_buffer[0]) { + SonoffBridgeLearnFailed(); + } + else if (0xA3 == serial_in_buffer[0]) { + SnfBridge.learn_active = 0; + low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; + high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; + if (low_time && high_time) { + for (uint32_t i = 0; i < 9; i++) { + Settings.rf_code[SnfBridge.learn_key][i] = serial_in_buffer[i +1]; + } + Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, SnfBridge.learn_key, D_JSON_LEARNED); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY)); + } else { + SonoffBridgeLearnFailed(); + } + } + else if (0xA4 == serial_in_buffer[0]) { + if (SnfBridge.learn_active) { + SonoffBridgeLearnFailed(); + } else { + sync_time = serial_in_buffer[1] << 8 | serial_in_buffer[2]; + low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; + high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; + received_id = serial_in_buffer[7] << 16 | serial_in_buffer[8] << 8 | serial_in_buffer[9]; + + unsigned long now = millis(); + if (!((received_id == SnfBridge.last_received_id) && (now - SnfBridge.last_time < SFB_TIME_AVOID_DUPLICATE))) { + SnfBridge.last_received_id = received_id; + SnfBridge.last_time = now; + strncpy_P(rfkey, PSTR("\"" D_JSON_NONE "\""), sizeof(rfkey)); + for (uint32_t i = 1; i <= 16; i++) { + if (Settings.rf_code[i][0]) { + uint32_t send_id = Settings.rf_code[i][6] << 16 | Settings.rf_code[i][7] << 8 | Settings.rf_code[i][8]; + if (send_id == received_id) { + snprintf_P(rfkey, sizeof(rfkey), PSTR("%d"), i); + break; + } + } + } + if (Settings.flag.rf_receive_decimal) { + snprintf_P(stemp, sizeof(stemp), PSTR("%u"), received_id); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"%06X\""), received_id); + } + ResponseTime_P(PSTR(",\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":%s,\"" D_CMND_RFKEY "\":%s}}"), + sync_time, low_time, high_time, stemp, rfkey); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED)); + XdrvRulesProcess(); + #ifdef USE_DOMOTICZ + DomoticzSensor(DZ_COUNT, received_id); + #endif + } + } + } +} + +bool SonoffBridgeSerialInput(void) +{ + + static int8_t receive_len = 0; + + if (SnfBridge.receive_flag) { + if (SnfBridge.receive_raw_flag) { + if (!serial_in_byte_counter) { + serial_in_buffer[serial_in_byte_counter++] = 0xAA; + } + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if (serial_in_byte_counter == 3) { + if ((0xA6 == serial_in_buffer[1]) || (0xAB == serial_in_buffer[1])) { + receive_len = serial_in_buffer[2] + 4; + } + } + if ((!receive_len && (0x55 == serial_in_byte)) || (receive_len && (serial_in_byte_counter == receive_len))) { + SonoffBridgeReceivedRaw(); + SnfBridge.receive_flag = 0; + return 1; + } + } + else if (!((0 == serial_in_byte_counter) && (0 == serial_in_byte))) { + if (0 == serial_in_byte_counter) { + SnfBridge.expected_bytes = 2; + if (serial_in_byte >= 0xA3) { + SnfBridge.expected_bytes = 11; + } + if (serial_in_byte == 0xA6) { + SnfBridge.expected_bytes = 0; + serial_in_buffer[serial_in_byte_counter++] = 0xAA; + SnfBridge.receive_raw_flag = 1; + } + } + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if ((SnfBridge.expected_bytes == serial_in_byte_counter) && (0x55 == serial_in_byte)) { + SonoffBridgeReceived(); + SnfBridge.receive_flag = 0; + return 1; + } + } + serial_in_byte = 0; + } + if (0xAA == serial_in_byte) { + serial_in_byte_counter = 0; + serial_in_byte = 0; + SnfBridge.receive_flag = 1; + receive_len = 0; + } + return 0; +} + +void SonoffBridgeSendCommand(uint8_t code) +{ + Serial.write(0xAA); + Serial.write(code); + Serial.write(0x55); +} + +void SonoffBridgeSendAck(void) +{ + Serial.write(0xAA); + Serial.write(0xA0); + Serial.write(0x55); +} + +void SonoffBridgeSendCode(uint32_t code) +{ + Serial.write(0xAA); + Serial.write(0xA5); + for (uint32_t i = 0; i < 6; i++) { + Serial.write(Settings.rf_code[0][i]); + } + Serial.write((code >> 16) & 0xff); + Serial.write((code >> 8) & 0xff); + Serial.write(code & 0xff); + Serial.write(0x55); + Serial.flush(); +} + +void SonoffBridgeSend(uint8_t idx, uint8_t key) +{ + uint8_t code; + + key--; + Serial.write(0xAA); + Serial.write(0xA5); + for (uint32_t i = 0; i < 8; i++) { + Serial.write(Settings.rf_code[idx][i]); + } + if (0 == idx) { + code = (0x10 << (key >> 2)) | (1 << (key & 3)); + } else { + code = Settings.rf_code[idx][8]; + } + Serial.write(code); + Serial.write(0x55); + Serial.flush(); +#ifdef USE_DOMOTICZ + + +#endif +} + +void SonoffBridgeLearn(uint8_t key) +{ + SnfBridge.learn_key = key; + SnfBridge.learn_active = 1; + SnfBridge.last_learn_time = millis(); + Serial.write(0xAA); + Serial.write(0xA1); + Serial.write(0x55); +} + + + + + +void CmndRfBridge(void) +{ + char *p; + char stemp [10]; + uint32_t code = 0; + uint8_t radix = 10; + + uint32_t set_index = XdrvMailbox.command_code *2; + + if (XdrvMailbox.data[0] == '#') { + XdrvMailbox.data++; + XdrvMailbox.data_len--; + radix = 16; + } + + if (XdrvMailbox.data_len) { + code = strtol(XdrvMailbox.data, &p, radix); + if (code) { + if (CMND_RFCODE == XdrvMailbox.command_code) { + SnfBridge.last_send_code = code; + SonoffBridgeSendCode(code); + } else { + if (1 == XdrvMailbox.payload) { + code = pgm_read_byte(kDefaultRfCode + set_index) << 8 | pgm_read_byte(kDefaultRfCode + set_index +1); + } + uint8_t msb = code >> 8; + uint8_t lsb = code & 0xFF; + if ((code > 0) && (code < 0x7FFF) && (msb != 0x55) && (lsb != 0x55)) { + Settings.rf_code[0][set_index] = msb; + Settings.rf_code[0][set_index +1] = lsb; + } + } + } + } + if (CMND_RFCODE == XdrvMailbox.command_code) { + code = SnfBridge.last_send_code; + } else { + code = Settings.rf_code[0][set_index] << 8 | Settings.rf_code[0][set_index +1]; + } + if (10 == radix) { + snprintf_P(stemp, sizeof(stemp), PSTR("%d"), code); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"#%06X\""), code); + } + Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, stemp); +} + +void CmndRfKey(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 16)) { + unsigned long now = millis(); + if ((!SnfBridge.learn_active) || (now - SnfBridge.last_learn_time > 60100)) { + SnfBridge.learn_active = 0; + if (2 == XdrvMailbox.payload) { + SonoffBridgeLearn(XdrvMailbox.index); + ResponseCmndIdxChar(D_JSON_START_LEARNING); + } + else if (3 == XdrvMailbox.payload) { + Settings.rf_code[XdrvMailbox.index][0] = 0; + ResponseCmndIdxChar(D_JSON_SET_TO_DEFAULT); + } + else if (4 == XdrvMailbox.payload) { + for (uint32_t i = 0; i < 6; i++) { + Settings.rf_code[XdrvMailbox.index][i] = Settings.rf_code[0][i]; + } + Settings.rf_code[XdrvMailbox.index][6] = (SnfBridge.last_send_code >> 16) & 0xff; + Settings.rf_code[XdrvMailbox.index][7] = (SnfBridge.last_send_code >> 8) & 0xff; + Settings.rf_code[XdrvMailbox.index][8] = SnfBridge.last_send_code & 0xff; + ResponseCmndIdxChar(D_JSON_SAVED); + } else if (5 == XdrvMailbox.payload) { + uint8_t key = XdrvMailbox.index; + uint8_t index = (0 == Settings.rf_code[key][0]) ? 0 : key; + uint16_t sync_time = (Settings.rf_code[index][0] << 8) | Settings.rf_code[index][1]; + uint16_t low_time = (Settings.rf_code[index][2] << 8) | Settings.rf_code[index][3]; + uint16_t high_time = (Settings.rf_code[index][4] << 8) | Settings.rf_code[index][5]; + uint32_t code = (Settings.rf_code[index][6] << 16) | (Settings.rf_code[index][7] << 8); + if (0 == index) { + key--; + code |= (uint8_t)((0x10 << (key >> 2)) | (1 << (key & 3))); + } else { + code |= Settings.rf_code[index][8]; + } + Response_P(PSTR("{\"%s%d\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":\"%06X\"}}"), + XdrvMailbox.command, XdrvMailbox.index, sync_time, low_time, high_time, code); + } else { + if ((1 == XdrvMailbox.payload) || (0 == Settings.rf_code[XdrvMailbox.index][0])) { + SonoffBridgeSend(0, XdrvMailbox.index); + ResponseCmndIdxChar(D_JSON_DEFAULT_SENT); + } else { + SonoffBridgeSend(XdrvMailbox.index, 0); + ResponseCmndIdxChar(D_JSON_LEARNED_SENT); + } + } + } else { + Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, SnfBridge.learn_key, D_JSON_LEARNING_ACTIVE); + } + } +} + +void CmndRfRaw(void) +{ + if (XdrvMailbox.data_len) { + if (XdrvMailbox.data_len < 6) { + switch (XdrvMailbox.payload) { + case 0: + SonoffBridgeSendCommand(0xA7); + case 1: + SnfBridge.receive_raw_flag = XdrvMailbox.payload; + break; + case 166: + case 167: + case 169: + case 176: + case 177: + case 255: + SonoffBridgeSendCommand(XdrvMailbox.payload); + SnfBridge.receive_raw_flag = 1; + break; + case 192: + char beep[] = "AAC000C055\0"; + SerialSendRaw(beep); + break; + } + } else { + SerialSendRaw(RemoveSpace(XdrvMailbox.data)); + SnfBridge.receive_raw_flag = 1; + } + } + ResponseCmndStateText(SnfBridge.receive_raw_flag); +} + + + + + +bool Xdrv06(uint8_t function) +{ + bool result = false; + + if (SONOFF_BRIDGE == my_module_type) { + switch (function) { + case FUNC_SERIAL: + result = SonoffBridgeSerialInput(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kSonoffBridgeCommands, SonoffBridgeCommand); + break; + case FUNC_INIT: + SnfBridge.receive_raw_flag = 0; + SonoffBridgeSendCommand(0xA7); + break; + case FUNC_PRE_INIT: + SetSerial(19200, TS_SERIAL_8N1); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_07_domoticz.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_07_domoticz.ino" +#ifdef USE_DOMOTICZ + +#define XDRV_07 7 + +#define D_PRFX_DOMOTICZ "Domoticz" +#define D_CMND_IDX "Idx" +#define D_CMND_KEYIDX "KeyIdx" +#define D_CMND_SWITCHIDX "SwitchIdx" +#define D_CMND_SENSORIDX "SensorIdx" +#define D_CMND_UPDATETIMER "UpdateTimer" + +const char kDomoticzCommands[] PROGMEM = D_PRFX_DOMOTICZ "|" + D_CMND_IDX "|" D_CMND_KEYIDX "|" D_CMND_SWITCHIDX "|" D_CMND_SENSORIDX "|" D_CMND_UPDATETIMER ; + +void (* const DomoticzCommand[])(void) PROGMEM = { + &CmndDomoticzIdx, &CmndDomoticzKeyIdx, &CmndDomoticzSwitchIdx, &CmndDomoticzSensorIdx, &CmndDomoticzUpdateTimer }; + +const char DOMOTICZ_MESSAGE[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\",\"Battery\":%d,\"RSSI\":%d}"; + +#if MAX_DOMOTICZ_SNS_IDX < DZ_MAX_SENSORS + #error "Domoticz: Too many sensors or change settings.h layout" +#endif + +const char kDomoticzSensors[] PROGMEM = + D_DOMOTICZ_TEMP "|" D_DOMOTICZ_TEMP_HUM "|" D_DOMOTICZ_TEMP_HUM_BARO "|" D_DOMOTICZ_POWER_ENERGY "|" D_DOMOTICZ_ILLUMINANCE "|" + D_DOMOTICZ_COUNT "|" D_DOMOTICZ_VOLTAGE "|" D_DOMOTICZ_CURRENT "|" D_DOMOTICZ_AIRQUALITY "|" D_DOMOTICZ_P1_SMART_METER "|" D_DOMOTICZ_SHUTTER ; + +char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; + +int domoticz_update_timer = 0; +uint32_t domoticz_fan_debounce = 0; +bool domoticz_subscribe = false; +bool domoticz_update_flag = true; + +#ifdef USE_SHUTTER +bool domoticz_is_shutter = false; +#endif + +int DomoticzBatteryQuality(void) +{ + + + + + int quality = 100; + +#ifdef USE_ADC_VCC + uint16_t voltage = ESP.getVcc(); + if (voltage <= 2600) { + quality = 0; + } else if (voltage >= 4600) { + quality = 200; + } else { + quality = (voltage - 2600) / 10; + } +#endif + return quality; +} + +int DomoticzRssiQuality(void) +{ + + + return WifiGetRssiAsQuality(WiFi.RSSI()) / 10; +} + +#ifdef USE_SONOFF_IFAN +void MqttPublishDomoticzFanState(void) +{ + if (Settings.flag.mqtt_enabled && Settings.domoticz_relay_idx[1]) { + char svalue[8]; + + int fan_speed = GetFanspeed(); + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fan_speed * 10); + Response_P(DOMOTICZ_MESSAGE, (int)Settings.domoticz_relay_idx[1], (0 == fan_speed) ? 0 : 2, svalue, DomoticzBatteryQuality(), DomoticzRssiQuality()); + MqttPublish(domoticz_in_topic); + + domoticz_fan_debounce = millis(); + } +} + +void DomoticzUpdateFanState(void) +{ + if (domoticz_update_flag) { + MqttPublishDomoticzFanState(); + } + domoticz_update_flag = true; +} +#endif + +void MqttPublishDomoticzPowerState(uint8_t device) +{ + if (Settings.flag.mqtt_enabled) { + if ((device < 1) || (device > devices_present)) { device = 1; } + if (Settings.domoticz_relay_idx[device -1]) { +#ifdef USE_SHUTTER + if (domoticz_is_shutter) { + + } else { +#endif +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (device > 1)) { + + } else { +#endif + char svalue[8]; + + snprintf_P(svalue, sizeof(svalue), PSTR("%d"), Settings.light_dimmer); + Response_P(DOMOTICZ_MESSAGE, (int)Settings.domoticz_relay_idx[device -1], (power & (1 << (device -1))) ? 1 : 0, (light_type) ? svalue : "", DomoticzBatteryQuality(), DomoticzRssiQuality()); + MqttPublish(domoticz_in_topic); +#ifdef USE_SONOFF_IFAN + } +#endif +#ifdef USE_SHUTTER + } +#endif + } + } +} + +void DomoticzUpdatePowerState(uint8_t device) +{ + if (domoticz_update_flag) { + MqttPublishDomoticzPowerState(device); + } + domoticz_update_flag = true; +} + +void DomoticzMqttUpdate(void) +{ + if (domoticz_subscribe && (Settings.domoticz_update_timer || domoticz_update_timer)) { + domoticz_update_timer--; + if (domoticz_update_timer <= 0) { + domoticz_update_timer = Settings.domoticz_update_timer; + for (uint32_t i = 1; i <= devices_present; i++) { +#ifdef USE_SHUTTER + if (domoticz_is_shutter) + { + + break; + } +#endif +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (i > 1)) { + MqttPublishDomoticzFanState(); + break; + } else { +#endif + MqttPublishDomoticzPowerState(i); +#ifdef USE_SONOFF_IFAN + } +#endif + } + } + } +} + +void DomoticzMqttSubscribe(void) +{ + uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; + for (uint32_t i = 0; i < maxdev; i++) { + if (Settings.domoticz_relay_idx[i]) { + domoticz_subscribe = true; + } + } + + if (domoticz_subscribe) { + char stopic[TOPSZ]; + snprintf_P(stopic, sizeof(stopic), PSTR(DOMOTICZ_OUT_TOPIC "/#")); + MqttSubscribe(stopic); + } +} +# 219 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_07_domoticz.ino" +bool DomoticzMqttData(void) +{ + domoticz_update_flag = true; + + if (strncasecmp_P(XdrvMailbox.topic, PSTR(DOMOTICZ_OUT_TOPIC), strlen(DOMOTICZ_OUT_TOPIC)) != 0) { + return false; + } + + + if (XdrvMailbox.data_len < 20) { + return true; + } + StaticJsonBuffer<400> jsonBuf; + JsonObject& domoticz = jsonBuf.parseObject(XdrvMailbox.data); + if (!domoticz.success()) { + return true; + } + + + + uint32_t idx = domoticz["idx"]; + int16_t nvalue = -1; + if (domoticz.containsKey("nvalue")) { + nvalue = domoticz["nvalue"]; + } + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ "idx %d, nvalue %d"), idx, nvalue); + + bool found = false; + if ((idx > 0) && (nvalue >= 0) && (nvalue <= 15)) { + uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; + for (uint32_t i = 0; i < maxdev; i++) { + if (idx == Settings.domoticz_relay_idx[i]) { + bool iscolordimmer = strcmp_P(domoticz["dtype"],PSTR("Color Switch")) == 0; + bool isShutter = strcmp_P(domoticz["dtype"],PSTR("Light/Switch")) == 0 & strncmp_P(domoticz["switchType"],PSTR("Blinds"), 6) == 0; + + char stemp1[10]; + snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1); +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (1 == i)) { + uint8_t svalue = 0; + if (domoticz.containsKey("svalue1")) { + svalue = domoticz["svalue1"]; + } else { + return true; + } + svalue = (nvalue == 2) ? svalue / 10 : 0; + if (GetFanspeed() == svalue) { + return true; + } + if (TimePassedSince(domoticz_fan_debounce) < 1000) { + return true; + } + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_FANSPEED)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), svalue); + found = true; + } else +#endif +#ifdef USE_SHUTTER + if (isShutter) + { + if (domoticz.containsKey("nvalue")) { + nvalue = domoticz["nvalue"]; + } + + uint8_t position = 0; + if (domoticz.containsKey("svalue1")) { + position = domoticz["svalue1"]; + } + if (nvalue != 2) { + position = nvalue == 0 ? 0 : 100; + } + + snprintf_P(XdrvMailbox.topic, TOPSZ, PSTR("/" D_PRFX_SHUTTER D_CMND_SHUTTER_POSITION)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), position); + XdrvMailbox.data_len = position > 99 ? 3 : (position > 9 ? 2 : 1); + + found = true; + } else +#endif + if (iscolordimmer && 10 == nvalue) { + + JsonObject& color = domoticz["Color"]; + uint16_t level = nvalue = domoticz["svalue1"]; + uint16_t r = color["r"]; r = r * level / 100; + uint16_t g = color["g"]; g = g * level / 100; + uint16_t b = color["b"]; b = b * level / 100; + uint16_t cw = color["cw"]; cw = cw * level / 100; + uint16_t ww = color["ww"]; ww = ww * level / 100; + uint16_t m = 0; + uint16_t t = 0; + if (color.containsKey("m")) { + m = color["m"]; + t = color["t"]; + } + if (2 == m) { + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_BACKLOG)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR(D_CMND_COLORTEMPERATURE " %d;" D_CMND_DIMMER " %d"), changeUIntScale(t, 0, 255, CT_MIN, CT_MAX), level); + } else { + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_COLOR)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%02x%02x%02x%02x%02x"), r, g, b, cw, ww); + } + found = true; + } + else if ((!iscolordimmer && 2 == nvalue) || + (iscolordimmer && 15 == nvalue)) { + if (domoticz.containsKey("svalue1")) { + nvalue = domoticz["svalue1"]; + } else { + return true; + } + if (light_type && (Settings.light_dimmer == nvalue) && ((power >> i) &1)) { + return true; + } + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_DIMMER)); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); + found = true; + } + else if (1 == nvalue || 0 == nvalue) { + if (((power >> i) &1) == (power_t)nvalue) { + return true; + } + snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_POWER "%s"), (devices_present > 1) ? stemp1 : ""); + snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); + found = true; + } + break; + } + } + } + if (!found) { return true; } + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ D_RECEIVED_TOPIC " %s, " D_DATA " %s"), XdrvMailbox.topic, XdrvMailbox.data); + + domoticz_update_flag = false; + return false; +} + + + +bool DomoticzSendKey(uint8_t key, uint8_t device, uint8_t state, uint8_t svalflg) +{ + bool result = false; + + if (device <= MAX_DOMOTICZ_IDX) { + if ((Settings.domoticz_key_idx[device -1] || Settings.domoticz_switch_idx[device -1]) && (svalflg)) { + Response_P(PSTR("{\"command\":\"switchlight\",\"idx\":%d,\"switchcmd\":\"%s\"}"), + (key) ? Settings.domoticz_switch_idx[device -1] : Settings.domoticz_key_idx[device -1], (state) ? (POWER_TOGGLE == state) ? "Toggle" : "On" : "Off"); + MqttPublish(domoticz_in_topic); + result = true; + } + } + return result; +} +# 391 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_07_domoticz.ino" +uint8_t DomoticzHumidityState(char *hum) +{ + uint8_t h = atoi(hum); + return (!h) ? 0 : (h < 40) ? 2 : (h > 70) ? 3 : 1; +} + +void DomoticzSensor(uint8_t idx, char *data) +{ + if (Settings.domoticz_sensor_idx[idx]) { + char dmess[128]; + + memcpy(dmess, mqtt_data, sizeof(dmess)); + if (DZ_AIRQUALITY == idx) { + Response_P(PSTR("{\"idx\":%d,\"nvalue\":%s,\"Battery\":%d,\"RSSI\":%d}"), + Settings.domoticz_sensor_idx[idx], data, DomoticzBatteryQuality(), DomoticzRssiQuality()); + } else { + uint8_t nvalue = 0; +#ifdef USE_SHUTTER + if (DZ_SHUTTER == idx) { + uint8_t position = atoi(data); + nvalue = position < 2 ? 0 : (position == 100 ? 1 : 2); + } +#endif + Response_P(DOMOTICZ_MESSAGE, + Settings.domoticz_sensor_idx[idx], nvalue, data, DomoticzBatteryQuality(), DomoticzRssiQuality()); + } + MqttPublish(domoticz_in_topic); + memcpy(mqtt_data, dmess, sizeof(dmess)); + } +} + +void DomoticzSensor(uint8_t idx, uint32_t value) +{ + char data[16]; + snprintf_P(data, sizeof(data), PSTR("%d"), value); + DomoticzSensor(idx, data); +} + +void DomoticzTempHumSensor(char *temp, char *hum) +{ + char data[16]; + snprintf_P(data, sizeof(data), PSTR("%s;%s;%d"), temp, hum, DomoticzHumidityState(hum)); + DomoticzSensor(DZ_TEMP_HUM, data); +} + +void DomoticzTempHumPressureSensor(char *temp, char *hum, char *baro) +{ + char data[32]; + snprintf_P(data, sizeof(data), PSTR("%s;%s;%d;%s;5"), temp, hum, DomoticzHumidityState(hum), baro); + DomoticzSensor(DZ_TEMP_HUM_BARO, data); +} + +void DomoticzSensorPowerEnergy(int power, char *energy) +{ + char data[16]; + snprintf_P(data, sizeof(data), PSTR("%d;%s"), power, energy); + DomoticzSensor(DZ_POWER_ENERGY, data); +} + +void DomoticzSensorP1SmartMeter(char *usage1, char *usage2, char *return1, char *return2, int power) +{ + + + + + + int consumed = power; + int produced = 0; + if (power < 0) { + consumed = 0; + produced = -power; + } + char data[64]; + snprintf_P(data, sizeof(data), PSTR("%s;%s;%s;%s;%d;%d"), usage1, usage2, return1, return2, consumed, produced); + DomoticzSensor(DZ_P1_SMART_METER, data); +} + + + + + +void CmndDomoticzIdx(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_relay_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + restart_flag = 2; + } + ResponseCmndIdxNumber(Settings.domoticz_relay_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzKeyIdx(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_key_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.domoticz_key_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzSwitchIdx(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_switch_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.domoticz_switch_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzSensorIdx(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= DZ_MAX_SENSORS)) { + if (XdrvMailbox.payload >= 0) { + Settings.domoticz_sensor_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.domoticz_sensor_idx[XdrvMailbox.index -1]); + } +} + +void CmndDomoticzUpdateTimer(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { + Settings.domoticz_update_timer = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.domoticz_update_timer); +} + + + + + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_DOMOTICZ "dm" + +const char S_CONFIGURE_DOMOTICZ[] PROGMEM = D_CONFIGURE_DOMOTICZ; + +const char HTTP_BTN_MENU_DOMOTICZ[] PROGMEM = + "

"; + +const char HTTP_FORM_DOMOTICZ[] PROGMEM = + "
 " D_DOMOTICZ_PARAMETERS " " + "
" + ""; +const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM = + "" + ""; +const char HTTP_FORM_DOMOTICZ_SWITCH[] PROGMEM = + ""; +const char HTTP_FORM_DOMOTICZ_SENSOR[] PROGMEM = + ""; +const char HTTP_FORM_DOMOTICZ_TIMER[] PROGMEM = + ""; + +void HandleDomoticzConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_DOMOTICZ); + + if (WebServer->hasArg("save")) { + DomoticzSaveSettings(); + WebRestart(1); + return; + } + + char stemp[40]; + + WSContentStart_P(S_CONFIGURE_DOMOTICZ); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_DOMOTICZ); + for (uint32_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { + if (i < devices_present) { + WSContentSend_P(HTTP_FORM_DOMOTICZ_RELAY, + i +1, i, Settings.domoticz_relay_idx[i], + i +1, i, Settings.domoticz_key_idx[i]); + } + if (pin[GPIO_SWT1 +i] < 99) { + WSContentSend_P(HTTP_FORM_DOMOTICZ_SWITCH, + i +1, i, Settings.domoticz_switch_idx[i]); + } +#ifdef USE_SONOFF_IFAN + if (IsModuleIfan() && (1 == i)) { break; } +#endif + } + for (uint32_t i = 0; i < DZ_MAX_SENSORS; i++) { + WSContentSend_P(HTTP_FORM_DOMOTICZ_SENSOR, + i +1, GetTextIndexed(stemp, sizeof(stemp), i, kDomoticzSensors), i, Settings.domoticz_sensor_idx[i]); + } + WSContentSend_P(HTTP_FORM_DOMOTICZ_TIMER, Settings.domoticz_update_timer); + WSContentSend_P(PSTR("
" D_DOMOTICZ_IDX " %d
" D_DOMOTICZ_KEY_IDX " %d
" D_DOMOTICZ_SWITCH_IDX " %d
" D_DOMOTICZ_SENSOR_IDX " %d %s
" D_DOMOTICZ_UPDATE_TIMER " (" STR(DOMOTICZ_UPDATE_TIMER) ")
")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void DomoticzSaveSettings(void) +{ + char stemp[20]; + char ssensor_indices[6 * MAX_DOMOTICZ_SNS_IDX]; + char tmp[100]; + + for (uint32_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_relay_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + snprintf_P(stemp, sizeof(stemp), PSTR("k%d"), i); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_key_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_switch_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + } + ssensor_indices[0] = '\0'; + for (uint32_t i = 0; i < DZ_MAX_SENSORS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i); + WebGetArg(stemp, tmp, sizeof(tmp)); + Settings.domoticz_sensor_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); + snprintf_P(ssensor_indices, sizeof(ssensor_indices), PSTR("%s%s%d"), ssensor_indices, (strlen(ssensor_indices)) ? "," : "", Settings.domoticz_sensor_idx[i]); + } + WebGetArg("ut", tmp, sizeof(tmp)); + Settings.domoticz_update_timer = (!strlen(tmp)) ? DOMOTICZ_UPDATE_TIMER : atoi(tmp); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"), + Settings.domoticz_relay_idx[0], Settings.domoticz_relay_idx[1], Settings.domoticz_relay_idx[2], Settings.domoticz_relay_idx[3], + Settings.domoticz_key_idx[0], Settings.domoticz_key_idx[1], Settings.domoticz_key_idx[2], Settings.domoticz_key_idx[3], + Settings.domoticz_switch_idx[0], Settings.domoticz_switch_idx[1], Settings.domoticz_switch_idx[2], Settings.domoticz_switch_idx[3], + ssensor_indices, Settings.domoticz_update_timer); +} +#endif + + + + + +bool Xdrv07(uint8_t function) +{ + bool result = false; + + if (Settings.flag.mqtt_enabled) { + switch (function) { + case FUNC_EVERY_SECOND: + DomoticzMqttUpdate(); + break; + case FUNC_MQTT_DATA: + result = DomoticzMqttData(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_DOMOTICZ); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer->on("/" WEB_HANDLE_DOMOTICZ, HandleDomoticzConfiguration); + break; +#endif + case FUNC_MQTT_SUBSCRIBE: + DomoticzMqttSubscribe(); +#ifdef USE_SHUTTER + if (Settings.domoticz_sensor_idx[DZ_SHUTTER]) { domoticz_is_shutter = true; } +#endif + break; + case FUNC_MQTT_INIT: + domoticz_update_timer = 2; + break; + case FUNC_SHOW_SENSOR: + + break; + case FUNC_COMMAND: + result = DecodeCommand(kDomoticzCommands, DomoticzCommand); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_08_serial_bridge.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_08_serial_bridge.ino" +#ifdef USE_SERIAL_BRIDGE + + + + +#define XDRV_08 8 + +const uint8_t SERIAL_BRIDGE_BUFFER_SIZE = 130; + +const char kSerialBridgeCommands[] PROGMEM = "|" + D_CMND_SSERIALSEND "|" D_CMND_SBAUDRATE; + +void (* const SerialBridgeCommand[])(void) PROGMEM = { + &CmndSSerialSend, &CmndSBaudrate }; + +#include + +TasmotaSerial *SerialBridgeSerial = nullptr; + +unsigned long serial_bridge_polling_window = 0; +char *serial_bridge_buffer = nullptr; +int serial_bridge_in_byte_counter = 0; +bool serial_bridge_active = true; +bool serial_bridge_raw = false; + +void SerialBridgeInput(void) +{ + while (SerialBridgeSerial->available()) { + yield(); + uint8_t serial_in_byte = SerialBridgeSerial->read(); + + if ((serial_in_byte > 127) && !serial_bridge_raw) { + serial_bridge_in_byte_counter = 0; + SerialBridgeSerial->flush(); + return; + } + if (serial_in_byte || serial_bridge_raw) { + + if ((serial_bridge_in_byte_counter < SERIAL_BRIDGE_BUFFER_SIZE -1) && + ((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || + ((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || + serial_bridge_raw)) { + serial_bridge_buffer[serial_bridge_in_byte_counter++] = serial_in_byte; + serial_bridge_polling_window = millis(); + } else { + serial_bridge_polling_window = 0; + break; + } + } + } + + if (serial_bridge_in_byte_counter && (millis() > (serial_bridge_polling_window + SERIAL_POLLING))) { + serial_bridge_buffer[serial_bridge_in_byte_counter] = 0; + char hex_char[(serial_bridge_in_byte_counter * 2) + 2]; + bool assume_json = (!serial_bridge_raw && (serial_bridge_buffer[0] == '{')); + Response_P(PSTR("{\"" D_JSON_SSERIALRECEIVED "\":%s%s%s}"), + (assume_json) ? "" : """", + (serial_bridge_raw) ? ToHex_P((unsigned char*)serial_bridge_buffer, serial_bridge_in_byte_counter, hex_char, sizeof(hex_char)) : serial_bridge_buffer, + (assume_json) ? "" : """"); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SSERIALRECEIVED)); + XdrvRulesProcess(); + serial_bridge_in_byte_counter = 0; + } +} + + + +void SerialBridgeInit(void) +{ + serial_bridge_active = false; + 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 * 300)) { + if (SerialBridgeSerial->hardwareSerial()) { + ClaimSerial(); + serial_bridge_buffer = serial_in_buffer; + } else { + serial_bridge_buffer = (char*)(malloc(SERIAL_BRIDGE_BUFFER_SIZE)); + } + serial_bridge_active = true; + SerialBridgeSerial->flush(); + } + } +} + + + + + +void CmndSSerialSend(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { + serial_bridge_raw = (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) || (4 == 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); + } + else if (5 == XdrvMailbox.index) { + char *p; + char stemp[3]; + uint8_t code; + + char *codes = RemoveSpace(XdrvMailbox.data); + int size = strlen(XdrvMailbox.data); + + while (size > 1) { + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, &p, 16); + SerialBridgeSerial->write(code); + size -= 2; + codes += 2; + } + } + ResponseCmndDone(); + } + } +} + +void CmndSBaudrate(void) +{ + if (XdrvMailbox.payload >= 300) { + XdrvMailbox.payload /= 300; + Settings.sbaudrate = XdrvMailbox.payload; + SerialBridgeSerial->begin(Settings.sbaudrate * 300); + } + ResponseCmndNumber(Settings.sbaudrate * 300); +} + + + + + +bool Xdrv08(uint8_t function) +{ + bool result = false; + + if (serial_bridge_active) { + switch (function) { + case FUNC_LOOP: + if (SerialBridgeSerial) { SerialBridgeInput(); } + break; + case FUNC_PRE_INIT: + SerialBridgeInit(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kSerialBridgeCommands, SerialBridgeCommand); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_09_timers.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_09_timers.ino" +#ifdef USE_TIMERS +# 39 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_09_timers.ino" +#define XDRV_09 9 + +const char kTimerCommands[] PROGMEM = "|" + D_CMND_TIMER "|" D_CMND_TIMERS +#ifdef USE_SUNRISE + "|" D_CMND_LATITUDE "|" D_CMND_LONGITUDE +#endif + ; + +void (* const TimerCommand[])(void) PROGMEM = { + &CmndTimer, &CmndTimers +#ifdef USE_SUNRISE + , &CmndLatitude, &CmndLongitude +#endif + }; + +uint16_t timer_last_minute = 60; +int8_t timer_window[MAX_TIMERS] = { 0 }; + +#ifdef USE_SUNRISE +# 67 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_09_timers.ino" +const float pi2 = TWO_PI; +const float pi = PI; +const float RAD = DEG_TO_RAD; + +float JulianischesDatum(void) +{ + + int Gregor; + int Jahr = RtcTime.year; + int Monat = RtcTime.month; + int Tag = RtcTime.day_of_month; + + if (Monat <= 2) { + Monat += 12; + Jahr -= 1; + } + Gregor = (Jahr / 400) - (Jahr / 100) + (Jahr / 4); + return 2400000.5f + 365.0f*Jahr - 679004.0f + Gregor + (int)(30.6001f * (Monat +1)) + Tag + 0.5f; +} + +float InPi(float x) +{ + int n = (int)(x / pi2); + x = x - n*pi2; + if (x < 0) x += pi2; + return x; +} + +float eps(float T) +{ + + return RAD * (23.43929111f + (-46.8150f*T - 0.00059f*T*T + 0.001813f*T*T*T)/3600.0f); +} + +float BerechneZeitgleichung(float *DK,float T) +{ + float RA_Mittel = 18.71506921f + 2400.0513369f*T +(2.5862e-5f - 1.72e-9f*T)*T*T; + float M = InPi(pi2 * (0.993133f + 99.997361f*T)); + float L = InPi(pi2 * (0.7859453f + M/pi2 + (6893.0f*sinf(M)+72.0f*sinf(2.0f*M)+6191.2f*T) / 1296.0e3f)); + float e = eps(T); + float RA = atanf(tanf(L)*cosf(e)); + if (RA < 0.0) RA += pi; + if (L > pi) RA += pi; + RA = 24.0*RA/pi2; + *DK = asinf(sinf(e)*sinf(L)); + + RA_Mittel = 24.0f * InPi(pi2*RA_Mittel/24.0f)/pi2; + float dRA = RA_Mittel - RA; + if (dRA < -12.0f) dRA += 24.0f; + if (dRA > 12.0f) dRA -= 24.0f; + dRA = dRA * 1.0027379f; + return dRA; +} + +void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8_t *minute_down) +{ + float JD2000 = 2451545.0f; + float JD = JulianischesDatum(); + float T = (JD - JD2000) / 36525.0f; + float DK; + + + + + + + + float h = SUNRISE_DAWN_ANGLE *RAD; + float B = (((float)Settings.latitude)/1000000) * RAD; + float GeographischeLaenge = ((float)Settings.longitude)/1000000; + + + + float Zeitzone = ((float)Rtc.time_timezone) / 60; + float Zeitgleichung = BerechneZeitgleichung(&DK, T); + float Zeitdifferenz = 12.0f*acosf((sinf(h) - sinf(B)*sinf(DK)) / (cosf(B)*cosf(DK)))/pi; + float AufgangOrtszeit = 12.0f - Zeitdifferenz - Zeitgleichung; + float UntergangOrtszeit = 12.0f + Zeitdifferenz - Zeitgleichung; + float AufgangWeltzeit = AufgangOrtszeit - GeographischeLaenge / 15.0f; + float UntergangWeltzeit = UntergangOrtszeit - GeographischeLaenge / 15.0f; + float Aufgang = AufgangWeltzeit + Zeitzone; + if (Aufgang < 0.0f) { + Aufgang += 24.0f; + } else { + if (Aufgang >= 24.0f) Aufgang -= 24.0f; + } + float Untergang = UntergangWeltzeit + Zeitzone; + if (Untergang < 0.0f) { + Untergang += 24.0f; + } else { + if (Untergang >= 24.0f) Untergang -= 24.0f; + } + int AufgangMinuten = (int)(60.0f*(Aufgang - (int)Aufgang)+0.5f); + int AufgangStunden = (int)Aufgang; + if (AufgangMinuten >= 60.0f) { + AufgangMinuten -= 60.0f; + AufgangStunden++; + } else { + if (AufgangMinuten < 0.0f) { + AufgangMinuten += 60.0f; + AufgangStunden--; + if (AufgangStunden < 0.0f) AufgangStunden += 24.0f; + } + } + int UntergangMinuten = (int)(60.0f*(Untergang - (int)Untergang)+0.5f); + int UntergangStunden = (int)Untergang; + if (UntergangMinuten >= 60.0f) { + UntergangMinuten -= 60.0f; + UntergangStunden++; + } else { + if (UntergangMinuten<0) { + UntergangMinuten += 60.0f; + UntergangStunden--; + if (UntergangStunden < 0.0f) UntergangStunden += 24.0f; + } + } + *hour_up = AufgangStunden; + *minute_up = AufgangMinuten; + *hour_down = UntergangStunden; + *minute_down = UntergangMinuten; +} + +void ApplyTimerOffsets(Timer *duskdawn) +{ + uint8_t hour[2]; + uint8_t minute[2]; + Timer stored = (Timer)*duskdawn; + + + DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); + uint8_t mode = (duskdawn->mode -1) &1; + duskdawn->time = (hour[mode] *60) + minute[mode]; + + + uint16_t timeBuffer; + if ((uint16_t)stored.time > 719) { + + timeBuffer = (uint16_t)stored.time - 720; + + if (timeBuffer > (uint16_t)duskdawn->time) { + timeBuffer = 1440 - (timeBuffer - (uint16_t)duskdawn->time); + duskdawn->days = duskdawn->days >> 1; + duskdawn->days |= (stored.days << 6); + } else { + timeBuffer = (uint16_t)duskdawn->time - timeBuffer; + } + } else { + + timeBuffer = (uint16_t)duskdawn->time + (uint16_t)stored.time; + + if (timeBuffer > 1440) { + timeBuffer -= 1440; + duskdawn->days = duskdawn->days << 1; + duskdawn->days |= (stored.days >> 6); + } + } + duskdawn->time = timeBuffer; +} + +String GetSun(uint32_t dawn) +{ + char stime[6]; + + uint8_t hour[2]; + uint8_t minute[2]; + + DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); + dawn &= 1; + snprintf_P(stime, sizeof(stime), PSTR("%02d:%02d"), hour[dawn], minute[dawn]); + return String(stime); +} + +uint16_t SunMinutes(uint32_t dawn) +{ + uint8_t hour[2]; + uint8_t minute[2]; + + DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); + dawn &= 1; + return (hour[dawn] *60) + minute[dawn]; +} + +#endif + + + +void TimerSetRandomWindow(uint32_t index) +{ + timer_window[index] = 0; + if (Settings.timer[index].window) { + timer_window[index] = (random(0, (Settings.timer[index].window << 1) +1)) - Settings.timer[index].window; + } +} + +void TimerSetRandomWindows(void) +{ + for (uint32_t i = 0; i < MAX_TIMERS; i++) { TimerSetRandomWindow(i); } +} + +void TimerEverySecond(void) +{ + if (RtcTime.valid) { + if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { TimerSetRandomWindows(); } + if (Settings.flag3.timers_enable && + (uptime > 60) && (RtcTime.minute != timer_last_minute)) { + timer_last_minute = RtcTime.minute; + int32_t time = (RtcTime.hour *60) + RtcTime.minute; + uint8_t days = 1 << (RtcTime.day_of_week -1); + + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + + Timer xtimer = Settings.timer[i]; +#ifdef USE_SUNRISE + if ((1 == xtimer.mode) || (2 == xtimer.mode)) { + ApplyTimerOffsets(&xtimer); + } +#endif + if (xtimer.arm) { + int32_t set_time = xtimer.time + timer_window[i]; + if (set_time < 0) { + set_time = abs(timer_window[i]); + } + if (set_time > 1439) { + set_time = xtimer.time - abs(timer_window[i]); + } + if (set_time > 1439) { set_time = 1439; } + + DEBUG_DRIVER_LOG(PSTR("TIM: Timer %d, Time %d, Window %d, SetTime %d"), i +1, xtimer.time, timer_window[i], set_time); + + if (time == set_time) { + if (xtimer.days & days) { + Settings.timer[i].arm = xtimer.repeat; +#if defined(USE_RULES) || defined(USE_SCRIPT) + if (POWER_BLINK == xtimer.power) { + Response_P(PSTR("{\"Clock\":{\"Timer\":%d}}"), i +1); + XdrvRulesProcess(); + } else +#endif + if (devices_present) { ExecuteCommandPower(xtimer.device +1, xtimer.power, SRC_TIMER); } + } + } + } + } + } + } +} + +void PrepShowTimer(uint32_t index) +{ + Timer xtimer = Settings.timer[index -1]; + + char days[8] = { 0 }; + for (uint32_t i = 0; i < 7; i++) { + uint8_t mask = 1 << i; + snprintf(days, sizeof(days), "%s%d", days, ((xtimer.days & mask) > 0)); + } + + char soutput[80]; + soutput[0] = '\0'; + if (devices_present) { + snprintf_P(soutput, sizeof(soutput), PSTR(",\"" D_JSON_TIMER_OUTPUT "\":%d"), xtimer.device +1); + } +#ifdef USE_SUNRISE + char sign[2] = { 0 }; + int16_t hour = xtimer.time / 60; + if ((1 == xtimer.mode) || (2 == xtimer.mode)) { + if (hour > 11) { + hour -= 12; + sign[0] = '-'; + } + } + ResponseAppend_P(PSTR("\"" D_CMND_TIMER "%d\":{\"" D_JSON_TIMER_ARM "\":%d,\"" D_JSON_TIMER_MODE "\":%d,\"" D_JSON_TIMER_TIME "\":\"%s%02d:%02d\",\"" D_JSON_TIMER_WINDOW "\":%d,\"" D_JSON_TIMER_DAYS "\":\"%s\",\"" D_JSON_TIMER_REPEAT "\":%d%s,\"" D_JSON_TIMER_ACTION "\":%d}"), + index, xtimer.arm, xtimer.mode, sign, hour, xtimer.time % 60, xtimer.window, days, xtimer.repeat, soutput, xtimer.power); +#else + ResponseAppend_P(PSTR("\"" D_CMND_TIMER "%d\":{\"" D_JSON_TIMER_ARM "\":%d,\"" D_JSON_TIMER_TIME "\":\"%02d:%02d\",\"" D_JSON_TIMER_WINDOW "\":%d,\"" D_JSON_TIMER_DAYS "\":\"%s\",\"" D_JSON_TIMER_REPEAT "\":%d%s,\"" D_JSON_TIMER_ACTION "\":%d}"), + index, xtimer.arm, xtimer.time / 60, xtimer.time % 60, xtimer.window, days, xtimer.repeat, soutput, xtimer.power); +#endif +} + + + + + +void CmndTimer(void) +{ + uint32_t index = XdrvMailbox.index; + if ((index > 0) && (index <= MAX_TIMERS)) { + uint32_t error = 0; + if (XdrvMailbox.data_len) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_TIMERS)) { + if (XdrvMailbox.payload == 0) { + Settings.timer[index -1].data = 0; + } else { + Settings.timer[index -1].data = Settings.timer[XdrvMailbox.payload -1].data; + } + } else { + +#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 + if (devices_present) { +#endif + char dataBufUc[XdrvMailbox.data_len + 1]; + UpperCase(dataBufUc, XdrvMailbox.data); + StaticJsonBuffer<256> jsonBuffer; + JsonObject& root = jsonBuffer.parseObject(dataBufUc); + if (!root.success()) { + Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); + error = 1; + } + else { + char parm_uc[10]; + index--; + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ARM))].success()) { + Settings.timer[index].arm = (root[parm_uc] != 0); + } +#ifdef USE_SUNRISE + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_MODE))].success()) { + Settings.timer[index].mode = (uint8_t)root[parm_uc] & 0x03; + } +#endif + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_TIME))].success()) { + uint16_t itime = 0; + int8_t value = 0; + uint8_t sign = 0; + char time_str[10]; + + strlcpy(time_str, root[parm_uc], sizeof(time_str)); + const char *substr = strtok(time_str, ":"); + if (substr != nullptr) { + if (strchr(substr, '-')) { + sign = 1; + substr++; + } + value = atoi(substr); + if (sign) { value += 12; } + if (value > 23) { value = 23; } + itime = value * 60; + substr = strtok(nullptr, ":"); + if (substr != nullptr) { + value = atoi(substr); + if (value < 0) { value = 0; } + if (value > 59) { value = 59; } + itime += value; + } + } + Settings.timer[index].time = itime; + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_WINDOW))].success()) { + Settings.timer[index].window = (uint8_t)root[parm_uc] & 0x0F; + TimerSetRandomWindow(index); + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DAYS))].success()) { + + Settings.timer[index].days = 0; + const char *tday = root[parm_uc]; + uint8_t i = 0; + char ch = *tday++; + while ((ch != '\0') && (i < 7)) { + if (ch == '-') { ch = '0'; } + uint8_t mask = 1 << i++; + Settings.timer[index].days |= (ch == '0') ? 0 : mask; + ch = *tday++; + } + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_REPEAT))].success()) { + Settings.timer[index].repeat = (root[parm_uc] != 0); + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_OUTPUT))].success()) { + uint8_t device = ((uint8_t)root[parm_uc] -1) & 0x0F; + Settings.timer[index].device = (device < devices_present) ? device : 0; + } + if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ACTION))].success()) { + uint8_t action = (uint8_t)root[parm_uc] & 0x03; + Settings.timer[index].power = (devices_present) ? action : 3; + } + + index++; + } + +#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 + } else { + Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_TIMER_NO_DEVICE "\"}"), index); + error = 1; + } +#endif + } + } + if (!error) { + Response_P(PSTR("{")); + PrepShowTimer(index); + ResponseJsonEnd(); + } + } +} + +void CmndTimers(void) +{ + if (XdrvMailbox.data_len) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag3.timers_enable = XdrvMailbox.payload; + } + if (XdrvMailbox.payload == 2) { + Settings.flag3.timers_enable = !Settings.flag3.timers_enable; + } + } + + ResponseCmndStateText(Settings.flag3.timers_enable); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, XdrvMailbox.command); + + uint32_t jsflg = 0; + uint32_t lines = 1; + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + if (!jsflg) { + Response_P(PSTR("{\"" D_CMND_TIMERS "%d\":{"), lines++); + } else { + ResponseAppend_P(PSTR(",")); + } + jsflg++; + PrepShowTimer(i +1); + if (jsflg > 3) { + ResponseJsonEndEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_TIMERS)); + jsflg = 0; + } + } + mqtt_data[0] = '\0'; +} + +#ifdef USE_SUNRISE +void CmndLongitude(void) +{ + if (XdrvMailbox.data_len) { + Settings.longitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); + } + ResponseCmndFloat((float)(Settings.longitude) /1000000, 6); +} + +void CmndLatitude(void) +{ + if (XdrvMailbox.data_len) { + Settings.latitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); + } + ResponseCmndFloat((float)(Settings.latitude) /1000000, 6); +} +#endif + + + + + +#ifdef USE_WEBSERVER +#ifdef USE_TIMERS_WEB + +#define WEB_HANDLE_TIMER "tm" + +const char S_CONFIGURE_TIMER[] PROGMEM = D_CONFIGURE_TIMER; + +const char HTTP_BTN_MENU_TIMER[] PROGMEM = + "

"; + +const char HTTP_TIMER_SCRIPT1[] PROGMEM = + "var pt=[],ct=99;" + "function ce(i,q){" + "var o=document.createElement('option');" + "o.textContent=i;" + "q.appendChild(o);" + "}"; +#ifdef USE_SUNRISE +const char HTTP_TIMER_SCRIPT2[] PROGMEM = + "function gt(){" + "var m,p,q;" + "m=qs('input[name=\"rd\"]:checked').value;" + "p=pt[ct]&0x7FF;" + "if(m==0){" + "so(0);" + "q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" + "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" + "}" + "if((m==1)||(m==2)){" + "so(1);" + "q=Math.floor(p/60);" + "if(q>=12){q-=12;qs('#dr').selectedIndex=1;}" + "else{qs('#dr').selectedIndex=0;}" + "if(q<10){q='0'+q;}qs('#ho').value=q;" + "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" + "}" + "}" + "function so(b){" + "o=qs('#ho');" + "e=o.childElementCount;" + "if(b==1){" + "qs('#dr').style.visibility='';" + "if(e>12){for(i=12;i<=23;i++){o.removeChild(o.lastElementChild);}}" + "}else{" + "qs('#dr').style.visibility='hidden';" + "if(e<23){for(i=12;i<=23;i++){ce(i,o);}}" + "}" + "}"; +#endif +const char HTTP_TIMER_SCRIPT3[] PROGMEM = + "function st(){" + "var i,l,m,n,p,s;" + "m=0;s=0;" + "n=1<<31;if(eb('a0').checked){s|=n;}" + "n=1<<15;if(eb('r0').checked){s|=n;}" + "for(i=0;i<7;i++){n=1<<(16+i);if(eb('w'+i).checked){s|=n;}}" +#ifdef USE_SUNRISE + "m=qs('input[name=\"rd\"]:checked').value;" + "s|=(qs('input[name=\"rd\"]:checked').value<<29);" +#endif + "if(%d>0){" + "i=qs('#d1').selectedIndex;if(i>=0){s|=(i<<23);}" + "s|=(qs('#p1').selectedIndex<<27);" + "}else{" + "s|=3<<27;" + "}" + "l=((qs('#ho').selectedIndex*60)+qs('#mi').selectedIndex)&0x7FF;" + "if(m==0){s|=l;}" +#ifdef USE_SUNRISE + "if((m==1)||(m==2)){" + "if(qs('#dr').selectedIndex>0){if(l>0){l+=720;}}" + "s|=l&0x7FF;" + "}" +#endif + "s|=((qs('#mw').selectedIndex)&0x0F)<<11;" + "pt[ct]=s;" + "eb('t0').value=pt.join();" + "}"; +const char HTTP_TIMER_SCRIPT4[] PROGMEM = + "function ot(t,e){" + "var i,n,o,p,q,s;" + "if(ct<99){st();}" + "ct=t;" + "o=document.getElementsByClassName('tl');" + "for(i=0;i>29)&3;eb('b'+p).checked=1;" + "gt();" +#else + "p=s&0x7FF;" + "q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" + "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" +#endif + "q=(s>>11)&0xF;if(q<10){q='0'+q;}qs('#mw').value=q;" + "for(i=0;i<7;i++){p=(s>>(16+i))&1;eb('w'+i).checked=p;}" + "if(%d>0){" + "p=(s>>23)&0xF;qs('#d1').value=p+1;" + "p=(s>>27)&3;qs('#p1').selectedIndex=p;" + "}" + "p=(s>>15)&1;eb('r0').checked=p;" + "p=(s>>31)&1;eb('a0').checked=p;" + "}"; +const char HTTP_TIMER_SCRIPT5[] PROGMEM = + "function it(){" + "var b,i,o,s;" + "pt=eb('t0').value.split(',').map(Number);" + "s='';" + "for(i=0;i<%d;i++){" + "b='';" + "if(0==i){b=\" id='dP'\";}" + "s+=\"\"" + "}" + "eb('bt').innerHTML=s;" + "if(%d>0){" + "eb('oa').innerHTML=\"" D_TIMER_OUTPUT " " D_TIMER_ACTION " \";" + "o=qs('#p1');ce('" D_OFF "',o);ce('" D_ON "',o);ce('" D_TOGGLE "',o);" +#if defined(USE_RULES) || defined(USE_SCRIPT) + "ce('" D_RULE "',o);" +#else + "ce('" D_BLINK "',o);" +#endif + "}else{" + "eb('oa').innerHTML=\"" D_TIMER_ACTION " " D_RULE "\";" + "}"; +const char HTTP_TIMER_SCRIPT6[] PROGMEM = +#ifdef USE_SUNRISE + "o=qs('#dr');ce('+',o);ce('-',o);" +#endif + "o=qs('#ho');for(i=0;i<=23;i++){ce((i<10)?('0'+i):i,o);}" + "o=qs('#mi');for(i=0;i<=59;i++){ce((i<10)?('0'+i):i,o);}" + "o=qs('#mw');for(i=0;i<=15;i++){ce((i<10)?('0'+i):i,o);}" + "o=qs('#d1');for(i=0;i<%d;i++){ce(i+1,o);}" + "var a='" D_DAY3LIST "';" + "s='';for(i=0;i<7;i++){s+=\"\"+a.substring(i*3,(i*3)+3)+\" \"}" + "eb('ds').innerHTML=s;" + "eb('dP').click();" + "}" + "wl(it);"; +const char HTTP_TIMER_STYLE[] PROGMEM = + ".tl{float:left;border-radius:0;border:1px solid #%06x;padding:1px;width:6.25%%;}"; +const char HTTP_FORM_TIMER1[] PROGMEM = + "
" + " " D_TIMER_PARAMETERS " " + "
" + "
" D_TIMER_ENABLE "


" + "



" + "

" + "
" + "" D_TIMER_ARM " " + "" D_TIMER_REPEAT "" + "

" + "
"; +#ifdef USE_SUNRISE +const char HTTP_FORM_TIMER3[] PROGMEM = + "
" + "" D_TIMER_TIME "
" + "" D_SUNRISE " (%s)
" + "" D_SUNSET " (%s)
" + "
" + "

" + "" + " "; +#else +const char HTTP_FORM_TIMER3[] PROGMEM = + "" D_TIMER_TIME " "; +#endif +const char HTTP_FORM_TIMER4[] PROGMEM = + "" + " " D_HOUR_MINUTE_SEPARATOR " " + "" + " +/- " + "" + "

" + "
"; + +void HandleTimerConfiguration(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TIMER); + + if (WebServer->hasArg("save")) { + TimerSaveSettings(); + HandleConfiguration(); + return; + } + + WSContentStart_P(S_CONFIGURE_TIMER); + WSContentSend_P(HTTP_TIMER_SCRIPT1); +#ifdef USE_SUNRISE + WSContentSend_P(HTTP_TIMER_SCRIPT2); +#endif + WSContentSend_P(HTTP_TIMER_SCRIPT3, devices_present); + WSContentSend_P(HTTP_TIMER_SCRIPT4, WebColor(COL_TIMER_TAB_BACKGROUND), WebColor(COL_TIMER_TAB_TEXT), WebColor(COL_FORM), WebColor(COL_TEXT), devices_present); + WSContentSend_P(HTTP_TIMER_SCRIPT5, MAX_TIMERS, devices_present); + WSContentSend_P(HTTP_TIMER_SCRIPT6, devices_present); + WSContentSendStyle_P(HTTP_TIMER_STYLE, WebColor(COL_FORM)); + WSContentSend_P(HTTP_FORM_TIMER1, (Settings.flag3.timers_enable) ? " checked" : ""); + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + WSContentSend_P(PSTR("%s%u"), (i > 0) ? "," : "", Settings.timer[i].data); + } + WSContentSend_P(HTTP_FORM_TIMER2); +#ifdef USE_SUNRISE + WSContentSend_P(HTTP_FORM_TIMER3, 100 + (strlen(D_SUNSET) *12), GetSun(0).c_str(), GetSun(1).c_str()); +#else + WSContentSend_P(HTTP_FORM_TIMER3); +#endif + WSContentSend_P(HTTP_FORM_TIMER4); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void TimerSaveSettings(void) +{ + char tmp[MAX_TIMERS *12]; + char message[LOGSZ]; + Timer timer; + + Settings.flag3.timers_enable = WebServer->hasArg("e0"); + WebGetArg("t0", tmp, sizeof(tmp)); + char *p = tmp; + snprintf_P(message, sizeof(message), PSTR(D_LOG_MQTT D_CMND_TIMERS " %d"), Settings.flag3.timers_enable); + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + timer.data = strtol(p, &p, 10); + p++; + if (timer.time < 1440) { + bool flag = (timer.window != Settings.timer[i].window); + Settings.timer[i].data = timer.data; + if (flag) TimerSetRandomWindow(i); + } + snprintf_P(message, sizeof(message), PSTR("%s,0x%08X"), message, Settings.timer[i].data); + } + AddLog_P(LOG_LEVEL_DEBUG, message); +} +#endif +#endif + + + + + +bool Xdrv09(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_PRE_INIT: + TimerSetRandomWindows(); + break; +#ifdef USE_WEBSERVER +#ifdef USE_TIMERS_WEB + case FUNC_WEB_ADD_BUTTON: +#if defined(USE_RULES) || defined(USE_SCRIPT) + WSContentSend_P(HTTP_BTN_MENU_TIMER); +#else + if (devices_present) { WSContentSend_P(HTTP_BTN_MENU_TIMER); } +#endif + break; + case FUNC_WEB_ADD_HANDLER: + WebServer->on("/" WEB_HANDLE_TIMER, HandleTimerConfiguration); + break; +#endif +#endif + case FUNC_EVERY_SECOND: + TimerEverySecond(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kTimerCommands, TimerCommand); + break; + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +#ifdef USE_RULES +#ifndef USE_SCRIPT +# 67 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +#define XDRV_10 10 + +#define D_CMND_RULE "Rule" +#define D_CMND_RULETIMER "RuleTimer" +#define D_CMND_EVENT "Event" +#define D_CMND_VAR "Var" +#define D_CMND_MEM "Mem" +#define D_CMND_ADD "Add" +#define D_CMND_SUB "Sub" +#define D_CMND_MULT "Mult" +#define D_CMND_SCALE "Scale" +#define D_CMND_CALC_RESOLUTION "CalcRes" +#define D_CMND_SUBSCRIBE "Subscribe" +#define D_CMND_UNSUBSCRIBE "Unsubscribe" +#define D_CMND_IF "If" + +#define D_JSON_INITIATED "Initiated" + +#define COMPARE_OPERATOR_NONE -1 +#define COMPARE_OPERATOR_EQUAL 0 +#define COMPARE_OPERATOR_BIGGER 1 +#define COMPARE_OPERATOR_SMALLER 2 +#define COMPARE_OPERATOR_EXACT_DIVISION 3 +#define COMPARE_OPERATOR_NUMBER_EQUAL 4 +#define COMPARE_OPERATOR_NOT_EQUAL 5 +#define COMPARE_OPERATOR_BIGGER_EQUAL 6 +#define COMPARE_OPERATOR_SMALLER_EQUAL 7 +#define MAXIMUM_COMPARE_OPERATOR COMPARE_OPERATOR_SMALLER_EQUAL +const char kCompareOperators[] PROGMEM = "=\0>\0<\0|\0==!=>=<="; + +#ifdef USE_EXPRESSION + #include + + const char kExpressionOperators[] PROGMEM = "+-*/%^\0"; + #define EXPRESSION_OPERATOR_ADD 0 + #define EXPRESSION_OPERATOR_SUBTRACT 1 + #define EXPRESSION_OPERATOR_MULTIPLY 2 + #define EXPRESSION_OPERATOR_DIVIDEDBY 3 + #define EXPRESSION_OPERATOR_MODULO 4 + #define EXPRESSION_OPERATOR_POWER 5 + + const uint8_t kExpressionOperatorsPriorities[] PROGMEM = {1, 1, 2, 2, 3, 4}; + #define MAX_EXPRESSION_OPERATOR_PRIORITY 4 + + + #define LOGIC_OPERATOR_AND 1 + #define LOGIC_OPERATOR_OR 2 + + #define IF_BLOCK_INVALID -1 + #define IF_BLOCK_ANY 0 + #define IF_BLOCK_ELSEIF 1 + #define IF_BLOCK_ELSE 2 + #define IF_BLOCK_ENDIF 3 +#endif + +const char kRulesCommands[] PROGMEM = "|" + D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMND_EVENT "|" D_CMND_VAR "|" D_CMND_MEM "|" + D_CMND_ADD "|" D_CMND_SUB "|" D_CMND_MULT "|" D_CMND_SCALE "|" D_CMND_CALC_RESOLUTION +#ifdef SUPPORT_MQTT_EVENT + "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE +#endif +#ifdef SUPPORT_IF_STATEMENT + "|" D_CMND_IF +#endif + ; + +void (* const RulesCommand[])(void) PROGMEM = { + &CmndRule, &CmndRuleTimer, &CmndEvent, &CmndVariable, &CmndMemory, + &CmndAddition, &CmndSubtract, &CmndMultiply, &CmndScale, &CmndCalcResolution +#ifdef SUPPORT_MQTT_EVENT + , &CmndSubscribe, &CmndUnsubscribe +#endif +#ifdef SUPPORT_IF_STATEMENT + , &CmndIf +#endif + }; + +#ifdef SUPPORT_MQTT_EVENT + #include + typedef struct { + String Event; + String Topic; + String Key; + } MQTT_Subscription; + LinkedList subscriptions; +#endif + +struct RULES { + String event_value; + unsigned long timer[MAX_RULE_TIMERS] = { 0 }; + uint32_t triggers[MAX_RULE_SETS] = { 0 }; + uint8_t trigger_count[MAX_RULE_SETS] = { 0 }; + + long new_power = -1; + long old_power = -1; + long old_dimm = -1; + + uint16_t last_minute = 60; + uint16_t vars_event = 0; + uint8_t mems_event = 0; + bool teleperiod = false; + + char event_data[100]; +} Rules; + +char rules_vars[MAX_RULE_VARS][33] = {{ 0 }}; + +#if (MAX_RULE_VARS>16) +#error MAX_RULE_VARS is bigger than 16 +#endif +#if (MAX_RULE_MEMS>16) +#error MAX_RULE_MEMS is bigger than 16 +#endif + + + +bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) +{ + + + + + bool match = false; + char stemp[10]; + + + int pos = rule.indexOf('#'); + if (pos == -1) { return false; } + + String rule_task = rule.substring(0, pos); + if (Rules.teleperiod) { + int ppos = rule_task.indexOf("TELE-"); + if (ppos == -1) { return false; } + rule_task = rule.substring(5, pos); + } + + String rule_expr = rule.substring(pos +1); + String rule_name, rule_param; + int8_t compareOperator = parseCompareExpression(rule_expr, rule_name, rule_param); + + char rule_svalue[80] = { 0 }; + float rule_value = 0; + if (compareOperator != COMPARE_OPERATOR_NONE) { + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); + if (rule_param.startsWith(stemp)) { + rule_param = rules_vars[i]; + break; + } + } + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); + if (rule_param.startsWith(stemp)) { + rule_param = SettingsText(SET_MEM1 + i); + break; + } + } + snprintf_P(stemp, sizeof(stemp), PSTR("%%TIME%%")); + if (rule_param.startsWith(stemp)) { + rule_param = String(MinutesPastMidnight()); + } + snprintf_P(stemp, sizeof(stemp), PSTR("%%UPTIME%%")); + if (rule_param.startsWith(stemp)) { + rule_param = String(MinutesUptime()); + } + snprintf_P(stemp, sizeof(stemp), PSTR("%%TIMESTAMP%%")); + if (rule_param.startsWith(stemp)) { + rule_param = GetDateAndTime(DT_LOCAL).c_str(); + } +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNRISE%%")); + if (rule_param.startsWith(stemp)) { + rule_param = String(SunMinutes(0)); + } + snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNSET%%")); + if (rule_param.startsWith(stemp)) { + rule_param = String(SunMinutes(1)); + } +#endif + rule_param.toUpperCase(); + strlcpy(rule_svalue, rule_param.c_str(), sizeof(rule_svalue)); + + int temp_value = GetStateNumber(rule_svalue); + if (temp_value > -1) { + rule_value = temp_value; + } else { + rule_value = CharToFloat((char*)rule_svalue); + } + } + + + int rule_name_idx = 0; + if ((pos = rule_name.indexOf("[")) > 0) { + rule_name_idx = rule_name.substring(pos +1).toInt(); + if ((rule_name_idx < 1) || (rule_name_idx > 6)) { + rule_name_idx = 1; + } + rule_name = rule_name.substring(0, pos); + } + + StaticJsonBuffer<1024> jsonBuf; + JsonObject &root = jsonBuf.parseObject(event); + if (!root.success()) { return false; } + if (!root[rule_task].success()) { return false; } + + JsonObject &obj1 = root[rule_task]; + JsonObject *obj = &obj1; + String subtype; + uint32_t i = 0; + while ((pos = rule_name.indexOf("#")) > 0) { + subtype = rule_name.substring(0, pos); + if (!(*obj)[subtype].success()) { return false; } + JsonObject &obj2 = (*obj)[subtype]; + obj = &obj2; + rule_name = rule_name.substring(pos +1); + if (i++ > 10) { return false; } + } + if (!(*obj)[rule_name].success()) { return false; } + const char* str_value; + if (rule_name_idx) { + str_value = (*obj)[rule_name][rule_name_idx -1]; + } else { + str_value = (*obj)[rule_name]; + } + + + + + Rules.event_value = str_value; + + + float value = 0; + if (str_value) { + value = CharToFloat((char*)str_value); + int int_value = int(value); + int int_rule_value = int(rule_value); + switch (compareOperator) { + case COMPARE_OPERATOR_EXACT_DIVISION: + match = (int_rule_value && (int_value % int_rule_value) == 0); + break; + case COMPARE_OPERATOR_EQUAL: + match = (!strcasecmp(str_value, rule_svalue)); + break; + case COMPARE_OPERATOR_BIGGER: + match = (value > rule_value); + break; + case COMPARE_OPERATOR_SMALLER: + match = (value < rule_value); + break; + case COMPARE_OPERATOR_NUMBER_EQUAL: + match = (value == rule_value); + break; + case COMPARE_OPERATOR_NOT_EQUAL: + match = (value != rule_value); + break; + case COMPARE_OPERATOR_BIGGER_EQUAL: + match = (value >= rule_value); + break; + case COMPARE_OPERATOR_SMALLER_EQUAL: + match = (value <= rule_value); + break; + default: + match = true; + } + } else match = true; + + if (bitRead(Settings.rule_once, rule_set)) { + if (match) { + if (!bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set])) { + bitSet(Rules.triggers[rule_set], Rules.trigger_count[rule_set]); + } else { + match = false; + } + } else { + bitClear(Rules.triggers[rule_set], Rules.trigger_count[rule_set]); + } + } + + return match; +} +# 363 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +int8_t parseCompareExpression(String &expr, String &leftExpr, String &rightExpr) +{ + char compare_operator[3]; + int8_t compare = COMPARE_OPERATOR_NONE; + leftExpr = expr; + int position; + for (int8_t i = MAXIMUM_COMPARE_OPERATOR; i >= 0; i--) { + snprintf_P(compare_operator, sizeof(compare_operator), kCompareOperators + (i *2)); + if ((position = expr.indexOf(compare_operator)) > 0) { + compare = i; + leftExpr = expr.substring(0, position); + leftExpr.trim(); + rightExpr = expr.substring(position + strlen(compare_operator)); + rightExpr.trim(); + break; + } + } + return compare; +} + +void RulesVarReplace(String &commands, const String &sfind, const String &replace) +{ + + + + char *find = (char*)sfind.c_str(); + uint32_t flen = strlen(find); + + String ucommand = commands; + ucommand.toUpperCase(); + char *read_from = (char*)ucommand.c_str(); + char *write_to = (char*)commands.c_str(); + char *found_at; + while ((found_at = strstr(read_from, find)) != nullptr) { + write_to += (found_at - read_from); + memmove_P(write_to, find, flen); + write_to += flen; + read_from = found_at + flen; + } + + commands.replace(find, replace); +} + + + +bool RuleSetProcess(uint8_t rule_set, String &event_saved) +{ + bool serviced = false; + char stemp[10]; + + delay(0); + + + + String rules = Settings.rules[rule_set]; + + Rules.trigger_count[rule_set] = 0; + int plen = 0; + int plen2 = 0; + bool stop_all_rules = false; + while (true) { + rules = rules.substring(plen); + rules.trim(); + if (!rules.length()) { return serviced; } + + String rule = rules; + rule.toUpperCase(); + if (!rule.startsWith("ON ")) { return serviced; } + + int pevt = rule.indexOf(" DO "); + if (pevt == -1) { return serviced; } + String event_trigger = rule.substring(3, pevt); + + plen = rule.indexOf(" ENDON"); + plen2 = rule.indexOf(" BREAK"); + if ((plen == -1) && (plen2 == -1)) { return serviced; } + + if (plen == -1) { plen = 9999; } + if (plen2 == -1) { plen2 = 9999; } + plen = tmin(plen, plen2); + + String commands = rules.substring(pevt +4, plen); + Rules.event_value = ""; + String event = event_saved; + + + + if (RulesRuleMatch(rule_set, event, event_trigger)) { + if (plen == plen2) { stop_all_rules = true; } + commands.trim(); + String ucommand = commands; + ucommand.toUpperCase(); + + + + if ((ucommand.indexOf("IF ") == -1) && + (ucommand.indexOf("EVENT ") != -1) && + (ucommand.indexOf("BACKLOG ") == -1)) { + commands = "backlog " + commands; + } + + RulesVarReplace(commands, F("%VALUE%"), Rules.event_value); + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); + RulesVarReplace(commands, stemp, rules_vars[i]); + } + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); + RulesVarReplace(commands, stemp, SettingsText(SET_MEM1 +i)); + } + RulesVarReplace(commands, F("%TIME%"), String(MinutesPastMidnight())); + RulesVarReplace(commands, F("%UPTIME%"), String(MinutesUptime())); + RulesVarReplace(commands, F("%TIMESTAMP%"), GetDateAndTime(DT_LOCAL)); + RulesVarReplace(commands, F("%TOPIC%"), SettingsText(SET_MQTT_TOPIC)); +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0))); + RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1))); +#endif + + char command[commands.length() +1]; + strlcpy(command, commands.c_str(), sizeof(command)); + + AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: %s performs \"%s\""), event_trigger.c_str(), command); + + + +#ifdef SUPPORT_IF_STATEMENT + char *pCmd = command; + RulesPreprocessCommand(pCmd); +#endif + ExecuteCommand(command, SRC_RULE); + serviced = true; + if (stop_all_rules) { return serviced; } + } + plen += 6; + Rules.trigger_count[rule_set]++; + } + return serviced; +} + + + +bool RulesProcessEvent(char *json_event) +{ + bool serviced = false; + +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("RulesProcessEvent")); +#endif + + String event_saved = json_event; + + + + char *p = strchr(json_event, ':'); + if ((p != NULL) && !(strchr(++p, ':'))) { + event_saved.replace(F(":"), F(":{\"Data\":")); + event_saved += F("}"); + + } + event_saved.toUpperCase(); + + + + for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { + if (strlen(Settings.rules[i]) && bitRead(Settings.rule_enabled, i)) { + if (RuleSetProcess(i, event_saved)) { serviced = true; } + } + } + return serviced; +} + +bool RulesProcess(void) +{ + return RulesProcessEvent(mqtt_data); +} + +void RulesInit(void) +{ + rules_flag.data = 0; + for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { + if (Settings.rules[i][0] == '\0') { + bitWrite(Settings.rule_enabled, i, 0); + bitWrite(Settings.rule_once, i, 0); + } + } + Rules.teleperiod = false; +} + +void RulesEvery50ms(void) +{ + if (Settings.rule_enabled) { + char json_event[120]; + + if (-1 == Rules.new_power) { Rules.new_power = power; } + if (Rules.new_power != Rules.old_power) { + if (Rules.old_power != -1) { + for (uint32_t i = 0; i < devices_present; i++) { + uint8_t new_state = (Rules.new_power >> i) &1; + if (new_state != ((Rules.old_power >> i) &1)) { + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"State\":%d}}"), i +1, new_state); + RulesProcessEvent(json_event); + } + } + } else { + + for (uint32_t i = 0; i < devices_present; i++) { + uint8_t new_state = (Rules.new_power >> i) &1; + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"Boot\":%d}}"), i +1, new_state); + RulesProcessEvent(json_event); + } + + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { +#ifdef USE_TM1638 + if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { +#else + if (pin[GPIO_SWT1 +i] < 99) { +#endif + snprintf_P(json_event, sizeof(json_event), PSTR("{\"" D_JSON_SWITCH "%d\":{\"Boot\":%d}}"), i +1, (SwitchState(i))); + RulesProcessEvent(json_event); + } + } + } + Rules.old_power = Rules.new_power; + } + else if (Rules.old_dimm != Settings.light_dimmer) { + if (Rules.old_dimm != -1) { + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Dimmer\":{\"State\":%d}}"), Settings.light_dimmer); + } else { + + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Dimmer\":{\"Boot\":%d}}"), Settings.light_dimmer); + } + RulesProcessEvent(json_event); + Rules.old_dimm = Settings.light_dimmer; + } + else if (Rules.event_data[0]) { + char *event; + char *parameter; + event = strtok_r(Rules.event_data, "=", ¶meter); + if (event) { + event = Trim(event); + if (parameter) { + parameter = Trim(parameter); + } else { + parameter = event + strlen(event); + } + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Event\":{\"%s\":\"%s\"}}"), event, parameter); + Rules.event_data[0] ='\0'; + RulesProcessEvent(json_event); + } else { + Rules.event_data[0] ='\0'; + } + } + else if (Rules.vars_event || Rules.mems_event){ + if (Rules.vars_event) { + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + if (bitRead(Rules.vars_event, i)) { + bitClear(Rules.vars_event, i); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Var%d\":{\"State\":%s}}"), i+1, rules_vars[i]); + RulesProcessEvent(json_event); + break; + } + } + } + if (Rules.mems_event) { + for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { + if (bitRead(Rules.mems_event, i)) { + bitClear(Rules.mems_event, i); + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Mem%d\":{\"State\":%s}}"), i+1, SettingsText(SET_MEM1 +i)); + RulesProcessEvent(json_event); + break; + } + } + } + } + else if (rules_flag.data) { + uint16_t mask = 1; + for (uint32_t i = 0; i < MAX_RULES_FLAG; i++) { + if (rules_flag.data & mask) { + rules_flag.data ^= mask; + json_event[0] = '\0'; + switch (i) { + case 0: strncpy_P(json_event, PSTR("{\"System\":{\"Boot\":1}}"), sizeof(json_event)); break; + case 1: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Initialized\":%d}}"), MinutesPastMidnight()); break; + case 2: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Set\":%d}}"), MinutesPastMidnight()); break; + case 3: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Connected\":1}}"), sizeof(json_event)); break; + case 4: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Disconnected\":1}}"), sizeof(json_event)); break; + case 5: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Connected\":1}}"), sizeof(json_event)); break; + case 6: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Disconnected\":1}}"), sizeof(json_event)); break; + case 7: strncpy_P(json_event, PSTR("{\"HTTP\":{\"Initialized\":1}}"), sizeof(json_event)); break; +#ifdef USE_SHUTTER + case 8: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moved\":1}}"), sizeof(json_event)); break; + case 9: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moving\":1}}"), sizeof(json_event)); break; +#endif + } + if (json_event[0]) { + RulesProcessEvent(json_event); + break; + } + } + mask <<= 1; + } + } + } +} + +uint8_t rules_xsns_index = 0; + +void RulesEvery100ms(void) +{ + if (Settings.rule_enabled && (uptime > 4)) { + mqtt_data[0] = '\0'; + int tele_period_save = tele_period; + tele_period = 2; + XsnsNextCall(FUNC_JSON_APPEND, rules_xsns_index); + tele_period = tele_period_save; + if (strlen(mqtt_data)) { + mqtt_data[0] = '{'; + ResponseJsonEnd(); + RulesProcess(); + } + } +} + +void RulesEverySecond(void) +{ + if (Settings.rule_enabled) { + char json_event[120]; + + if (RtcTime.valid) { + if ((uptime > 60) && (RtcTime.minute != Rules.last_minute)) { + Rules.last_minute = RtcTime.minute; + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Minute\":%d}}"), MinutesPastMidnight()); + RulesProcessEvent(json_event); + } + } + for (uint32_t i = 0; i < MAX_RULE_TIMERS; i++) { + if (Rules.timer[i] != 0L) { + if (TimeReached(Rules.timer[i])) { + Rules.timer[i] = 0L; + snprintf_P(json_event, sizeof(json_event), PSTR("{\"Rules\":{\"Timer\":%d}}"), i +1); + RulesProcessEvent(json_event); + } + } + } + } +} + +void RulesSaveBeforeRestart(void) +{ + if (Settings.rule_enabled) { + char json_event[32]; + + strncpy_P(json_event, PSTR("{\"System\":{\"Save\":1}}"), sizeof(json_event)); + RulesProcessEvent(json_event); + } +} + +void RulesSetPower(void) +{ + Rules.new_power = XdrvMailbox.index; +} + +void RulesTeleperiod(void) +{ + Rules.teleperiod = true; + RulesProcess(); + Rules.teleperiod = false; +} + +#ifdef SUPPORT_MQTT_EVENT +# 744 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +bool RulesMqttData(void) +{ + if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { + return false; + } + bool serviced = false; + String sTopic = XdrvMailbox.topic; + String sData = XdrvMailbox.data; + + MQTT_Subscription event_item; + + for (uint32_t index = 0; index < subscriptions.size(); index++) { + event_item = subscriptions.get(index); + + + if (sTopic.startsWith(event_item.Topic)) { + + serviced = true; + String value; + if (event_item.Key.length() == 0) { + value = sData; + } else { + StaticJsonBuffer<500> jsonBuf; + JsonObject& jsonData = jsonBuf.parseObject(sData); + String key1 = event_item.Key; + String key2; + if (!jsonData.success()) break; + int dot; + if ((dot = key1.indexOf('.')) > 0) { + key2 = key1.substring(dot+1); + key1 = key1.substring(0, dot); + if (!jsonData[key1][key2].success()) break; + value = (const char *)jsonData[key1][key2]; + } else { + if (!jsonData[key1].success()) break; + value = (const char *)jsonData[key1]; + } + } + value.trim(); + + snprintf_P(Rules.event_data, sizeof(Rules.event_data), PSTR("%s=%s"), event_item.Event.c_str(), value.c_str()); + } + } + return serviced; +} +# 806 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +void CmndSubscribe(void) +{ + MQTT_Subscription subscription_item; + String events; + if (XdrvMailbox.data_len > 0) { + char parameters[XdrvMailbox.data_len+1]; + memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len); + parameters[XdrvMailbox.data_len] = '\0'; + String event_name, topic, key; + + char * pos = strtok(parameters, ","); + if (pos) { + event_name = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + topic = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + key = Trim(pos); + } + } + } + + event_name.toUpperCase(); + if (event_name.length() > 0 && topic.length() > 0) { + + for (uint32_t index=0; index < subscriptions.size(); index++) { + if (subscriptions.get(index).Event.equals(event_name)) { + + String stopic = subscriptions.get(index).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(index); + break; + } + } + + if (!topic.endsWith("#")) { + if (topic.endsWith("/")) { + topic.concat("#"); + } else { + topic.concat("/#"); + } + } + + + subscription_item.Event = event_name; + subscription_item.Topic = topic.substring(0, topic.length() - 2); + subscription_item.Key = key; + subscriptions.add(subscription_item); + + MqttSubscribe(topic.c_str()); + events.concat(event_name + "," + topic + + (key.length()>0 ? "," : "") + + key); + } else { + events = D_JSON_WRONG_PARAMETERS; + } + } else { + + for (uint32_t index=0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + events.concat(subscription_item.Event + "," + subscription_item.Topic + + (subscription_item.Key.length()>0 ? "," : "") + + subscription_item.Key + "; "); + } + } + ResponseCmndChar(events.c_str()); +} +# 886 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +void CmndUnsubscribe(void) +{ + MQTT_Subscription subscription_item; + String events; + if (XdrvMailbox.data_len > 0) { + for (uint32_t index = 0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + if (subscription_item.Event.equalsIgnoreCase(XdrvMailbox.data)) { + String stopic = subscription_item.Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + events = subscription_item.Event; + subscriptions.remove(index); + break; + } + } + } else { + + String stopic; + while (subscriptions.size() > 0) { + events.concat(subscriptions.get(0).Event + "; "); + stopic = subscriptions.get(0).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(0); + } + } + ResponseCmndChar(events.c_str()); +} + +#endif + +#ifdef USE_EXPRESSION +# 928 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +char * findClosureBracket(char * pStart) +{ + char * pointer = pStart + 1; + + bool bFindClosures = false; + uint8_t matchClosures = 1; + while (*pointer) + { + if (*pointer == ')') { + matchClosures--; + if (matchClosures == 0) { + bFindClosures = true; + break; + } + } else if (*pointer == '(') { + matchClosures++; + } + pointer++; + } + if (bFindClosures) { + return pointer; + } else { + return nullptr; + } +} +# 967 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextNumber(char * &pNumber, float &value) +{ + bool bSucceed = false; + String sNumber = ""; + if (*pNumber == '-') { + sNumber = "-"; + pNumber++; + } + while (*pNumber) { + if (isdigit(*pNumber) || (*pNumber == '.')) { + sNumber += *pNumber; + pNumber++; + } else { + break; + } + } + if (sNumber.length() > 0) { + value = CharToFloat(sNumber.c_str()); + bSucceed = true; + } + return bSucceed; +} +# 1003 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextVariableValue(char * &pVarname, float &value) +{ + bool succeed = true; + value = 0; + String sVarName = ""; + while (*pVarname) { + if (isalpha(*pVarname) || isdigit(*pVarname)) { + sVarName.concat(*pVarname); + pVarname++; + } else { + break; + } + } + sVarName.toUpperCase(); + if (sVarName.startsWith(F("VAR"))) { + int index = sVarName.substring(3).toInt(); + if (index > 0 && index <= MAX_RULE_VARS) { + value = CharToFloat(rules_vars[index -1]); + } + } else if (sVarName.startsWith(F("MEM"))) { + int index = sVarName.substring(3).toInt(); + if (index > 0 && index <= MAX_RULE_MEMS) { + value = CharToFloat(SettingsText(SET_MEM1 + index -1)); + } + } else if (sVarName.equals(F("TIME"))) { + value = MinutesPastMidnight(); + } else if (sVarName.equals(F("UPTIME"))) { + value = MinutesUptime(); + } else if (sVarName.equals(F("UTCTIME"))) { + value = UtcTime(); + } else if (sVarName.equals(F("LOCALTIME"))) { + value = LocalTime(); +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + } else if (sVarName.equals(F("SUNRISE"))) { + value = SunMinutes(0); + } else if (sVarName.equals(F("SUNSET"))) { + value = SunMinutes(1); +#endif + } else { + succeed = false; + } + + return succeed; +} +# 1065 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextObjectValue(char * &pointer, float &value) +{ + bool bSucceed = false; + while (*pointer) + { + if (isspace(*pointer)) { + pointer++; + continue; + } + if (isdigit(*pointer) || (*pointer) == '-') { + bSucceed = findNextNumber(pointer, value); + break; + } else if (isalpha(*pointer)) { + bSucceed = findNextVariableValue(pointer, value); + break; + } else if (*pointer == '(') { + char * closureBracket = findClosureBracket(pointer); + if (closureBracket != nullptr) { + value = evaluateExpression(pointer+1, closureBracket - pointer - 1); + pointer = closureBracket + 1; + bSucceed = true; + } + break; + } else { + break; + } + } + return bSucceed; +} +# 1109 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextOperator(char * &pointer, int8_t &op) +{ + bool bSucceed = false; + while (*pointer) + { + if (isspace(*pointer)) { + pointer++; + continue; + } + op = EXPRESSION_OPERATOR_ADD; + const char *pch = kExpressionOperators; + char ch; + while ((ch = pgm_read_byte(pch++)) != '\0') { + if (ch == *pointer) { + bSucceed = true; + pointer++; + break; + } + op++; + } + break; + } + return bSucceed; +} +# 1146 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +float calculateTwoValues(float v1, float v2, uint8_t op) +{ + switch (op) + { + case EXPRESSION_OPERATOR_ADD: + return v1 + v2; + case EXPRESSION_OPERATOR_SUBTRACT: + return v1 - v2; + case EXPRESSION_OPERATOR_MULTIPLY: + return v1 * v2; + case EXPRESSION_OPERATOR_DIVIDEDBY: + return (0 == v2) ? 0 : (v1 / v2); + case EXPRESSION_OPERATOR_MODULO: + return (0 == v2) ? 0 : (int(v1) % int(v2)); + case EXPRESSION_OPERATOR_POWER: + return FastPrecisePow(v1, v2); + } + return 0; +} +# 1199 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +float evaluateExpression(const char * expression, unsigned int len) +{ + char expbuf[len + 1]; + memcpy(expbuf, expression, len); + expbuf[len] = '\0'; + char * scan_pointer = expbuf; + + LinkedList object_values; + LinkedList operators; + int8_t op; + float va; + + if (findNextObjectValue(scan_pointer, va)) { + object_values.add(va); + } else { + return 0; + } + while (*scan_pointer) + { + if (findNextOperator(scan_pointer, op) + && *scan_pointer + && findNextObjectValue(scan_pointer, va)) + { + operators.add(op); + object_values.add(va); + } else { + + break; + } + } + + + + for (int32_t priority = MAX_EXPRESSION_OPERATOR_PRIORITY; priority>0; priority--) { + int index = 0; + while (index < operators.size()) { + if (priority == pgm_read_byte(kExpressionOperatorsPriorities + operators.get(index))) { + + va = calculateTwoValues(object_values.get(index), object_values.remove(index + 1), operators.remove(index)); + + object_values.set(index, va); + } else { + index++; + } + } + } + return object_values.get(0); +} +#endif + +#ifdef SUPPORT_IF_STATEMENT +void CmndIf(void) +{ + if (XdrvMailbox.data_len > 0) { + char parameters[XdrvMailbox.data_len+1]; + memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len); + parameters[XdrvMailbox.data_len] = '\0'; + ProcessIfStatement(parameters); + } + ResponseCmndDone(); +} +# 1273 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +bool evaluateComparisonExpression(const char *expression, int len) +{ + bool bResult = true; + char expbuf[len + 1]; + memcpy(expbuf, expression, len); + expbuf[len] = '\0'; + String compare_expression = expbuf; + String leftExpr, rightExpr; + int8_t compareOp = parseCompareExpression(compare_expression, leftExpr, rightExpr); + + double leftValue = evaluateExpression(leftExpr.c_str(), leftExpr.length()); + double rightValue = evaluateExpression(rightExpr.c_str(), rightExpr.length()); + switch (compareOp) { + case COMPARE_OPERATOR_EXACT_DIVISION: + bResult = (rightValue != 0 && leftValue == int(leftValue) + && rightValue == int(rightValue) && (int(leftValue) % int(rightValue)) == 0); + break; + case COMPARE_OPERATOR_EQUAL: + bResult = leftExpr.equalsIgnoreCase(rightExpr); + break; + case COMPARE_OPERATOR_BIGGER: + bResult = (leftValue > rightValue); + break; + case COMPARE_OPERATOR_SMALLER: + bResult = (leftValue < rightValue); + break; + case COMPARE_OPERATOR_NUMBER_EQUAL: + bResult = (leftValue == rightValue); + break; + case COMPARE_OPERATOR_NOT_EQUAL: + bResult = (leftValue != rightValue); + break; + case COMPARE_OPERATOR_BIGGER_EQUAL: + bResult = (leftValue >= rightValue); + break; + case COMPARE_OPERATOR_SMALLER_EQUAL: + bResult = (leftValue <= rightValue); + break; + } + return bResult; +} +# 1329 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextLogicOperator(char * &pointer, int8_t &op) +{ + bool bSucceed = false; + while (*pointer && isspace(*pointer)) { + + pointer++; + } + if (*pointer) { + if (strncasecmp_P(pointer, PSTR("AND "), 4) == 0) { + op = LOGIC_OPERATOR_AND; + pointer += 4; + bSucceed = true; + } else if (strncasecmp_P(pointer, PSTR("OR "), 3) == 0) { + op = LOGIC_OPERATOR_OR; + pointer += 3; + bSucceed = true; + } + } + return bSucceed; +} +# 1366 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +bool findNextLogicObjectValue(char * &pointer, bool &value) +{ + bool bSucceed = false; + while (*pointer && isspace(*pointer)) { + + pointer++; + } + char * pExpr = pointer; + while (*pointer) { + if (isalpha(*pointer) + && (strncasecmp_P(pointer, PSTR("AND "), 4) == 0 + || strncasecmp_P(pointer, PSTR("OR "), 3) == 0)) + { + value = evaluateComparisonExpression(pExpr, pointer - pExpr); + bSucceed = true; + break; + } else if (*pointer == '(') { + char * closureBracket = findClosureBracket(pointer); + if (closureBracket != nullptr) { + value = evaluateLogicalExpression(pointer+1, closureBracket - pointer - 1); + pointer = closureBracket + 1; + bSucceed = true; + } + break; + } + pointer++; + } + if (!bSucceed && pointer > pExpr) { + + value = evaluateComparisonExpression(pExpr, pointer - pExpr); + bSucceed = true; + } + return bSucceed; +} +# 1415 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +bool evaluateLogicalExpression(const char * expression, int len) +{ + bool bResult = false; + + char expbuff[len + 1]; + memcpy(expbuff, expression, len); + expbuff[len] = '\0'; + + + char * pointer = expbuff; + LinkedList values; + LinkedList logicOperators; + + bool bValue; + if (findNextLogicObjectValue(pointer, bValue)) { + values.add(bValue); + } else { + return false; + } + int8_t op; + while (*pointer) { + if (findNextLogicOperator(pointer, op) + && (*pointer) && findNextLogicObjectValue(pointer, bValue)) + { + logicOperators.add(op); + values.add(bValue); + } else { + break; + } + } + + int index = 0; + while (index < logicOperators.size()) { + if (logicOperators.get(index) == LOGIC_OPERATOR_AND) { + values.set(index, values.get(index) && values.get(index+1)); + values.remove(index + 1); + logicOperators.remove(index); + } else { + index++; + } + } + + index = 0; + while (index < logicOperators.size()) { + if (logicOperators.get(index) == LOGIC_OPERATOR_OR) { + values.set(index, values.get(index) || values.get(index+1)); + values.remove(index + 1); + logicOperators.remove(index); + } else { + index++; + } + } + return values.get(0); +} +# 1486 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +int8_t findIfBlock(char * &pointer, int &lenWord, int8_t block_type) +{ + int8_t foundBlock = IF_BLOCK_INVALID; + + const char * word; + while (*pointer) { + if (!isalpha(*pointer)) { + pointer++; + continue; + } + word = pointer; + while (*pointer && isalpha(*pointer)) { + pointer++; + } + lenWord = pointer - word; + + if (2 == lenWord && 0 == strncasecmp_P(word, PSTR("IF"), 2)) { + + + if (findIfBlock(pointer, lenWord, IF_BLOCK_ENDIF) != IF_BLOCK_ENDIF) { + + break; + } + } else if ( (IF_BLOCK_ENDIF == block_type || IF_BLOCK_ANY == block_type) + && (5 == lenWord) && (0 == strncasecmp_P(word, PSTR("ENDIF"), 5))) + { + + foundBlock = IF_BLOCK_ENDIF; + break; + } else if ( (IF_BLOCK_ELSEIF == block_type || IF_BLOCK_ANY == block_type) + && (6 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSEIF"), 6))) + { + + foundBlock = IF_BLOCK_ELSEIF; + break; + } else if ( (IF_BLOCK_ELSE == block_type || IF_BLOCK_ANY == block_type) + && (4 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSE"), 4))) + { + + foundBlock = IF_BLOCK_ELSE; + break; + } + } + return foundBlock; +} +# 1543 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +void ExecuteCommandBlock(const char * commands, int len) +{ + char cmdbuff[len + 1]; + memcpy(cmdbuff, commands, len); + cmdbuff[len] = '\0'; + + + char oneCommand[len + 1]; + int insertPosition = 0; + char * pos = cmdbuff; + int lenEndBlock = 0; + while (*pos) { + if (isspace(*pos) || '\x1e' == *pos || ';' == *pos) { + pos++; + continue; + } + if (strncasecmp_P(pos, PSTR("BACKLOG "), 8) == 0) { + + pos += 8; + continue; + } + if (strncasecmp_P(pos, PSTR("IF "), 3) == 0) { + + + char *pEndif = pos + 3; + if (IF_BLOCK_ENDIF != findIfBlock(pEndif, lenEndBlock, IF_BLOCK_ENDIF)) { + + break; + } + + memcpy(oneCommand, pos, pEndif - pos); + oneCommand[pEndif - pos] = '\0'; + pos = pEndif; + } else { + + char *pEndOfCommand = strpbrk(pos, "\x1e;"); + if (NULL == pEndOfCommand) { + pEndOfCommand = pos + strlen(pos); + } + memcpy(oneCommand, pos, pEndOfCommand - pos); + oneCommand[pEndOfCommand - pos] = '\0'; + pos = pEndOfCommand; + } + + + String sCurrentCommand = oneCommand; + sCurrentCommand.trim(); + if (sCurrentCommand.length() > 0 + && backlog.size() < MAX_BACKLOG && !backlog_mutex) + { + + backlog_mutex = true; + backlog.add(insertPosition, sCurrentCommand); + backlog_mutex = false; + insertPosition++; + } + } + return; +} +# 1613 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +void ProcessIfStatement(const char* statements) +{ + String conditionExpression; + int len = strlen(statements); + char statbuff[len + 1]; + memcpy(statbuff, statements, len + 1); + char *pos = statbuff; + int lenEndBlock = 0; + while (true) { + + + while (*pos && *pos != '(') { + pos++; + } + if (0 == *pos) { break; } + char * posEnd = findClosureBracket(pos); + + if (true == evaluateLogicalExpression(pos + 1, posEnd - (pos + 1))) { + + char * cmdBlockStart = posEnd + 1; + char * cmdBlockEnd = cmdBlockStart; + int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ANY); + if (IF_BLOCK_INVALID == nextBlock) { + + break; + } + ExecuteCommandBlock(cmdBlockStart, cmdBlockEnd - cmdBlockStart - lenEndBlock); + pos = cmdBlockEnd; + break; + } else { + pos = posEnd + 1; + int8_t nextBlock = findIfBlock(pos, lenEndBlock, IF_BLOCK_ANY); + if (IF_BLOCK_ELSEIF == nextBlock) { + + continue; + } else if (IF_BLOCK_ELSE == nextBlock) { + + char * cmdBlockEnd = pos; + int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ENDIF); + if (IF_BLOCK_ENDIF != nextBlock) { + + break; + } + ExecuteCommandBlock(pos, cmdBlockEnd - pos - lenEndBlock); + break; + } else { + + break; + } + } + } +} +# 1677 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" +void RulesPreprocessCommand(char *pCommands) +{ + char * cmd = pCommands; + int lenEndBlock = 0; + while (*cmd) { + + if (';' == *cmd || isspace(*cmd)) { + cmd++; + } + else if (strncasecmp_P(cmd, PSTR("IF "), 3) == 0) { + + char * pIfStart = cmd; + char * pIfEnd = pIfStart + 3; + + if (IF_BLOCK_ENDIF == findIfBlock(pIfEnd, lenEndBlock, IF_BLOCK_ENDIF)) { + + cmd = pIfEnd; + + + while (pIfStart < pIfEnd) { + if (';' == *pIfStart) + *pIfStart = '\x1e'; + pIfStart++; + } + } + else { + break; + } + } + else { + while (*cmd && ';' != *cmd) { + cmd++; + } + } + } + return; +} +#endif + + + + + +void CmndRule(void) +{ + uint8_t index = XdrvMailbox.index; + if ((index > 0) && (index <= MAX_RULE_SETS)) { + if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.rules[index -1]))) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) { + switch (XdrvMailbox.payload) { + case 0: + case 1: + bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); + break; + case 2: + bitWrite(Settings.rule_enabled, index -1, bitRead(Settings.rule_enabled, index -1) ^1); + break; + case 4: + case 5: + bitWrite(Settings.rule_once, index -1, XdrvMailbox.payload &1); + break; + case 6: + bitWrite(Settings.rule_once, index -1, bitRead(Settings.rule_once, index -1) ^1); + break; + case 8: + case 9: + bitWrite(Settings.rule_stop, index -1, XdrvMailbox.payload &1); + break; + case 10: + bitWrite(Settings.rule_stop, index -1, bitRead(Settings.rule_stop, index -1) ^1); + break; + } + } else { + int offset = 0; + if ('+' == XdrvMailbox.data[0]) { + offset = strlen(Settings.rules[index -1]); + if (XdrvMailbox.data_len < (sizeof(Settings.rules[index -1]) - offset -1)) { + XdrvMailbox.data[0] = ' '; + } else { + offset = -1; + } + } + if (offset != -1) { + strlcpy(Settings.rules[index -1] + offset, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.rules[index -1])); + } + } + Rules.triggers[index -1] = 0; + } + snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Free\":%d,\"Rules\":\"%s\"}"), + XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)), + GetStateText(bitRead(Settings.rule_stop, index -1)), sizeof(Settings.rules[index -1]) - strlen(Settings.rules[index -1]) -1, Settings.rules[index -1]); + } +} + +void CmndRuleTimer(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_TIMERS)) { + if (XdrvMailbox.data_len > 0) { +#ifdef USE_EXPRESSION + float timer_set = evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len); + Rules.timer[XdrvMailbox.index -1] = (timer_set > 0) ? millis() + (1000 * timer_set) : 0; +#else + Rules.timer[XdrvMailbox.index -1] = (XdrvMailbox.payload > 0) ? millis() + (1000 * XdrvMailbox.payload) : 0; +#endif + } + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < MAX_RULE_TIMERS; i++) { + ResponseAppend_P(PSTR("%c\"T%d\":%d"), (i) ? ',' : '{', i +1, (Rules.timer[i]) ? (Rules.timer[i] - millis()) / 1000 : 0); + } + ResponseJsonEnd(); + } +} + +void CmndEvent(void) +{ + if (XdrvMailbox.data_len > 0) { + strlcpy(Rules.event_data, XdrvMailbox.data, sizeof(Rules.event_data)); + } + ResponseCmndDone(); +} + +void CmndVariable(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (!XdrvMailbox.usridx) { + mqtt_data[0] = '\0'; + for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { + ResponseAppend_P(PSTR("%c\"Var%d\":\"%s\""), (i) ? ',' : '{', i +1, rules_vars[i]); + } + ResponseJsonEnd(); + } else { + if (XdrvMailbox.data_len > 0) { +#ifdef USE_EXPRESSION + if (XdrvMailbox.data[0] == '=') { + dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + } else { + strlcpy(rules_vars[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(rules_vars[XdrvMailbox.index -1])); + } +#else + strlcpy(rules_vars[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(rules_vars[XdrvMailbox.index -1])); +#endif + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } + } +} + +void CmndMemory(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_MEMS)) { + if (!XdrvMailbox.usridx) { + ResponseCmndAll(SET_MEM1, MAX_RULE_MEMS); + } else { + if (XdrvMailbox.data_len > 0) { +#ifdef USE_EXPRESSION + if (XdrvMailbox.data[0] == '=') { + dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, SettingsText(SET_MEM1 + XdrvMailbox.index -1)); + } else { + SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); + } +#else + SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); +#endif + bitSet(Rules.mems_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(SettingsText(SET_MEM1 + XdrvMailbox.index -1)); + } + } +} + +void CmndCalcResolution(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 7)) { + Settings.flag2.calc_resolution = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.flag2.calc_resolution); +} + +void CmndAddition(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (XdrvMailbox.data_len > 0) { + float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) + CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } +} + +void CmndSubtract(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (XdrvMailbox.data_len > 0) { + float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) - CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } +} + +void CmndMultiply(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (XdrvMailbox.data_len > 0) { + float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) * CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } +} + +void CmndScale(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { + if (XdrvMailbox.data_len > 0) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + char sub_string[XdrvMailbox.data_len +1]; + + float valueIN = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 1)); + float fromLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)); + float fromHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 3)); + float toLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)); + float toHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 5)); + float value = map_double(valueIN, fromLow, fromHigh, toLow, toHigh); + dtostrfd(value, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); + bitSet(Rules.vars_event, XdrvMailbox.index -1); + } + } + ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); + } +} + +float map_double(float x, float in_min, float in_max, float out_min, float out_max) +{ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + + + + + +bool Xdrv10(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_50_MSECOND: + RulesEvery50ms(); + break; + case FUNC_EVERY_100_MSECOND: + RulesEvery100ms(); + break; + case FUNC_EVERY_SECOND: + RulesEverySecond(); + break; + case FUNC_SET_POWER: + RulesSetPower(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kRulesCommands, RulesCommand); + break; + case FUNC_RULES_PROCESS: + result = RulesProcess(); + break; + case FUNC_SAVE_BEFORE_RESTART: + RulesSaveBeforeRestart(); + break; +#ifdef SUPPORT_MQTT_EVENT + case FUNC_MQTT_DATA: + result = RulesMqttData(); + break; +#endif + case FUNC_PRE_INIT: + RulesInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" +#ifdef USE_SCRIPT +#ifndef USE_RULES +# 40 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" +#define XDRV_10 10 +#define XI2C_37 37 + +#define SCRIPT_DEBUG 0 + + +#ifndef MAXVARS +#define MAXVARS 50 +#endif +#ifndef MAXSVARS +#define MAXSVARS 5 +#endif +#define MAXNVARS MAXVARS-MAXSVARS + +#define MAXFILT 5 +#define SCRIPT_SVARSIZE 20 +#define SCRIPT_MAXSSIZE 48 +#define SCRIPT_EOL '\n' +#define SCRIPT_FLOAT_PRECISION 2 +#define PMEM_SIZE sizeof(Settings.script_pram) +#define SCRIPT_MAXPERM (PMEM_SIZE)-4/sizeof(float) +#define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS + + +#define EPOCH_OFFSET 1546300800 + +enum {OPER_EQU=1,OPER_PLS,OPER_MIN,OPER_MUL,OPER_DIV,OPER_PLSEQU,OPER_MINEQU,OPER_MULEQU,OPER_DIVEQU,OPER_EQUEQU,OPER_NOTEQU,OPER_GRTEQU,OPER_LOWEQU,OPER_GRT,OPER_LOW,OPER_PERC,OPER_XOR,OPER_AND,OPER_OR,OPER_ANDEQU,OPER_OREQU,OPER_XOREQU,OPER_PERCEQU}; +enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD}; + +#ifdef USE_SCRIPT_FATFS +#include +#include +#ifndef FAT_SCRIPT_SIZE +#define FAT_SCRIPT_SIZE 4096 +#endif +#define FAT_SCRIPT_NAME "script.txt" +#if USE_LONG_FILE_NAMES==1 +#warning ("FATFS long filenames not supported"); +#endif +#if USE_STANDARD_SPI_LIBRARY==0 +#warning ("FATFS standard spi should be used"); +#endif +#endif + +#ifdef SUPPORT_MQTT_EVENT + #include + typedef struct { + String Event; + String Topic; + String Key; + } MQTT_Subscription; + LinkedList subscriptions; +#endif + +#ifdef USE_DISPLAY +#ifdef USE_TOUCH_BUTTONS +#include +extern VButton *buttons[MAXBUTTONS]; +#endif +#endif + +typedef union { + uint8_t data; + struct { + uint8_t is_string : 1; + uint8_t is_permanent : 1; + uint8_t is_timer : 1; + uint8_t is_autoinc : 1; + uint8_t changed : 1; + uint8_t settable : 1; + uint8_t is_filter : 1; + uint8_t constant : 1; + }; +} SCRIPT_TYPE; + +struct T_INDEX { + uint8_t index; + SCRIPT_TYPE bits; +}; + +struct M_FILT { + uint8_t numvals; + uint8_t index; + float maccu; + float rbuff[1]; +}; + +typedef union { + uint8_t data; + struct { + uint8_t nutu8 : 1; + uint8_t nutu7 : 1; + uint8_t nutu6 : 1; + uint8_t nutu5 : 1; + uint8_t nutu4 : 1; + uint8_t nutu3 : 1; + uint8_t is_dir : 1; + uint8_t is_open : 1; + }; +} FILE_FLAGS; + +#define SFS_MAX 4 + +struct SCRIPT_MEM { + float *fvars; + float *s_fvars; + struct T_INDEX *type; + struct M_FILT *mfilt; + char *glob_vnp; + uint8_t *vnp_offset; + char *glob_snp; + char *scriptptr; + char *section_ptr; + char *scriptptr_bu; + char *script_ram; + uint16_t script_size; + uint8_t *script_pram; + uint16_t script_pram_size; + uint8_t numvars; + void *script_mem; + uint16_t script_mem_size; + uint8_t script_dprec; + uint8_t var_not_found; + uint8_t glob_error; + uint8_t max_ssize; + uint8_t script_loglevel; + uint8_t flags; +#ifdef USE_SCRIPT_FATFS + File files[SFS_MAX]; + FILE_FLAGS file_flags[SFS_MAX]; + uint8_t script_sd_found; + char flink[2][14]; +#endif +} glob_script_mem; + + +int16_t last_findex; +uint8_t tasm_cmd_activ=0; +uint8_t fast_script=0; +uint32_t script_lastmillis; + + +#ifdef USE_BUTTON_EVENT +int8_t script_button[MAX_KEYS]; +#endif + +char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo); +char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo); +char *ForceStringVar(char *lp,char *dstr); +void send_download(void); +uint8_t reject(char *name); + +void ScriptEverySecond(void) { + + if (bitRead(Settings.rule_enabled, 0)) { + struct T_INDEX *vtp=glob_script_mem.type; + float delta=(millis()-script_lastmillis)/1000; + script_lastmillis=millis(); + for (uint8_t count=0; count0) { + + *fp-=delta; + if (*fp<0) *fp=0; + } + } + if (vtp[count].bits.is_autoinc) { + + float *fp=&glob_script_mem.fvars[vtp[count].index]; + if (*fp>=0) { + *fp+=delta; + } + } + } + Run_Scripter(">S",2,0); + } +} + +void RulesTeleperiod(void) { + if (bitRead(Settings.rule_enabled, 0) && mqtt_data[0]) Run_Scripter(">T",2, mqtt_data); +} + + +#ifdef USE_24C256 +#ifndef USE_SCRIPT_FATFS + +#include +#define EEPROM_ADDRESS 0x50 + +#ifndef EEP_SCRIPT_SIZE +#define EEP_SCRIPT_SIZE 4095 +#endif + + +static Eeprom24C128_256 eeprom(EEPROM_ADDRESS); + +#define EEP_WRITE(A,B,C) eeprom.writeBytes(A,B,(uint8_t*)C); + +#define EEP_READ(A,B,C) eeprom.readBytes(A,B,(uint8_t*)C); +#endif +#endif + +#define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++; +#define SCRIPT_SKIP_EOL while (*lp==SCRIPT_EOL) lp++; + + +int16_t Init_Scripter(void) { +char *script; + + script=glob_script_mem.script_ram; + + + uint16_t lines=0,nvars=0,svars=0,vars=0; + char *lp=script; + char vnames[MAXVARS*10]; + char *vnames_p=vnames; + char *vnp[MAXVARS]; + char **vnp_p=vnp; + char strings[MAXSVARS*SCRIPT_MAXSSIZE]; + struct M_FILT mfilt[MAXFILT]; + + char *strings_p=strings; + char *snp[MAXSVARS]; + char **snp_p=snp; + uint8_t numperm=0,numflt=0,count; + + glob_script_mem.max_ssize=SCRIPT_SVARSIZE; + glob_script_mem.scriptptr=0; + + if (!*script) return -999; + + float fvalues[MAXVARS]; + struct T_INDEX vtypes[MAXVARS]; + char init=0; + while (1) { + + + SCRIPT_SKIP_SPACES + + if (*lp=='\n' || *lp=='\r') goto next_line; + + if (*lp==';') goto next_line; + if (init) { + + if (*lp=='>') { + init=0; + break; + } + char *op=strchr(lp,'='); + if (op) { + vtypes[vars].bits.data=0; + + if (*lp=='p' && *(lp+1)==':') { + lp+=2; + if (numpermMAXFILT) { + return -6; + } + } else { + vtypes[vars].bits.is_filter=0; + } + *vnp_p++=vnames_p; + while (lpMAXNVARS) { + return -1; + } + if (vtypes[vars].bits.is_filter) { + while (isdigit(*op) || *op=='.' || *op=='-') { + op++; + } + while (*op==' ') op++; + if (isdigit(*op)) { + + uint8_t flen=atoi(op); + mfilt[numflt-1].numvals&=0x80; + mfilt[numflt-1].numvals|=flen&0x7f; + } + } + + } else { + + op++; + *snp_p++=strings_p; + while (*op!='\"') { + if (*op==SCRIPT_EOL) break; + *strings_p++=*op++; + } + *strings_p++=0; + vtypes[vars].bits.is_string=1; + vtypes[vars].index=svars; + svars++; + if (svars>MAXSVARS) { + return -2; + } + } + vars++; + if (vars>MAXVARS) { + return -3; + } + } + } else { + if (!strncmp(lp,">D",2)) { + lp+=2; + SCRIPT_SKIP_SPACES + if (isdigit(*lp)) { + uint8_t ssize=atoi(lp)+1; + if (ssize<10 || ssize>SCRIPT_MAXSSIZE) ssize=SCRIPT_MAXSSIZE; + glob_script_mem.max_ssize=ssize; + } + init=1; + } + } + + next_line: + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + + uint16_t fsize=0; + for (count=0; count255) { + free(glob_script_mem.script_mem); + return -5; + } + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR("Script: nv=%d, tv=%d, vns=%d, ram=%d"), nvars, svars, index, glob_script_mem.script_mem_size); + + + char *cp1=glob_script_mem.glob_snp; + char *sp=strings; + for (count=0; countnumvals=mfilt[count].numvals; + mp+=sizeof(struct M_FILT)+((mfilt[count].numvals&0x7f)-1)*sizeof(float); + } + + glob_script_mem.numvars=vars; + glob_script_mem.script_dprec=SCRIPT_FLOAT_PRECISION; + glob_script_mem.script_loglevel=LOG_LEVEL_INFO; + + +#if SCRIPT_DEBUG>2 + struct T_INDEX *dvtp=glob_script_mem.type; + for (uint8_t count=0; count0 + ClaimSerial(); + SetSerialBaudrate(9600); +#endif + + + glob_script_mem.scriptptr=lp-1; + glob_script_mem.scriptptr_bu=glob_script_mem.scriptptr; + return 0; + +} + +#ifdef USE_LIGHT +#ifdef USE_WS2812 +void ws2812_set_array(float *array ,uint8_t len) { + + Ws2812ForceSuspend(); + for (uint8_t cnt=0;cntSettings.light_pixels) break; + uint32_t col=array[cnt]; + Ws2812SetColor(cnt+1,col>>16,col>>8,col,0); + } + Ws2812ForceUpdate(); +} +#endif +#endif + +#define NUM_RES 0xfe +#define STR_RES 0xfd +#define VAR_NV 0xff + +#define NTYPE 0 +#define STYPE 0x80 + +#ifndef FLT_MAX +#define FLT_MAX 99999999 +#endif + +float median_array(float *array,uint8_t len) { + uint8_t ind[len]; + uint8_t mind=0,index=0,flg; + float min=FLT_MAX; + + for (uint8_t hcnt=0; hcntnumvals&0x7f; + return mflp->rbuff; + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } + return 0; +} + + +float Get_MFVal(uint8_t index,uint8_t bind) { + uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; + for (uint8_t count=0; countnumvals&0x7f; + if (!bind) { + return mflp->index; + } + if (bind<1 || bind>maxind) bind=maxind; + return mflp->rbuff[bind-1]; + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } + return 0; +} + +void Set_MFVal(uint8_t index,uint8_t bind,float val) { + uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; + for (uint8_t count=0; countnumvals&0x7f; + if (!bind) { + mflp->index=val; + } else { + if (bind<1 || bind>maxind) bind=maxind; + mflp->rbuff[bind-1]=val; + } + return; + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } +} + + +float Get_MFilter(uint8_t index) { + uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; + for (uint8_t count=0; countnumvals&0x80) { + + return mflp->maccu/(mflp->numvals&0x7f); + } else { + + return median_array(mflp->rbuff,mflp->numvals); + } + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } + return 0; +} + +void Set_MFilter(uint8_t index, float invar) { + uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; + for (uint8_t count=0; countnumvals&0x80) { + + mflp->maccu-=mflp->rbuff[mflp->index]; + mflp->maccu+=invar; + mflp->rbuff[mflp->index]=invar; + mflp->index++; + if (mflp->index>=(mflp->numvals&0x7f)) mflp->index=0; + } else { + + mflp->rbuff[mflp->index]=invar; + mflp->index++; + if (mflp->index>=mflp->numvals) mflp->index=0; + } + break; + } + mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); + } +} + +#define MEDIAN_SIZE 5 +#define MEDIAN_FILTER_NUM 2 + +struct MEDIAN_FILTER { +float buffer[MEDIAN_SIZE]; +int8_t index; +} script_mf[MEDIAN_FILTER_NUM]; + +float DoMedian5(uint8_t index, float in) { + + if (index>=MEDIAN_FILTER_NUM) index=0; + + struct MEDIAN_FILTER* mf=&script_mf[index]; + mf->buffer[mf->index]=in; + mf->index++; + if (mf->index>=MEDIAN_SIZE) mf->index=0; + return median_array(mf->buffer,MEDIAN_SIZE); +} + +#ifdef USE_LIGHT + +uint32_t HSVToRGB(uint16_t hue, uint8_t saturation, uint8_t value) { +float r = 0, g = 0, b = 0; +struct HSV { + float H; + float S; + float V; +} hsv; + +hsv.H=hue; +hsv.S=(float)saturation/100.0; +hsv.V=(float)value/100.0; + +if (hsv.S == 0) { + r = hsv.V; + g = hsv.V; + b = hsv.V; + } else { + int i; + float f, p, q, t; + + if (hsv.H == 360) + hsv.H = 0; + else + hsv.H = hsv.H / 60; + + i = (int)trunc(hsv.H); + f = hsv.H - i; + + p = hsv.V * (1.0 - hsv.S); + q = hsv.V * (1.0 - (hsv.S * f)); + t = hsv.V * (1.0 - (hsv.S * (1.0 - f))); + + switch (i) + { + case 0: + r = hsv.V; + g = t; + b = p; + break; + + case 1: + r = q; + g = hsv.V; + b = p; + break; + + case 2: + r = p; + g = hsv.V; + b = t; + break; + + case 3: + r = p; + g = q; + b = hsv.V; + break; + + case 4: + r = t; + g = p; + b = hsv.V; + break; + + default: + r = hsv.V; + g = p; + b = q; + break; + } + + } + + uint8_t ir,ig,ib; + ir=r*255; + ig=g*255; + ib=b*255; + + uint32_t rgb=(ir<<16)|(ig<<8)|ib; + return rgb; +} +#endif + + + + +char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,JsonObject *jo) { + uint16_t count,len=0; + uint8_t nres=0; + char vname[32]; + float fvar=0; + tind->index=0; + tind->bits.data=0; + + if (isdigit(*lp) || (*lp=='-' && isdigit(*(lp+1))) || *lp=='.') { + + if (fp) { + if (*lp=='0' && *(lp+1)=='x') { + lp+=2; + *fp=strtol(lp,0,16); + } else { + *fp=CharToFloat(lp); + } + } + if (*lp=='-') lp++; + while (isdigit(*lp) || *lp=='.') { + if (*lp==0 || *lp==SCRIPT_EOL) break; + lp++; + } + tind->bits.constant=1; + tind->bits.is_string=0; + *vtype=NUM_RES; + return lp; + } + if (*lp=='"') { + lp++; + while (*lp!='"') { + if (*lp==0 || *lp==SCRIPT_EOL) break; + uint8_t iob=*lp; + if (iob=='\\') { + lp++; + if (*lp=='t') { + iob='\t'; + } else if (*lp=='n') { + iob='\n'; + } else if (*lp=='r') { + iob='\r'; + } else if (*lp=='\\') { + iob='\\'; + } else { + lp--; + } + if (sp) *sp++=iob; + } else { + if (sp) *sp++=iob; + } + lp++; + } + if (sp) *sp=0; + *vtype=STR_RES; + tind->bits.constant=1; + tind->bits.is_string=1; + return lp+1; + } + + if (*lp=='-') { + + nres=1; + lp++; + } + + const char *term="\n\r ])=+-/*%>index=VAR_NV; + glob_script_mem.var_not_found=1; + return lp; + } + + struct T_INDEX *vtp=glob_script_mem.type; + char dvnam[32]; + strcpy (dvnam,vname); + uint8_t olen=len; + last_findex=-1; + char *ja=strchr(dvnam,'['); + if (ja) { + *ja=0; + ja++; + olen=strlen(dvnam); + } + for (count=0; countindex=count; + if (vtp[count].bits.is_string==0) { + *vtype=NTYPE|index; + if (vtp[count].bits.is_filter) { + if (ja) { + lp+=olen+1; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + last_findex=fvar; + fvar=Get_MFVal(index,fvar); + len=1; + } else { + fvar=Get_MFilter(index); + } + } else { + fvar=glob_script_mem.fvars[index]; + } + if (nres) fvar=-fvar; + if (fp) *fp=fvar; + } else { + *vtype=STYPE|index; + if (sp) strlcpy(sp,glob_script_mem.glob_snp+(index*glob_script_mem.max_ssize),SCRIPT_MAXSSIZE); + } + return lp+len; + } + } + } + + if (jo) { + + const char* str_value; + uint8_t aindex; + String vn; + char *ja=strchr(vname,'['); + if (ja) { + + *ja=0; + ja++; + + float fvar; + GetNumericResult(ja,OPER_EQU,&fvar,0); + aindex=fvar; + if (aindex<1 || aindex>6) aindex=1; + aindex--; + } + if (jo->success()) { + char *subtype=strchr(vname,'#'); + char *subtype2; + if (subtype) { + *subtype=0; + subtype++; + subtype2=strchr(subtype,'#'); + if (subtype2) { + *subtype2=0; + *subtype2++; + } + } + vn=vname; + str_value = (*jo)[vn]; + if ((*jo)[vn].success()) { + if (subtype) { + JsonObject &jobj1=(*jo)[vn]; + if (jobj1.success()) { + vn=subtype; + jo=&jobj1; + str_value = (*jo)[vn]; + if ((*jo)[vn].success()) { + + if (subtype2) { + JsonObject &jobj2=(*jo)[vn]; + if ((*jo)[vn].success()) { + vn=subtype2; + jo=&jobj2; + str_value = (*jo)[vn]; + if ((*jo)[vn].success()) { + goto skip; + } else { + goto chknext; + } + } else { + goto chknext; + } + } + + goto skip; + } + } else { + goto chknext; + } + } + skip: + if (ja) { + + str_value = (*jo)[vn][aindex]; + } + if (str_value && *str_value) { + if ((*jo).is(vn)) { + if (!strncmp(str_value,"ON",2)) { + if (fp) *fp=1; + goto nexit; + } else if (!strncmp(str_value,"OFF",3)) { + if (fp) *fp=0; + goto nexit; + } else { + *vtype=STR_RES; + tind->bits.constant=1; + tind->bits.is_string=1; + if (sp) strlcpy(sp,str_value,SCRIPT_MAXSSIZE); + return lp+len; + } + + } else { + if (fp) { + if (!strncmp(vn.c_str(),"Epoch",5)) { + *fp=atoi(str_value)-(uint32_t)EPOCH_OFFSET; + } else { + *fp=CharToFloat((char*)str_value); + } + } + nexit: + *vtype=NUM_RES; + tind->bits.constant=1; + tind->bits.is_string=0; + return lp+len; + } + } + } + } + } + +chknext: + switch (vname[0]) { + case 'a': +#ifdef USE_ANGLE_FUNC + if (!strncmp(vname,"acos(",5)) { + lp+=5; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + fvar=acosf(fvar); + lp++; + len=0; + goto exit; + } +#endif + break; + + case 'b': + if (!strncmp(vname,"boot",4)) { + if (rules_flag.system_boot) { + rules_flag.system_boot=0; + fvar=1; + } + goto exit; + } +#ifdef USE_BUTTON_EVENT + if (!strncmp(vname,"bt[",3)) { + + GetNumericResult(vname+3,OPER_EQU,&fvar,0); + uint32_t index=fvar; + if (index<1 || index>MAX_KEYS) index=1; + fvar=script_button[index-1]; + script_button[index-1]|=0x80; + len++; + goto exit; + } +#endif + break; + case 'c': + if (!strncmp(vname,"chg[",4)) { + + struct T_INDEX ind; + uint8_t vtype; + isvar(vname+4,&vtype,&ind,0,0,0); + if (!ind.bits.constant) { + uint8_t index=glob_script_mem.type[ind.index].index; + if (glob_script_mem.fvars[index]!=glob_script_mem.s_fvars[index]) { + + glob_script_mem.s_fvars[index]=glob_script_mem.fvars[index]; + fvar=1; + len++; + goto exit; + } else { + fvar=0; + len++; + goto exit; + } + } + } + break; + case 'd': + if (!strncmp(vname,"day",3)) { + fvar=RtcTime.day_of_month; + goto exit; + } + break; + case 'e': + if (!strncmp(vname,"epoch",5)) { + fvar=UtcTime()-(uint32_t)EPOCH_OFFSET; + goto exit; + } + break; +#ifdef USE_SCRIPT_FATFS + case 'f': + if (!strncmp(vname,"fo(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + while (*lp==' ') lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + uint8_t mode=fvar; + fvar=-1; + for (uint8_t cnt=0;cnt=SFS_MAX) ind=SFS_MAX-1; + glob_script_mem.files[ind].close(); + glob_script_mem.file_flags[ind].is_open=0; + fvar=0; + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"ff(",3)) { + lp+=3; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + uint8_t ind=fvar; + if (ind>=SFS_MAX) ind=SFS_MAX-1; + glob_script_mem.files[ind].flush(); + fvar=0; + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fw(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=ForceStringVar(lp,str); + while (*lp==' ') lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + uint8_t ind=fvar; + if (ind>=SFS_MAX) ind=SFS_MAX-1; + if (glob_script_mem.file_flags[ind].is_open) { + fvar=glob_script_mem.files[ind].print(str); + } else { + fvar=0; + } + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fr(",3)) { + lp+=3; + struct T_INDEX ind; + uint8_t vtype; + uint8_t sindex=0; + lp=isvar(lp,&vtype,&ind,0,0,0); + if (vtype!=VAR_NV) { + + if ((vtype&STYPE)==0) { + + fvar=0; + goto exit; + } else { + + sindex=glob_script_mem.type[ind.index].index; + } + } else { + + fvar=0; + goto exit; + } + while (*lp==' ') lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + uint8_t find=fvar; + if (find>=SFS_MAX) find=SFS_MAX-1; + uint8_t index=0; + char str[glob_script_mem.max_ssize+1]; + char *cp=str; + if (glob_script_mem.file_flags[find].is_open) { + if (glob_script_mem.file_flags[find].is_dir) { + while (true) { + File entry=glob_script_mem.files[find].openNextFile(); + if (entry) { + if (!reject((char*)entry.name())) { + strcpy(str,entry.name()); + entry.close(); + break; + } + } else { + *cp=0; + break; + } + entry.close(); + } + index=strlen(str); + } else { + while (glob_script_mem.files[find].available()) { + uint8_t buf[1]; + glob_script_mem.files[find].read(buf,1); + if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { + break; + } else { + *cp++=buf[0]; + index++; + if (index>=glob_script_mem.max_ssize-1) break; + } + } + *cp=0; + } + } else { + strcpy(str,"file error"); + } + lp++; + strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + fvar=index; + len=0; + goto exit; + } + if (!strncmp(vname,"fd(",3)) { + lp+=3; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + SD.remove(str); + lp++; + len=0; + goto exit; + } +#ifdef USE_SCRIPT_FATFS_EXT + if (!strncmp(vname,"fe(",3)) { + lp+=3; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + + File ef=SD.open(str); + if (ef) { + uint16_t fsiz=ef.size(); + if (fsiz<2048) { + char *script=(char*)calloc(fsiz+16,1); + if (script) { + ef.read((uint8_t*)script,fsiz); + execute_script(script); + free(script); + fvar=1; + } + } + ef.close(); + } + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fmd(",4)) { + lp+=4; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + fvar=SD.mkdir(str); + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"frd(",4)) { + lp+=4; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + fvar=SD.rmdir(str); + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fx(",3)) { + lp+=3; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + if (SD.exists(str)) fvar=1; + else fvar=0; + lp++; + len=0; + goto exit; + } +#endif + if (!strncmp(vname,"fl1(",4) || !strncmp(vname,"fl2(",4) ) { + uint8_t lknum=*(lp+2)&3; + lp+=4; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + if (lknum<1 || lknum>2) lknum=1; + strlcpy(glob_script_mem.flink[lknum-1],str,14); + lp++; + fvar=0; + len=0; + goto exit; + } + if (!strncmp(vname,"fsm",3)) { + fvar=glob_script_mem.script_sd_found; + + goto exit; + } + break; + +#endif + case 'g': + if (!strncmp(vname,"gtmp",4)) { + fvar=global_temperature; + goto exit; + } + if (!strncmp(vname,"ghum",4)) { + fvar=global_humidity; + goto exit; + } + if (!strncmp(vname,"gprs",4)) { + fvar=global_pressure; + goto exit; + } + if (!strncmp(vname,"gtopic",6)) { + if (sp) strlcpy(sp,SettingsText(SET_MQTT_GRP_TOPIC),glob_script_mem.max_ssize); + goto strexit; + } + break; + case 'h': + if (!strncmp(vname,"hours",5)) { + fvar=RtcTime.hour; + goto exit; + } + if (!strncmp(vname,"heap",4)) { + fvar=ESP.getFreeHeap(); + goto exit; + } + if (!strncmp(vname,"hn(",3)) { + lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0); + if (fvar<0 || fvar>255) fvar=0; + lp++; + len=0; + if (sp) { + sprintf(sp,"%02x",(uint8_t)fvar); + } + goto strexit; + } + if (!strncmp(vname,"hx(",3)) { + lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0); + lp++; + len=0; + if (sp) { + sprintf(sp,"%08x",(uint32_t)fvar); + } + goto strexit; + } +#ifdef USE_LIGHT + + if (!strncmp(vname,"hsvrgb(",7)) { + lp=GetNumericResult(lp+7,OPER_EQU,&fvar,0); + if (fvar<0 || fvar>360) fvar=0; + SCRIPT_SKIP_SPACES + + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + if (fvar2<0 || fvar2>100) fvar2=0; + SCRIPT_SKIP_SPACES + + float fvar3; + lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); + if (fvar3<0 || fvar3>100) fvar3=0; + + fvar=HSVToRGB(fvar,fvar2,fvar3); + + lp++; + len=0; + goto exit; + } + +#endif + break; + case 'i': + if (!strncmp(vname,"int(",4)) { + lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0); + fvar=floor(fvar); + lp++; + len=0; + goto exit; + } + break; + case 'l': + if (!strncmp(vname,"loglvl",6)) { + fvar=glob_script_mem.script_loglevel; + tind->index=SCRIPT_LOGLEVEL; + exit_settable: + if (fp) *fp=fvar; + *vtype=NTYPE; + tind->bits.settable=1; + tind->bits.is_string=0; + return lp+len; + } + break; + case 'm': + if (!strncmp(vname,"med(",4)) { + float fvar1; + lp=GetNumericResult(lp+4,OPER_EQU,&fvar1,0); + SCRIPT_SKIP_SPACES + + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + fvar=DoMedian5(fvar1,fvar2); + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"micros",6)) { + fvar=micros(); + goto exit; + } + if (!strncmp(vname,"millis",6)) { + fvar=millis(); + goto exit; + } + if (!strncmp(vname,"mins",4)) { + fvar=RtcTime.minute; + goto exit; + } + if (!strncmp(vname,"month",5)) { + fvar=RtcTime.month; + goto exit; + } + if (!strncmp(vname,"mqttc",5)) { + if (rules_flag.mqtt_connected) { + rules_flag.mqtt_connected=0; + fvar=1; + } + goto exit; + } + if (!strncmp(vname,"mqttd",5)) { + if (rules_flag.mqtt_disconnected) { + rules_flag.mqtt_disconnected=0; + fvar=1; + } + goto exit; + } + if (!strncmp(vname,"mqtts",5)) { + fvar=!global_state.mqtt_down; + goto exit; + } + break; + case 'p': + if (!strncmp(vname,"pin[",4)) { + + GetNumericResult(vname+4,OPER_EQU,&fvar,0); + fvar=digitalRead((uint8_t)fvar); + + len++; + goto exit; + } + if (!strncmp(vname,"pn[",3)) { + GetNumericResult(vname+3,OPER_EQU,&fvar,0); + fvar=pin[(uint8_t)fvar]; + + len++; + goto exit; + } + if (!strncmp(vname,"pd[",3)) { + GetNumericResult(vname+3,OPER_EQU,&fvar,0); + uint8_t gpiopin=fvar; + for (uint8_t i=0;iMAX_COUNTERS) index=1; + fvar=RtcSettings.pulse_counter[index-1]; + len+=1; + goto exit; + } + break; + + case 'r': + if (!strncmp(vname,"ram",3)) { + fvar=glob_script_mem.script_mem_size+(glob_script_mem.script_size)+(PMEM_SIZE); + goto exit; + } + break; + case 's': + if (!strncmp(vname,"secs",4)) { + fvar=RtcTime.second; + goto exit; + } + if (!strncmp(vname,"sw[",3)) { + + GetNumericResult(vname+3,OPER_EQU,&fvar,0); + fvar=SwitchLastState((uint32_t)fvar); + + len++; + goto exit; + } + if (!strncmp(vname,"stack",5)) { + fvar=GetStack(); + goto exit; + } + if (!strncmp(vname,"slen",4)) { + fvar=strlen(glob_script_mem.script_ram); + goto exit; + } + if (!strncmp(vname,"sl(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + lp++; + len=0; + fvar=strlen(str); + goto exit; + } + if (!strncmp(vname,"sb(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + SCRIPT_SKIP_SPACES + float fvar1; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + SCRIPT_SKIP_SPACES + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + lp++; + len=0; + if (fvar1<0) { + fvar1=strlen(str)+fvar1; + } + memcpy(sp,&str[(uint8_t)fvar1],(uint8_t)fvar2); + sp[(uint8_t)fvar2] = '\0'; + goto strexit; + } + if (!strncmp(vname,"st(",3)) { + lp+=3; + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + while (*lp==' ') lp++; + char token[2]; + token[0]=*lp++; + token[1]=0; + while (*lp==' ') lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + + lp++; + len=0; + if (sp) { + + char *st=strtok(str,token); + if (!st) { + *sp=0; + } else { + for (uint8_t cnt=1; cnt<=fvar; cnt++) { + if (cnt==fvar) { + strcpy(sp,st); + break; + } + st=strtok(NULL,token); + if (!st) { + *sp=0; + break; + } + } + } + } + goto strexit; + } + if (!strncmp(vname,"s(",2)) { + lp+=2; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + char str[glob_script_mem.max_ssize+1]; + dtostrfd(fvar,glob_script_mem.script_dprec,str); + if (sp) strlcpy(sp,str,glob_script_mem.max_ssize); + lp++; + len=0; + goto strexit; + } +#if defined(USE_TIMERS) && defined(USE_SUNRISE) + if (!strncmp(vname,"sunrise",7)) { + fvar=SunMinutes(0); + goto exit; + } + if (!strncmp(vname,"sunset",6)) { + fvar=SunMinutes(1); + goto exit; + } +#endif + +#ifdef USE_SHUTTER + if (!strncmp(vname,"sht[",4)) { + GetNumericResult(vname+4,OPER_EQU,&fvar,0); + uint8_t index=fvar; + if (index<=shutters_present) { + fvar=Settings.shutter_position[index-1]; + } else { + fvar=-1; + } + len+=1; + goto exit; + } +#endif +#ifdef USE_ANGLE_FUNC + if (!strncmp(vname,"sin(",4)) { + lp+=4; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + fvar=sinf(fvar); + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"sqrt(",5)) { + lp+=5; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + fvar=sqrtf(fvar); + lp++; + len=0; + goto exit; + } +#endif +#ifdef USE_SML_SCRIPT_CMD + if (!strncmp(vname,"sml(",4)) { + lp+=4; + float fvar1; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); + SCRIPT_SKIP_SPACES + float fvar2; + lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); + SCRIPT_SKIP_SPACES + if (fvar2==0) { + float fvar3; + lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); + fvar=SML_SetBaud(fvar1,fvar3); + } else { + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + fvar=SML_Write(fvar1,str); + } + lp++; + fvar=0; + len=0; + goto exit; + } +#endif + break; + case 't': + if (!strncmp(vname,"time",4)) { + fvar=MinutesPastMidnight(); + goto exit; + } + if (!strncmp(vname,"tper",4)) { + fvar=Settings.tele_period; + tind->index=SCRIPT_TELEPERIOD; + goto exit_settable; + } + if (!strncmp(vname,"tinit",5)) { + if (rules_flag.time_init) { + rules_flag.time_init=0; + fvar=1; + } + goto exit; + } + if (!strncmp(vname,"tset",4)) { + if (rules_flag.time_set) { + rules_flag.time_set=0; + fvar=1; + } + goto exit; + } + if (!strncmp(vname,"tstamp",6)) { + if (sp) strlcpy(sp,GetDateAndTime(DT_LOCAL).c_str(),glob_script_mem.max_ssize); + goto strexit; + } + if (!strncmp(vname,"topic",5)) { + if (sp) strlcpy(sp,SettingsText(SET_MQTT_TOPIC),glob_script_mem.max_ssize); + goto strexit; + } +#ifdef USE_DISPLAY +#ifdef USE_TOUCH_BUTTONS + if (!strncmp(vname,"tbut[",5)) { + GetNumericResult(vname+5,OPER_EQU,&fvar,0); + uint8_t index=fvar; + if (index<1 || index>MAXBUTTONS) index=1; + index--; + if (buttons[index]) { + fvar=buttons[index]->vpower&0x80; + } else { + fvar=-1; + } + len+=1; + goto exit; + } + +#endif +#endif + break; + case 'u': + if (!strncmp(vname,"uptime",6)) { + fvar=MinutesUptime(); + goto exit; + } + if (!strncmp(vname,"upsecs",6)) { + fvar=uptime; + goto exit; + } + if (!strncmp(vname,"upd[",4)) { + + struct T_INDEX ind; + uint8_t vtype; + isvar(vname+4,&vtype,&ind,0,0,0); + if (!ind.bits.constant) { + if (!ind.bits.changed) { + fvar=0; + len++; + goto exit; + } else { + glob_script_mem.type[ind.index].bits.changed=0; + fvar=1; + len++; + goto exit; + } + } + goto notfound; + } + break; + + case 'w': + if (!strncmp(vname,"wday",4)) { + fvar=RtcTime.day_of_week; + goto exit; + } + if (!strncmp(vname,"wific",5)) { + if (rules_flag.wifi_connected) { + rules_flag.wifi_connected=0; + fvar=1; + } + goto exit; + } + if (!strncmp(vname,"wifid",5)) { + if (rules_flag.wifi_disconnected) { + rules_flag.wifi_disconnected=0; + fvar=1; + } + goto exit; + } + if (!strncmp(vname,"wifis",5)) { + fvar=!global_state.wifi_down; + goto exit; + } + break; + case 'y': + if (!strncmp(vname,"year",4)) { + fvar=RtcTime.year; + goto exit; + } + break; + default: + break; + } + +notfound: + if (fp) *fp=0; + *vtype=VAR_NV; + tind->index=VAR_NV; + glob_script_mem.var_not_found=1; + return lp; + +exit: + if (fp) *fp=fvar; + *vtype=NUM_RES; + tind->bits.constant=1; + tind->bits.is_string=0; + return lp+len; + +strexit: + *vtype=STYPE; + tind->bits.constant=1; + tind->bits.is_string=1; + return lp+len; +} + + + +char *getop(char *lp, uint8_t *operand) { + switch (*lp) { + case '=': + if (*(lp+1)=='=') { + *operand=OPER_EQUEQU; + return lp+2; + } else { + *operand=OPER_EQU; + return lp+1; + } + break; + case '+': + if (*(lp+1)=='=') { + *operand=OPER_PLSEQU; + return lp+2; + } else { + *operand=OPER_PLS; + return lp+1; + } + break; + case '-': + if (*(lp+1)=='=') { + *operand=OPER_MINEQU; + return lp+2; + } else { + *operand=OPER_MIN; + return lp+1; + } + break; + case '*': + if (*(lp+1)=='=') { + *operand=OPER_MULEQU; + return lp+2; + } else { + *operand=OPER_MUL; + return lp+1; + } + break; + case '/': + if (*(lp+1)=='=') { + *operand=OPER_DIVEQU; + return lp+2; + } else { + *operand=OPER_DIV; + return lp+1; + } + break; + case '!': + if (*(lp+1)=='=') { + *operand=OPER_NOTEQU; + return lp+2; + } + break; + case '>': + if (*(lp+1)=='=') { + *operand=OPER_GRTEQU; + return lp+2; + } else { + *operand=OPER_GRT; + return lp+1; + + } + break; + case '<': + if (*(lp+1)=='=') { + *operand=OPER_LOWEQU; + return lp+2; + } else { + *operand=OPER_LOW; + return lp+1; + } + break; + case '%': + if (*(lp+1)=='=') { + *operand=OPER_PERCEQU; + return lp+2; + } else { + *operand=OPER_PERC; + return lp+1; + } + break; + case '^': + if (*(lp+1)=='=') { + *operand=OPER_XOREQU; + return lp+2; + } else { + *operand=OPER_XOR; + return lp+1; + } + break; + case '&': + if (*(lp+1)=='=') { + *operand=OPER_ANDEQU; + return lp+2; + } else { + *operand=OPER_AND; + return lp+1; + } + break; + case '|': + if (*(lp+1)=='=') { + *operand=OPER_OREQU; + return lp+2; + } else { + *operand=OPER_OR; + return lp+1; + } + break; + } + *operand=0; + return lp; +} + + +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) + + +extern "C" { +#include + extern cont_t g_cont; +} +uint16_t GetStack(void) { + register uint32_t *sp asm("a1"); + return (4 * (sp - g_cont.stack)); +} + +#else +extern "C" { +#include + extern cont_t* g_pcont; +} +uint16_t GetStack(void) { + register uint32_t *sp asm("a1"); + return (4 * (sp - g_pcont->stack)); +} +#endif + +char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo) { + uint8_t operand=0; + uint8_t vtype; + char *slp; + struct T_INDEX ind; + char str[SCRIPT_MAXSSIZE],str1[SCRIPT_MAXSSIZE]; + while (1) { + lp=isvar(lp,&vtype,&ind,0,str1,jo); + if (vtype!=STR_RES && !(vtype&STYPE)) { + + glob_script_mem.glob_error=1; + return lp; + } + switch (lastop) { + case OPER_EQU: + strlcpy(str,str1,sizeof(str)); + break; + case OPER_PLS: + strncat(str,str1,sizeof(str)); + break; + } + slp=lp; + lp=getop(lp,&operand); + switch (operand) { + case OPER_EQUEQU: + case OPER_NOTEQU: + case OPER_LOW: + case OPER_LOWEQU: + case OPER_GRT: + case OPER_GRTEQU: + lp=slp; + strcpy(cp,str); + return lp; + break; + default: + break; + } + lastop=operand; + if (!operand) { + strcpy(cp,str); + return lp; + } + } +} + +char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo) { +uint8_t operand=0; +float fvar1,fvar; +char *slp; +uint8_t vtype; +struct T_INDEX ind; + while (1) { + + if (*lp=='(') { + lp++; + lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo); + lp++; + + } else { + lp=isvar(lp,&vtype,&ind,&fvar1,0,jo); + if (vtype!=NUM_RES && vtype&STYPE) { + + glob_script_mem.glob_error=1; + } + } + switch (lastop) { + case OPER_EQU: + fvar=fvar1; + break; + case OPER_PLS: + fvar+=fvar1; + break; + case OPER_MIN: + fvar-=fvar1; + break; + case OPER_MUL: + fvar*=fvar1; + break; + case OPER_DIV: + fvar/=fvar1; + break; + case OPER_PERC: + fvar=fmodf(fvar,fvar1); + break; + case OPER_XOR: + fvar=(uint32_t)fvar^(uint32_t)fvar1; + break; + case OPER_AND: + fvar=(uint32_t)fvar&(uint32_t)fvar1; + break; + case OPER_OR: + fvar=(uint32_t)fvar|(uint32_t)fvar1; + break; + default: + break; + + } + slp=lp; + lp=getop(lp,&operand); + switch (operand) { + case OPER_EQUEQU: + case OPER_NOTEQU: + case OPER_LOW: + case OPER_LOWEQU: + case OPER_GRT: + case OPER_GRTEQU: + lp=slp; + *fp=fvar; + return lp; + break; + default: + break; + } + lastop=operand; + if (!operand) { + *fp=fvar; + return lp; + } + } +} + + +char *ForceStringVar(char *lp,char *dstr) { + float fvar; + char *slp=lp; + glob_script_mem.glob_error=0; + lp=GetStringResult(lp,OPER_EQU,dstr,0); + if (glob_script_mem.glob_error) { + + lp=GetNumericResult(slp,OPER_EQU,&fvar,0); + dtostrfd(fvar,6,dstr); + glob_script_mem.glob_error=0; + } + return lp; +} + + +void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize) { + char *cp; + uint16_t count; + uint8_t vtype; + uint8_t dprec=glob_script_mem.script_dprec; + float fvar; + cp=srcbuf; + struct T_INDEX ind; + char string[SCRIPT_MAXSSIZE]; + dstsize-=2; + for (count=0;count=sizeof(str)) len=len>=sizeof(str); + strlcpy(str,cp,len); + toSLog(str); +} + +void toLogEOL(const char *s1,const char *str) { + if (!str) return; + uint8_t index=0; + char *cp=log_data; + strcpy(cp,s1); + cp+=strlen(s1); + while (*str) { + if (*str==SCRIPT_EOL) break; + *cp++=*str++; + } + *cp=0; + AddLog(LOG_LEVEL_INFO); +} + + +void toSLog(const char *str) { + if (!str) return; +#if SCRIPT_DEBUG>0 + while (*str) { + Serial.write(*str); + str++; + } +#endif +} + +char *Evaluate_expression(char *lp,uint8_t and_or, uint8_t *result,JsonObject *jo) { + float fvar,*dfvar,fvar1; + uint8_t numeric; + struct T_INDEX ind; + uint8_t vtype=0,lastop; + uint8_t res=0; + char *llp=lp; + char *slp; + + SCRIPT_SKIP_SPACES + if (*lp=='(') { + uint8_t res=0; + uint8_t xand_or=0; + lp++; + +loop: + SCRIPT_SKIP_SPACES + lp=Evaluate_expression(lp,xand_or,&res,jo); + if (*lp==')') { + lp++; + goto exit0; + } + + SCRIPT_SKIP_SPACES + if (!strncmp(lp,"or",2)) { + lp+=2; + xand_or=1; + goto loop; + } else if (!strncmp(lp,"and",3)) { + lp+=3; + xand_or=2; + goto loop; + } +exit0: + if (!and_or) { + *result=res; + } else if (and_or==1) { + *result|=res; + } else { + *result&=res; + } + goto exit10; + } + + llp=lp; + + dfvar=&fvar; + glob_script_mem.glob_error=0; + slp=lp; + numeric=1; + lp=GetNumericResult(lp,OPER_EQU,dfvar,0); + if (glob_script_mem.glob_error==1) { + + char cmpstr[SCRIPT_MAXSSIZE]; + lp=slp; + numeric=0; + + lp=isvar(lp,&vtype,&ind,0,cmpstr,0); + lp=getop(lp,&lastop); + + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,jo); + if (lastop==OPER_EQUEQU || lastop==OPER_NOTEQU) { + res=strcmp(cmpstr,str); + if (lastop==OPER_EQUEQU) res=!res; + goto exit; + } + + } else { + + + lp=getop(lp,&lastop); + lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo); + switch (lastop) { + case OPER_EQUEQU: + res=(*dfvar==fvar1); + break; + case OPER_NOTEQU: + res=(*dfvar!=fvar1); + break; + case OPER_LOW: + res=(*dfvarfvar1); + break; + case OPER_GRTEQU: + res=(*dfvar>=fvar1); + break; + default: + + break; + } + +exit: + if (!and_or) { + *result=res; + } else if (and_or==1) { + *result|=res; + } else { + *result&=res; + } + } + + +exit10: +#if SCRIPT_DEBUG>0 + char tbuff[128]; + sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d,and_or=%d line: ",(int32_t)*dfvar,(int32_t)fvar1,*result,and_or); + toLogEOL(tbuff,llp); +#endif + return lp; +} + + + +#define IF_NEST 8 + +int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { + + if (tasm_cmd_activ && tlen>0) return 0; + + uint8_t vtype=0,sindex,xflg,floop=0,globvindex,fromscriptcmd=0; + int8_t globaindex; + struct T_INDEX ind; + uint8_t operand,lastop,numeric=1,if_state[IF_NEST],if_exe[IF_NEST],if_result[IF_NEST],and_or,ifstck=0; + if_state[ifstck]=0; + if_result[ifstck]=0; + if_exe[ifstck]=1; + char cmpstr[SCRIPT_MAXSSIZE]; + uint8_t check=0; + if (tlen<0) { + tlen=abs(tlen); + check=1; + } + + float *dfvar,*cv_count,cv_max,cv_inc; + char *cv_ptr; + float fvar=0,fvar1,sysvar,swvar; + uint8_t section=0,sysv_type=0,swflg=0; + + if (!glob_script_mem.scriptptr) { + return -99; + } + + DynamicJsonBuffer jsonBuffer; + JsonObject &jobj=jsonBuffer.parseObject(js); + JsonObject *jo; + if (js) jo=&jobj; + else jo=0; + + char *lp=glob_script_mem.scriptptr; + + while (1) { + + + startline: + SCRIPT_SKIP_SPACES + + SCRIPT_SKIP_EOL + + if (*lp==';') goto next_line; + if (!*lp) break; + + if (section) { + + if (*lp=='>') { + return 0; + } + if (*lp=='#') { + return 0; + } + glob_script_mem.var_not_found=0; + + +#ifdef IFTHEN_DEBUG + char tbuff[128]; + sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]); + toLogEOL(tbuff,lp); +#endif + + + + + if (!strncmp(lp,"if",2)) { + lp+=2; + if (ifstck=2) { + lp+=5; + if (ifstck>0) { + if_state[ifstck]=0; + ifstck--; + } + goto next_line; + } else if (!strncmp(lp,"or",2) && if_state[ifstck]==1) { + lp+=2; + and_or=1; + } else if (!strncmp(lp,"and",3) && if_state[ifstck]==1) { + lp+=3; + and_or=2; + } + + if (*lp=='{' && if_state[ifstck]==1) { + lp+=1; + if_state[ifstck]=2; + if (if_exe[ifstck-1]) if_exe[ifstck]=if_result[ifstck]; + } else if (*lp=='{' && if_state[ifstck]==3) { + lp+=1; + + } else if (*lp=='}' && if_state[ifstck]>=2) { + lp++; + char *slp=lp; + uint8_t iselse=0; + for (uint8_t count=0; count<8;count++) { + if (*lp=='}') { + + break; + } + if (!strncmp(lp,"else",4)) { + + if_state[ifstck]=3; + if (if_exe[ifstck-1]) if_exe[ifstck]=!if_result[ifstck]; + lp+=4; + iselse=1; + SCRIPT_SKIP_SPACES + if (*lp=='{') lp++; + break; + } + lp++; + } + if (!iselse) { + lp=slp; + + if (ifstck>0) { + if_state[ifstck]=0; + ifstck--; + } + goto next_line; + } + } + + if (!strncmp(lp,"for",3)) { + + + lp+=3; + SCRIPT_SKIP_SPACES + lp=isvar(lp,&vtype,&ind,0,0,0); + if ((vtype!=VAR_NV) && (vtype&STYPE)==0) { + + uint8_t index=glob_script_mem.type[ind.index].index; + cv_count=&glob_script_mem.fvars[index]; + SCRIPT_SKIP_SPACES + lp=GetNumericResult(lp,OPER_EQU,cv_count,0); + SCRIPT_SKIP_SPACES + lp=GetNumericResult(lp,OPER_EQU,&cv_max,0); + SCRIPT_SKIP_SPACES + lp=GetNumericResult(lp,OPER_EQU,&cv_inc,0); + + cv_ptr=lp; + floop=1; + } else { + + toLogEOL("for error",lp); + } + } else if (!strncmp(lp,"next",4) && floop>0) { + + *cv_count+=cv_inc; + if (*cv_count<=cv_max) { + lp=cv_ptr; + } else { + lp+=4; + floop=0; + } + } + + if (!strncmp(lp,"switch",6)) { + lp+=6; + SCRIPT_SKIP_SPACES + char *slp=lp; + lp=GetNumericResult(lp,OPER_EQU,&swvar,0); + if (glob_script_mem.glob_error==1) { + + lp=slp; + + lp=isvar(lp,&vtype,&ind,0,cmpstr,0); + swflg=0x81; + } else { + swflg=1; + } + } else if (!strncmp(lp,"case",4) && swflg>0) { + lp+=4; + SCRIPT_SKIP_SPACES + float cvar; + if (!(swflg&0x80)) { + lp=GetNumericResult(lp,OPER_EQU,&cvar,0); + if (swvar!=cvar) { + swflg=2; + } else { + swflg=1; + } + } else { + char str[SCRIPT_MAXSSIZE]; + lp=GetStringResult(lp,OPER_EQU,str,0); + if (!strcmp(cmpstr,str)) { + swflg=0x81; + } else { + swflg=0x82; + } + } + } else if (!strncmp(lp,"ends",4) && swflg>0) { + lp+=4; + swflg=0; + } + if ((swflg&3)==2) goto next_line; + + SCRIPT_SKIP_SPACES + + if (*lp==SCRIPT_EOL) { + goto next_line; + } + + + if (!if_exe[ifstck] && if_state[ifstck]!=1) goto next_line; + +#ifdef IFTHEN_DEBUG + sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d execute line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]); + toLogEOL(tbuff,lp); +#endif + + if (!strncmp(lp,"break",5)) { + if (floop) { + + floop=0; + } else { + section=0; + } + break; + } else if (!strncmp(lp,"dp",2) && isdigit(*(lp+2))) { + lp+=2; + + glob_script_mem.script_dprec=atoi(lp); + goto next_line; + } else if (!strncmp(lp,"delay(",6)) { + lp+=5; + + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + delay(fvar); + goto next_line; + } else if (!strncmp(lp,"spinm(",6)) { + lp+=6; + + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + int8_t pinnr=fvar; + SCRIPT_SKIP_SPACES + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + int8_t mode=fvar; + pinMode(pinnr,mode&3); + goto next_line; + } else if (!strncmp(lp,"spin(",5)) { + lp+=5; + + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + int8_t pinnr=fvar; + SCRIPT_SKIP_SPACES + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + int8_t mode=fvar; + digitalWrite(pinnr,mode&1); + goto next_line; + } else if (!strncmp(lp,"svars(",5)) { + lp+=5; + + Scripter_save_pvars(); + goto next_line; + } +#ifdef USE_LIGHT +#ifdef USE_WS2812 + else if (!strncmp(lp,"ws2812(",7)) { + lp+=7; + lp=isvar(lp,&vtype,&ind,0,0,0); + if (vtype!=VAR_NV) { + + uint8_t index=glob_script_mem.type[ind.index].index; + if ((vtype&STYPE)==0) { + + if (glob_script_mem.type[index].bits.is_filter) { + uint8_t len=0; + float *fa=Get_MFAddr(index,&len); + + if (fa && len) ws2812_set_array(fa,len); + } + } + } + goto next_line; + } +#endif +#endif + + else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) { + + uint8_t sflag=0,pflg=0,svmqtt,swll; + if (*lp=='p') { + pflg=1; + lp+=5; + } + else { + if (*lp=='-') sflag=1; + if (*lp=='+') sflag=2; + lp+=2; + } + char *slp=lp; + SCRIPT_SKIP_SPACES + #define SCRIPT_CMDMEM 512 + char *cmdmem=(char*)malloc(SCRIPT_CMDMEM); + if (cmdmem) { + char *cmd=cmdmem; + uint16_t count; + for (count=0; count=0) { + Set_MFVal(glob_script_mem.type[globvindex].index,globaindex,*dfvar); + } else { + Set_MFilter(glob_script_mem.type[globvindex].index,*dfvar); + } + } + + if (sysv_type) { + switch (sysv_type) { + case SCRIPT_LOGLEVEL: + glob_script_mem.script_loglevel=*dfvar; + break; + case SCRIPT_TELEPERIOD: + if (*dfvar<10) *dfvar=10; + if (*dfvar>300) *dfvar=300; + Settings.tele_period=*dfvar; + break; + } + sysv_type=0; + } + } else { + + numeric=0; + sindex=index; + + char str[SCRIPT_MAXSSIZE]; + lp=getop(lp,&lastop); + char *slp=lp; + glob_script_mem.glob_error=0; + lp=GetStringResult(lp,OPER_EQU,str,jo); + if (!js && glob_script_mem.glob_error) { + + lp=GetNumericResult(slp,OPER_EQU,&fvar,0); + dtostrfd(fvar,6,str); + glob_script_mem.glob_error=0; + } + + if (!glob_script_mem.var_not_found) { + + glob_script_mem.type[globvindex].bits.changed=1; + if (lastop==OPER_EQU) { + strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + } else if (lastop==OPER_PLSEQU) { + strncat(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); + } + } + } + + } + SCRIPT_SKIP_SPACES + if (*lp=='{' && if_state[ifstck]==3) { + lp+=1; + + } + goto next_line; + } + } else { + + if (*lp=='>' && tlen==1) { + + lp++; + section=1; + fromscriptcmd=1; + goto startline; + } + if (!strncmp(lp,type,tlen)) { + + section=1; + glob_script_mem.section_ptr=lp; + if (check) { + return 99; + } + + char *ctype=(char*)type; + if (*ctype=='#') { + + ctype+=tlen; + if (*ctype=='(' && *(lp+tlen)=='(') { + float fparam; + numeric=1; + glob_script_mem.glob_error=0; + GetNumericResult((char*)ctype,OPER_EQU,&fparam,0); + if (glob_script_mem.glob_error==1) { + + numeric=0; + + GetStringResult((char*)ctype+1,OPER_EQU,cmpstr,0); + } + lp+=tlen; + if (*lp=='(') { + + lp++; + lp=isvar(lp,&vtype,&ind,0,0,0); + if (vtype!=VAR_NV) { + + uint8_t index=glob_script_mem.type[ind.index].index; + if ((vtype&STYPE)==0) { + + dfvar=&glob_script_mem.fvars[index]; + if (numeric) { + *dfvar=fparam; + } else { + + *dfvar=CharToFloat(cmpstr); + } + } else { + + sindex=index; + if (!numeric) { + strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),cmpstr,glob_script_mem.max_ssize); + } else { + + dtostrfd(fparam,6,glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize)); + } + } + } + } + } else { + lp+=tlen; + if (*ctype=='(' || (*lp!=SCRIPT_EOL && *lp!='?')) { + + section=0; + } + } + } + } + } + + next_line: + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) { + if (section) { + return 0; + } else { + return -1; + } + } + lp++; + } + } + return -1; +} + +uint8_t script_xsns_index = 0; + + +void ScripterEvery100ms(void) { + + if (Settings.rule_enabled && (uptime > 4)) { + mqtt_data[0] = '\0'; + uint16_t script_tele_period_save = tele_period; + tele_period = 2; + XsnsNextCall(FUNC_JSON_APPEND, script_xsns_index); + tele_period = script_tele_period_save; + if (strlen(mqtt_data)) { + mqtt_data[0] = '{'; + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); + Run_Scripter(">T",2, mqtt_data); + } + } + if (fast_script==99) Run_Scripter(">F",2,0); +} + + + + +void Scripter_save_pvars(void) { + int16_t mlen=0; + float *fp=(float*)glob_script_mem.script_pram; + mlen+=sizeof(float); + struct T_INDEX *vtp=glob_script_mem.type; + for (uint8_t count=0; countPMEM_SIZE) { + vtp[count].bits.is_permanent=0; + return; + } + *fp++=glob_script_mem.fvars[index]; + } + } + char *cp=(char*)fp; + for (uint8_t count=0; countPMEM_SIZE) { + vtp[count].bits.is_permanent=0; + return; + } + strcpy(cp,sp); + cp+=slen+1; + } + } +} + + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_SCRIPT "s10" + +const char S_CONFIGURE_SCRIPT[] PROGMEM = D_CONFIGURE_SCRIPT; + +const char HTTP_BTN_MENU_RULES[] PROGMEM = + "

"; + + +const char HTTP_FORM_SCRIPT[] PROGMEM = + "
 " D_SCRIPT " " + "
"; + +const char HTTP_FORM_SCRIPT1[] PROGMEM = + "
" + "" D_SCRIPT_ENABLE "
" + "
" + ""; + +const char HTTP_SCRIPT_FORM_END[] PROGMEM = + "
" + "" + "
"; + +#ifdef USE_SCRIPT_FATFS +const char HTTP_FORM_SCRIPT1c[] PROGMEM = + ""; +#ifdef SDCARD_DIR +const char HTTP_FORM_SCRIPT1d[] PROGMEM = + ""; +#else +const char HTTP_FORM_SCRIPT1d[] PROGMEM = + ""; +#endif + +#ifdef SDCARD_DIR +const char S_SCRIPT_FILE_UPLOAD[] PROGMEM = D_SDCARD_DIR; +#else +const char S_SCRIPT_FILE_UPLOAD[] PROGMEM = D_SDCARD_UPLOAD; +#endif + +const char HTTP_FORM_FILE_UPLOAD[] PROGMEM = +"
" +"
 %s" " "; +const char HTTP_FORM_FILE_UPG[] PROGMEM = +"
" +"

" +"
"; + +const char HTTP_FORM_FILE_UPGb[] PROGMEM = +"
" +"
" +""; + +const char HTTP_FORM_SDC_DIRa[] PROGMEM = +"
"; +const char HTTP_FORM_SDC_DIRb[] PROGMEM = + "
%s    %d
"; +const char HTTP_FORM_SDC_DIRd[] PROGMEM = +"
%s
"; +const char HTTP_FORM_SDC_DIRc[] PROGMEM = +"
"; +const char HTTP_FORM_SDC_HREF[] PROGMEM = +"http://%s/upl?download=%s/%s"; +#endif + + + +#ifdef USE_SCRIPT_FATFS + +#if USE_LONG_FILE_NAMES>0 +#undef REJCMPL +#define REJCMPL 6 +#else +#undef REJCMPL +#define REJCMPL 8 +#endif + +uint8_t reject(char *name) { + + if (*name=='_') return 1; + if (*name=='.') return 1; + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 + if (!strncasecmp(name,"SPOTLI~1",REJCMPL)) return 1; + if (!strncasecmp(name,"TRASHE~1",REJCMPL)) return 1; + if (!strncasecmp(name,"FSEVEN~1",REJCMPL)) return 1; + if (!strncasecmp(name,"SYSTEM~1",REJCMPL)) return 1; +#else + if (!strcasecmp(name,"SPOTLI~1")) return 1; + if (!strcasecmp(name,"TRASHE~1")) return 1; + if (!strcasecmp(name,"FSEVEN~1")) return 1; + if (!strcasecmp(name,"SYSTEM~1")) return 1; +#endif + return 0; +} + +void ListDir(char *path, uint8_t depth) { + char name[32]; + char npath[128]; + char format[12]; + sprintf(format,"%%-%ds",24-depth); + + File dir=SD.open(path); + if (dir) { + dir.rewindDirectory(); + if (strlen(path)>1) { + snprintf_P(npath,sizeof(npath),PSTR("http://%s/upl?download=%s"),WiFi.localIP().toString().c_str(),path); + for (uint8_t cnt=strlen(npath)-1;cnt>0;cnt--) { + if (npath[cnt]=='/') { + if (npath[cnt-1]=='=') npath[cnt+1]=0; + else npath[cnt]=0; + break; + } + } + WSContentSend_P(HTTP_FORM_SDC_DIRd,npath,path,".."); + } + while (true) { + File entry=dir.openNextFile(); + if (!entry) { + break; + } + char *pp=path; + if (!*(pp+1)) pp++; + char *cp=name; + + if (reject((char*)entry.name())) goto fclose; + + for (uint8_t cnt=0;cnt1) { + strcat(path,"/"); + } + strcat(path,entry.name()); + ListDir(path,depth+4); + path[plen]=0; + } else { + snprintf_P(npath,sizeof(npath),HTTP_FORM_SDC_HREF,WiFi.localIP().toString().c_str(),pp,entry.name()); + WSContentSend_P(HTTP_FORM_SDC_DIRb,npath,entry.name(),name,entry.size()); + } + fclose: + entry.close(); + } + dir.close(); + } +} + +char path[48]; + +void Script_FileUploadConfiguration(void) +{ + uint8_t depth=0; + strcpy(path,"/"); + + if (!HttpCheckPriviledgedAccess()) { return; } + + if (WebServer->hasArg("download")) { + String stmp = WebServer->arg("download"); + char *cp=(char*)stmp.c_str(); + if (DownloadFile(cp)) { + + strcpy(path,cp); + } + } + + WSContentStart_P(S_SCRIPT_FILE_UPLOAD); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_FILE_UPLOAD,D_SDCARD_DIR); + WSContentSend_P(HTTP_FORM_FILE_UPG, D_SCRIPT_UPLOAD); +#ifdef SDCARD_DIR + WSContentSend_P(HTTP_FORM_SDC_DIRa); + if (glob_script_mem.script_sd_found) { + ListDir(path,depth); + } + WSContentSend_P(HTTP_FORM_SDC_DIRc); +#endif + WSContentSend_P(HTTP_FORM_FILE_UPGb); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); + Web.upload_error = 0; +} + +File upload_file; + +void ScriptFileUploadSuccess(void) { + WSContentStart_P(S_INFORMATION); + WSContentSendStyle(); + WSContentSend_P(PSTR("
" D_UPLOAD " " D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS)); + WSContentSend_P(PSTR("

")); + WSContentSend_P(PSTR("

"),"/upl",D_UPL_DONE); + + WSContentStop(); +} + + + +void script_upload(void) { + + + + HTTPUpload& upload = WebServer->upload(); + if (upload.status == UPLOAD_FILE_START) { + char npath[48]; + sprintf(npath,"%s/%s",path,upload.filename.c_str()); + SD.remove(npath); + upload_file=SD.open(npath,FILE_WRITE); + if (!upload_file) Web.upload_error=1; + } else if(upload.status == UPLOAD_FILE_WRITE) { + if (upload_file) upload_file.write(upload.buf,upload.currentSize); + } else if(upload.status == UPLOAD_FILE_END) { + if (upload_file) upload_file.close(); + if (Web.upload_error) { + AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload error")); + } + } else { + Web.upload_error=1; + WebServer->send(500, "text/plain", "500: couldn't create file"); + } +} + +uint8_t DownloadFile(char *file) { + File download_file; + WiFiClient download_Client; + + if (!SD.exists(file)) { + AddLog_P(LOG_LEVEL_INFO,PSTR("file not found")); + return 0; + } + + download_file=SD.open(file,FILE_READ); + if (!download_file) { + AddLog_P(LOG_LEVEL_INFO,PSTR("could not open file")); + return 0; + } + + if (download_file.isDirectory()) { + download_file.close(); + return 1; + } + + uint32_t flen=download_file.size(); + + download_Client = WebServer->client(); + WebServer->setContentLength(flen); + + char attachment[100]; + char *cp; + for (uint8_t cnt=strlen(file); cnt>=0; cnt--) { + if (file[cnt]=='/') { + cp=&file[cnt+1]; + break; + } + } + snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s"),cp); + WebServer->sendHeader(F("Content-Disposition"), attachment); + WSSend(200, CT_STREAM, ""); + + uint8_t buff[512]; + uint16_t bread; + + + uint8_t cnt=0; + while (download_file.available()) { + bread=download_file.read(buff,sizeof(buff)); + uint16_t bw=download_Client.write((const char*)buff,bread); + if (!bw) break; + cnt++; + if (cnt>7) { + cnt=0; + if (glob_script_mem.script_loglevel&0x80) { + + loop(); + } + } + } + download_file.close(); + download_Client.stop(); + return 0; +} + +#endif + + +void HandleScriptTextareaConfiguration(void) { + if (!HttpCheckPriviledgedAccess()) { return; } + + if (WebServer->hasArg("save")) { + ScriptSaveSettings(); + HandleConfiguration(); + return; + } +} + +void HandleScriptConfiguration(void) { + + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_SCRIPT); + +#ifdef USE_SCRIPT_FATFS + if (WebServer->hasArg("d1")) { + DownloadFile(glob_script_mem.flink[0]); + } + if (WebServer->hasArg("d2")) { + DownloadFile(glob_script_mem.flink[1]); + } + if (WebServer->hasArg("upl")) { + Script_FileUploadConfiguration(); + } +#endif + + WSContentStart_P(S_CONFIGURE_SCRIPT); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_SCRIPT); + + +#ifdef xSCRIPT_STRIP_COMMENTS + uint16_t ssize=glob_script_mem.script_size; + if (bitRead(Settings.rule_enabled, 1)) ssize*=2; + WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",ssize); +#else + WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",glob_script_mem.script_size); +#endif + + + if (glob_script_mem.script_ram[0]) { + _WSContentSend(glob_script_mem.script_ram); + } + WSContentSend_P(HTTP_FORM_SCRIPT1b); + +#ifdef USE_SCRIPT_FATFS + if (glob_script_mem.script_sd_found) { + WSContentSend_P(HTTP_FORM_SCRIPT1d); + if (glob_script_mem.flink[0][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,1,glob_script_mem.flink[0]); + if (glob_script_mem.flink[1][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,2,glob_script_mem.flink[1]); + } +#endif + + WSContentSend_P(HTTP_SCRIPT_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); + } + + +void ScriptSaveSettings(void) { + + if (WebServer->hasArg("c1")) { + bitWrite(Settings.rule_enabled,0,1); + } else { + bitWrite(Settings.rule_enabled,0,0); + } + + + String str = WebServer->arg("t1"); + + if (*str.c_str()) { + + str.replace("\r\n","\n"); + str.replace("\r","\n"); + +#ifdef xSCRIPT_STRIP_COMMENTS + if (bitRead(Settings.rule_enabled, 1)) { + char *sp=(char*)str.c_str(); + char *sp1=sp; + char *dp=sp; + uint8_t flg=0; + while (*sp) { + while (*sp==' ') sp++; + sp1=sp; + sp=strchr(sp,'\n'); + if (!sp) { + flg=1; + } else { + *sp=0; + } + if (*sp1!=';') { + uint8_t slen=strlen(sp1); + if (slen) { + strcpy(dp,sp1); + dp+=slen; + *dp++='\n'; + } + } + if (flg) { + *dp=0; + break; + } + sp++; + } + } +#endif + + strlcpy(glob_script_mem.script_ram,str.c_str(), glob_script_mem.script_size); + +#ifdef USE_24C256 +#ifndef USE_SCRIPT_FATFS + if (glob_script_mem.flags&1) { + EEP_WRITE(0,EEP_SCRIPT_SIZE,glob_script_mem.script_ram); + } +#endif +#endif + +#ifdef USE_SCRIPT_FATFS + if (glob_script_mem.flags&1) { + SD.remove(FAT_SCRIPT_NAME); + File file=SD.open(FAT_SCRIPT_NAME,FILE_WRITE); + file.write(glob_script_mem.script_ram,FAT_SCRIPT_SIZE); + file.close(); + } +#endif + + } + + if (glob_script_mem.script_mem) { + Scripter_save_pvars(); + free(glob_script_mem.script_mem); + glob_script_mem.script_mem=0; + glob_script_mem.script_mem_size=0; + } + + if (bitRead(Settings.rule_enabled, 0)) { + int16_t res=Init_Scripter(); + if (res) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("script init error: %d"), res); + return; + } + Run_Scripter(">B",2,0); + fast_script=Run_Scripter(">F",-2,0); + } +} + +#endif + + +#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) + + +#define HUE_DEV_MVNUM 5 +#define HUE_DEV_NSIZE 16 +struct HUE_SCRIPT { + char name[HUE_DEV_NSIZE]; + uint8_t type; + uint8_t index[HUE_DEV_MVNUM]; + uint8_t vindex[HUE_DEV_MVNUM]; +} hue_script[32]; + + +const char SCRIPT_HUE_LIGHTS_STATUS_JSON1[] PROGMEM = + "{\"state\":" + "{\"on\":{state}," + "{light_status}" + "\"alert\":\"none\"," + "\"effect\":\"none\"," + "\"reachable\":true}" + ",\"type\":\"{type}\"," + "\"name\":\"{j1\"," + "\"modelid\":\"{m1}\"," + "\"uniqueid\":\"{j2\"," + "\"swversion\":\"5.50.1.19085\"}"; +# 3624 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" +const char SCRIPT_HUE_LIGHTS_STATUS_JSON2[] PROGMEM = +"{\"state\":{" +"\"presence\":{state}," +"\"lastupdated\":\"2017-10-01T12:37:30\"" +"}," +"\"swupdate\":{" +"\"state\":\"noupdates\"," +"\"lastinstall\": null" +"}," +"\"config\":{" +"\"on\":true," +"\"battery\":100," +"\"reachable\":true," +"\"alert\":\"none\"," +"\"ledindication\":false," +"\"usertest\":false," +"\"sensitivity\":2," +"\"sensitivitymax\":2," +"\"pending\":[]" +"}," +"\"name\":\"{j1\"," +"\"type\":\"ZLLPresence\"," +"\"modelid\":\"SML001\"," +"\"manufacturername\":\"Philips\"," +"\"swversion\":\"6.1.0.18912\"," +"\"uniqueid\":\"{j2\"" +"}"; +# 3705 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" +void Script_HueStatus(String *response, uint16_t hue_devs) { + + if (hue_script[hue_devs].type=='p') { + *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON2); + response->replace("{j1",hue_script[hue_devs].name); + response->replace("{j2", GetHueDeviceId(hue_devs)); + uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; + response->replace("{state}", (pwr ? "true" : "false")); + return; + } + + *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON1); + uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; + response->replace("{state}", (pwr ? "true" : "false")); + String light_status = ""; + if (hue_script[hue_devs].index[1]>0) { + + light_status += "\"bri\":"; + uint32_t bri=glob_script_mem.fvars[hue_script[hue_devs].index[1]-1]; + if (bri > 254) bri = 254; + if (bri < 1) bri = 1; + light_status += String(bri); + light_status += ","; + } + if (hue_script[hue_devs].index[2]>0) { + + uint32_t hue=glob_script_mem.fvars[hue_script[hue_devs].index[2]-1]; + + light_status += "\"hue\":"; + light_status += String(hue); + light_status += ","; + } + if (hue_script[hue_devs].index[3]>0) { + + uint32_t sat=glob_script_mem.fvars[hue_script[hue_devs].index[3]-1] ; + if (sat > 254) sat = 254; + if (sat < 1) sat = 1; + light_status += "\"sat\":"; + light_status += String(sat); + light_status += ","; + } + if (hue_script[hue_devs].index[4]>0) { + + uint32_t ct=glob_script_mem.fvars[hue_script[hue_devs].index[4]-1]; + light_status += "\"ct\":"; + light_status += String(ct); + light_status += ","; + } + + float temp; + switch (hue_script[hue_devs].type) { + case 'C': + response->replace("{type}","Color Ligh"); + response->replace("{m1","LST001"); + break; + case 'D': + response->replace("{type}","Dimmable Light"); + response->replace("{m1","LWB004"); + break; + case 'T': + response->replace("{type}","Color Temperature Light"); + response->replace("{m1","LTW011"); + break; + case 'E': + response->replace("{type}","Extended color light"); + response->replace("{m1","LCT007"); + break; + case 'S': + response->replace("{type}","On/Off light"); + response->replace("{m1","LCT007"); + break; + default: + response->replace("{type}","color light"); + response->replace("{m1","LST001"); + break; + } + + response->replace("{light_status}", light_status); + response->replace("{j1",hue_script[hue_devs].name); + response->replace("{j2", GetHueDeviceId(hue_devs)); + +} + +void Script_Check_Hue(String *response) { + if (!bitRead(Settings.rule_enabled, 0)) return; + + uint8_t hue_script_found=Run_Scripter(">H",-2,0); + if (hue_script_found!=99) return; + + char line[128]; + char tmp[128]; + uint8_t hue_devs=0; + uint8_t vindex=0; + char *cp; + char *lp=glob_script_mem.section_ptr+2; + while (lp) { + SCRIPT_SKIP_SPACES + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + + memcpy(line,lp,sizeof(line)); + line[sizeof(line)-1]=0; + cp=line; + for (uint32_t i=0; i0) *response+=",\""; + } + *response+=String(EncodeLightId(hue_devs+devices_present+1))+"\":"; + Script_HueStatus(response,hue_devs); + } + + hue_devs++; + } + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } +#if 0 + if (response) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Hue: %d"), hue_devs); + toLog(">>>>"); + toLog(response->c_str()); + toLog(response->c_str()+LOGSZ); + } +#endif +} + +const char sHUE_LIGHT_RESPONSE_JSON[] PROGMEM = + "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; + +const char sHUE_SENSOR_RESPONSE_JSON[] PROGMEM = + "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; + +const char sHUE_ERROR_JSON[] PROGMEM = + "[{\"error\":{\"type\":901,\"address\":\"/\",\"description\":\"Internal Error\"}}]"; + + + +void Script_Handle_Hue(String *path) { + String response; + int code = 200; + uint16_t tmp = 0; + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = 254; + uint16_t ct = 0; + bool resp = false; + + uint8_t device = DecodeLightId(atoi(path->c_str())); + uint8_t index = device-devices_present-1; + + if (WebServer->args()) { + response = "["; + + StaticJsonBuffer<400> jsonBuffer; + JsonObject &hue_json = jsonBuffer.parseObject(WebServer->arg((WebServer->args())-1)); + if (hue_json.containsKey("on")) { + + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "on"); + + bool on = hue_json["on"]; + switch(on) + { + case false : glob_script_mem.fvars[hue_script[index].index[0]-1]=0; + response.replace("{re", "false"); + break; + case true : glob_script_mem.fvars[hue_script[index].index[0]-1]=1; + response.replace("{re", "true"); + break; + } + glob_script_mem.type[hue_script[index].vindex[0]].bits.changed=1; + resp = true; + } + if (hue_json.containsKey("bri")) { + tmp = hue_json["bri"]; + bri=tmp; + if (254 <= bri) { bri = 255; } + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "bri"); + response.replace("{re", String(tmp)); + glob_script_mem.fvars[hue_script[index].index[1]-1]=bri; + glob_script_mem.type[hue_script[index].vindex[1]].bits.changed=1; + resp = true; + } + if (hue_json.containsKey("xy")) { + float x, y; + x = hue_json["xy"][0]; + y = hue_json["xy"][1]; + const String &x_str = hue_json["xy"][0]; + const String &y_str = hue_json["xy"][1]; + uint8_t rr,gg,bb; + LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); + LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(device)); + response.replace("{cm", "xy"); + response.replace("{re", "[" + x_str + "," + y_str + "]"); + glob_script_mem.fvars[hue_script[index].index[2]-1]=hue; + glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1; + glob_script_mem.fvars[hue_script[index].index[3]-1]=sat; + glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1; + resp = true; + } + + if (hue_json.containsKey("hue")) { + tmp = hue_json["hue"]; + + + hue=tmp; + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "hue"); + response.replace("{re", String(tmp)); + glob_script_mem.fvars[hue_script[index].index[2]-1]=hue; + glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1; + resp = true; + } + if (hue_json.containsKey("sat")) { + tmp = hue_json["sat"]; + sat=tmp; + if (254 <= sat) { sat = 255; } + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "sat"); + response.replace("{re", String(tmp)); + glob_script_mem.fvars[hue_script[index].index[3]-1]=sat; + glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1; + resp = true; + } + if (hue_json.containsKey("ct")) { + ct = hue_json["ct"]; + if (resp) { response += ","; } + response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "ct"); + response.replace("{re", String(ct)); + glob_script_mem.fvars[hue_script[index].index[4]-1]=ct; + glob_script_mem.type[hue_script[index].vindex[4]].bits.changed=1; + resp = true; + } + response += "]"; + + } else { + response = FPSTR(sHUE_ERROR_JSON); + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); + WSSend(code, CT_JSON, response); + if (resp) { + Run_Scripter(">E",2,0); + } +} +#endif + + +#ifdef USE_SCRIPT_SUB_COMMAND +bool Script_SubCmd(void) { + if (!bitRead(Settings.rule_enabled, 0)) return false; + + if (tasm_cmd_activ) return false; + + char command[CMDSZ]; + strlcpy(command,XdrvMailbox.topic,CMDSZ); + uint32_t pl=XdrvMailbox.payload; + char pld[64]; + strlcpy(pld,XdrvMailbox.data,sizeof(pld)); + + char cmdbuff[128]; + char *cp=cmdbuff; + *cp++='#'; + strcpy(cp,XdrvMailbox.topic); + uint8_t tlen=strlen(XdrvMailbox.topic); + cp+=tlen; + if (XdrvMailbox.index > 0) { + *cp++=XdrvMailbox.index|0x30; + tlen++; + } + if ((XdrvMailbox.payload>0) || (XdrvMailbox.data_len>0)) { + *cp++='('; + strncpy(cp,XdrvMailbox.data,XdrvMailbox.data_len); + cp+=XdrvMailbox.data_len; + *cp++=')'; + *cp=0; + } + + uint32_t res=Run_Scripter(cmdbuff,tlen+1,0); + + if (res) return false; + else { + if (pl>=0) { + Response_P(S_JSON_COMMAND_NVALUE, command, pl); + } else { + Response_P(S_JSON_COMMAND_SVALUE, command, pld); + } + } + return true; +} +#endif + +void execute_script(char *script) { + char *svd_sp=glob_script_mem.scriptptr; + strcat(script,"\n#"); + glob_script_mem.scriptptr=script; + Run_Scripter(">",1,0); + glob_script_mem.scriptptr=svd_sp; +} +#define D_CMND_SCRIPT "Script" +#define D_CMND_SUBSCRIBE "Subscribe" +#define D_CMND_UNSUBSCRIBE "Unsubscribe" + +enum ScriptCommands { CMND_SCRIPT,CMND_SUBSCRIBE, CMND_UNSUBSCRIBE }; +const char kScriptCommands[] PROGMEM = D_CMND_SCRIPT "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE; + +bool ScriptCommand(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t index = XdrvMailbox.index; + + if (tasm_cmd_activ) return false; + + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kScriptCommands); + if (-1 == command_code) { + serviced = false; + } + else if ((CMND_SCRIPT == command_code) && (index > 0)) { + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { + switch (XdrvMailbox.payload) { + case 0: + case 1: + bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); + break; +#ifdef xSCRIPT_STRIP_COMMENTS + case 2: + bitWrite(Settings.rule_enabled, 1,0); + break; + case 3: + bitWrite(Settings.rule_enabled, 1,1); + break; +#endif + } + } else { + if ('>' == XdrvMailbox.data[0]) { + + snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"),command,XdrvMailbox.data); + if (bitRead(Settings.rule_enabled, 0)) { + for (uint8_t count=0; count> 1; +} + +void dateTime(uint16_t* date, uint16_t* time) { + + *date = xFAT_DATE(RtcTime.year,RtcTime.month, RtcTime.day_of_month); + + *time = xFAT_TIME(RtcTime.hour,RtcTime.minute,RtcTime.second); +} + +#endif + + + +#ifdef SUPPORT_MQTT_EVENT +# 4199 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" +bool ScriptMqttData(void) +{ + bool serviced = false; + + toLog(XdrvMailbox.data); + if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { + return false; + } + String sTopic = XdrvMailbox.topic; + String sData = XdrvMailbox.data; + + MQTT_Subscription event_item; + + for (uint32_t index = 0; index < subscriptions.size(); index++) { + event_item = subscriptions.get(index); + + + if (sTopic.startsWith(event_item.Topic)) { + + serviced = true; + String value; + String lkey; + if (event_item.Key.length() == 0) { + value = sData; + } else { + StaticJsonBuffer<400> jsonBuf; + JsonObject& jsonData = jsonBuf.parseObject(sData); + String key1 = event_item.Key; + String key2; + if (!jsonData.success()) break; + int dot; + if ((dot = key1.indexOf('.')) > 0) { + key2 = key1.substring(dot+1); + key1 = key1.substring(0, dot); + lkey=key2; + if (!jsonData[key1][key2].success()) break; + value = (const char *)jsonData[key1][key2]; + } else { + if (!jsonData[key1].success()) break; + value = (const char *)jsonData[key1]; + lkey=key1; + } + } + value.trim(); + char sbuffer[128]; + + if (!strncmp(lkey.c_str(),"Epoch",5)) { + uint32_t ep=atoi(value.c_str())-(uint32_t)EPOCH_OFFSET; + snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=%d\n"), event_item.Event.c_str(),ep); + } else { + snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=\"%s\"\n"), event_item.Event.c_str(), value.c_str()); + } + + execute_script(sbuffer); + } + } + return serviced; +} +# 4274 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" +String ScriptSubscribe(const char *data, int data_len) +{ + MQTT_Subscription subscription_item; + String events; + if (data_len > 0) { + char parameters[data_len+1]; + memcpy(parameters, data, data_len); + parameters[data_len] = '\0'; + String event_name, topic, key; + + char * pos = strtok(parameters, ","); + if (pos) { + event_name = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + topic = Trim(pos); + pos = strtok(nullptr, ","); + if (pos) { + key = Trim(pos); + } + } + } + + + if (event_name.length() > 0 && topic.length() > 0) { + + for (uint32_t index=0; index < subscriptions.size(); index++) { + if (subscriptions.get(index).Event.equals(event_name)) { + + String stopic = subscriptions.get(index).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(index); + break; + } + } + + if (!topic.endsWith("#")) { + if (topic.endsWith("/")) { + topic.concat("#"); + } else { + topic.concat("/#"); + } + } + + + subscription_item.Event = event_name; + subscription_item.Topic = topic.substring(0, topic.length() - 2); + subscription_item.Key = key; + subscriptions.add(subscription_item); + + MqttSubscribe(topic.c_str()); + events.concat(event_name + "," + topic + + (key.length()>0 ? "," : "") + + key); + } else { + events = D_JSON_WRONG_PARAMETERS; + } + } else { + + for (uint32_t index=0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + events.concat(subscription_item.Event + "," + subscription_item.Topic + + (subscription_item.Key.length()>0 ? "," : "") + + subscription_item.Key + "; "); + } + } + return events; +} +# 4354 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" +String ScriptUnsubscribe(const char * data, int data_len) +{ + MQTT_Subscription subscription_item; + String events; + if (data_len > 0) { + for (uint32_t index = 0; index < subscriptions.size(); index++) { + subscription_item = subscriptions.get(index); + if (subscription_item.Event.equalsIgnoreCase(data)) { + String stopic = subscription_item.Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + events = subscription_item.Event; + subscriptions.remove(index); + break; + } + } + } else { + + String stopic; + while (subscriptions.size() > 0) { + events.concat(subscriptions.get(0).Event + "; "); + stopic = subscriptions.get(0).Topic + "/#"; + MqttUnsubscribe(stopic.c_str()); + subscriptions.remove(0); + } + } + return events; +} +#endif + + + +#ifdef USE_SCRIPT_WEB_DISPLAY + +void Script_Check_HTML_Setvars(void) { + + if (!HttpCheckPriviledgedAccess()) { return; } + + if (WebServer->hasArg("sv")) { + String stmp = WebServer->arg("sv"); + char cmdbuf[64]; + memset(cmdbuf,0,sizeof(cmdbuf)); + char *cp=cmdbuf; + *cp++='>'; + strncpy(cp,stmp.c_str(),sizeof(cmdbuf)-1); + char *cp1=strchr(cp,'_'); + if (!cp1) return; + *cp1=0; + char vname[32]; + strncpy(vname,cp,sizeof(vname)); + *cp1='='; + cp1++; + + struct T_INDEX ind; + uint8_t vtype; + isvar(vname,&vtype,&ind,0,0,0); + if (vtype!=NUM_RES && vtype&STYPE) { + + uint8_t tlen=strlen(cp1); + memmove(cp1+1,cp1,tlen); + *cp1='\"'; + *(cp1+tlen+1)='\"'; + } + + + execute_script(cmdbuf); + Run_Scripter(">E",2,0); + } +} + + +const char SCRIPT_MSG_BUTTONa[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUTTONa_TBL[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUTTONb[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUT_START[] PROGMEM = + "
"; +const char SCRIPT_MSG_BUT_START_TBL[] PROGMEM = + ""; + +const char SCRIPT_MSG_BUT_STOP[] PROGMEM = + ""; +const char SCRIPT_MSG_BUT_STOP_TBL[] PROGMEM = + "
"; + +const char SCRIPT_MSG_SLIDER[] PROGMEM = + "
%s
%s%s
" + "
"; + +const char SCRIPT_MSG_CHKBOX[] PROGMEM = + "
"; + +const char SCRIPT_MSG_TEXTINP[] PROGMEM = + "
"; + +const char SCRIPT_MSG_NUMINP[] PROGMEM = + "
"; + + +void ScriptGetVarname(char *nbuf,char *sp, uint32_t blen) { +uint32_t cnt; + for (cnt=0;cntW",-2,0); + if (web_script==99) { + char line[128]; + char tmp[128]; + uint8_t optflg=0; + char *lp=glob_script_mem.section_ptr+2; + while (lp) { + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + + memcpy(line,lp,sizeof(line)); + line[sizeof(line)-1]=0; + char *cp=line; + for (uint32_t i=0; i0) { + cp="checked='checked'"; + uval=0; + } else { + cp=""; + uval=1; + } + WSContentSend_PD(SCRIPT_MSG_CHKBOX,label,(char*)cp,uval,vname); + + } else if (!strncmp(lin,"bu(",3)) { + char *lp=lin+3; + uint8_t bcnt=0; + char *found=lin; + while (bcnt<4) { + found=strstr(found,"bu("); + if (!found) break; + found+=3; + bcnt++; + } + uint8_t proz=100/bcnt; + if (!optflg && bcnt>1) proz-=2; + if (optflg) WSContentSend_PD(SCRIPT_MSG_BUT_START_TBL); + else WSContentSend_PD(SCRIPT_MSG_BUT_START); + for (uint32_t cnt=0;cnt0) { + cp=ontxt; + uval=0; + } else { + cp=offtxt; + uval=1; + } + if (bcnt>1 && cnt==bcnt-1) { + if (!optflg) proz+=2; + } + if (!optflg) { + WSContentSend_PD(SCRIPT_MSG_BUTTONa,proz,uval,vname,cp); + } else { + WSContentSend_PD(SCRIPT_MSG_BUTTONa_TBL,proz,uval,vname,cp); + } + if (bcnt>1 && cnt%s
"),tmp); + } else { + WSContentSend_PD(PSTR("{s}%s{e}"),tmp); + } + } + } + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + } +} +#endif + + +#ifdef USE_SENDMAIL +void script_send_email_body(BearSSL::WiFiClientSecure_light *client) { +uint8_t msect=Run_Scripter(">m",-2,0); + if (msect==99) { + char line[128]; + char tmp[128]; + char *lp=glob_script_mem.section_ptr+2; + while (lp) { + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + + memcpy(line,lp,sizeof(line)); + line[sizeof(line)-1]=0; + char *cp=line; + for (uint32_t i=0; iprintln(tmp); + } + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + } else { + client->println("*"); + } +} +#endif + +#ifdef USE_SCRIPT_JSON_EXPORT +void ScriptJsonAppend(void) { + uint8_t web_script=Run_Scripter(">J",-2,0); + if (web_script==99) { + char line[128]; + char tmp[128]; + char *lp=glob_script_mem.section_ptr+2; + while (lp) { + while (*lp==SCRIPT_EOL) { + lp++; + } + if (!*lp || *lp=='#' || *lp=='>') { + break; + } + if (*lp!=';') { + + memcpy(line,lp,sizeof(line)); + line[sizeof(line)-1]=0; + char *cp=line; + for (uint32_t i=0; iB",2,0); + fast_script=Run_Scripter(">F",-2,0); +#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) + Script_Check_Hue(0); +#endif + } + break; + case FUNC_EVERY_100_MSECOND: + ScripterEvery100ms(); + break; + case FUNC_EVERY_SECOND: + ScriptEverySecond(); + break; + case FUNC_COMMAND: + result = ScriptCommand(); + break; + case FUNC_SET_POWER: +#ifdef SCRIPT_POWER_SECTION + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">P",2,0); +#else + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,0); +#endif + break; + case FUNC_RULES_PROCESS: + if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,mqtt_data); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_RULES); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration); + WebServer->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration); + +#ifdef USE_SCRIPT_FATFS + WebServer->on("/u3", HTTP_POST,[]() { WebServer->sendHeader("Location","/u3");WebServer->send(303);},script_upload); + WebServer->on("/u3", HTTP_GET,ScriptFileUploadSuccess); + WebServer->on("/upl", HTTP_GET,Script_FileUploadConfiguration); +#endif + break; +#endif + case FUNC_SAVE_BEFORE_RESTART: + if (bitRead(Settings.rule_enabled, 0)) { + Run_Scripter(">R",2,0); + Scripter_save_pvars(); + } + break; +#ifdef SUPPORT_MQTT_EVENT + case FUNC_MQTT_DATA: + if (bitRead(Settings.rule_enabled, 0)) { + result = ScriptMqttData(); + } + break; +#endif +#ifdef USE_SCRIPT_WEB_DISPLAY + case FUNC_WEB_SENSOR: + if (bitRead(Settings.rule_enabled, 0)) { + ScriptWebShow(); + } + break; +#endif + +#ifdef USE_SCRIPT_JSON_EXPORT + case FUNC_JSON_APPEND: + if (bitRead(Settings.rule_enabled, 0)) { + ScriptJsonAppend(); + } + break; +#endif + +#ifdef USE_BUTTON_EVENT + case FUNC_BUTTON_PRESSED: + if (bitRead(Settings.rule_enabled, 0)) { + if ((script_button[XdrvMailbox.index]&1)!=(XdrvMailbox.payload&1)) { + script_button[XdrvMailbox.index]=XdrvMailbox.payload; + Run_Scripter(">b",2,0); + } + } + break; +#endif + + } + return result; +} + + + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_11_knx.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_11_knx.ino" +#ifdef USE_KNX +# 51 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_11_knx.ino" +#define XDRV_11 11 + +#include + +address_t KNX_physs_addr; +address_t KNX_addr; + +#define KNX_Empty 255 + +#define TOGGLE_INHIBIT_TIME 15 + +float last_temp; +float last_hum; +uint8_t toggle_inhibit; + +typedef struct __device_parameters +{ + uint8_t type; + + + + + bool show; + + bool last_state; + + callback_id_t CB_id; + + + + + +} device_parameters_t; + + +device_parameters_t device_param[] = { + { 1, false, false, KNX_Empty }, + { 2, false, false, KNX_Empty }, + { 3, false, false, KNX_Empty }, + { 4, false, false, KNX_Empty }, + { 5, false, false, KNX_Empty }, + { 6, false, false, KNX_Empty }, + { 7, false, false, KNX_Empty }, + { 8, false, false, KNX_Empty }, + { 9, false, false, KNX_Empty }, + { 10, false, false, KNX_Empty }, + { 11, false, false, KNX_Empty }, + { 12, false, false, KNX_Empty }, + { 13, false, false, KNX_Empty }, + { 14, false, false, KNX_Empty }, + { 15, false, false, KNX_Empty }, + { 16, false, false, KNX_Empty }, + { KNX_TEMPERATURE, false, false, KNX_Empty }, + { KNX_HUMIDITY , false, false, KNX_Empty }, + { KNX_ENERGY_VOLTAGE , false, false, KNX_Empty }, + { KNX_ENERGY_CURRENT , false, false, KNX_Empty }, + { KNX_ENERGY_POWER , false, false, KNX_Empty }, + { KNX_ENERGY_POWERFACTOR , false, false, KNX_Empty }, + { KNX_ENERGY_DAILY , false, false, KNX_Empty }, + { KNX_ENERGY_START , false, false, KNX_Empty }, + { KNX_ENERGY_TOTAL , false, false, KNX_Empty }, + { KNX_SLOT1 , false, false, KNX_Empty }, + { KNX_SLOT2 , false, false, KNX_Empty }, + { KNX_SLOT3 , false, false, KNX_Empty }, + { KNX_SLOT4 , false, false, KNX_Empty }, + { KNX_SLOT5 , false, false, KNX_Empty }, + { KNX_Empty, false, false, KNX_Empty} +}; + + +const char * device_param_ga[] = { + D_TIMER_OUTPUT " 1", + D_TIMER_OUTPUT " 2", + D_TIMER_OUTPUT " 3", + D_TIMER_OUTPUT " 4", + D_TIMER_OUTPUT " 5", + D_TIMER_OUTPUT " 6", + D_TIMER_OUTPUT " 7", + D_TIMER_OUTPUT " 8", + D_SENSOR_BUTTON " 1", + D_SENSOR_BUTTON " 2", + D_SENSOR_BUTTON " 3", + D_SENSOR_BUTTON " 4", + D_SENSOR_BUTTON " 5", + D_SENSOR_BUTTON " 6", + D_SENSOR_BUTTON " 7", + D_SENSOR_BUTTON " 8", + D_TEMPERATURE , + D_HUMIDITY , + D_VOLTAGE , + D_CURRENT , + D_POWERUSAGE , + D_POWER_FACTOR , + D_ENERGY_TODAY , + D_ENERGY_YESTERDAY , + D_ENERGY_TOTAL , + D_KNX_TX_SLOT " 1", + D_KNX_TX_SLOT " 2", + D_KNX_TX_SLOT " 3", + D_KNX_TX_SLOT " 4", + D_KNX_TX_SLOT " 5", + nullptr +}; + + +const char *device_param_cb[] = { + D_TIMER_OUTPUT " 1", + D_TIMER_OUTPUT " 2", + D_TIMER_OUTPUT " 3", + D_TIMER_OUTPUT " 4", + D_TIMER_OUTPUT " 5", + D_TIMER_OUTPUT " 6", + D_TIMER_OUTPUT " 7", + D_TIMER_OUTPUT " 8", + D_TIMER_OUTPUT " 1 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 2 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 3 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 4 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 5 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 6 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 7 " D_BUTTON_TOGGLE, + D_TIMER_OUTPUT " 8 " D_BUTTON_TOGGLE, + D_REPLY " " D_TEMPERATURE, + D_REPLY " " D_HUMIDITY, + D_REPLY " " D_VOLTAGE , + D_REPLY " " D_CURRENT , + D_REPLY " " D_POWERUSAGE , + D_REPLY " " D_POWER_FACTOR , + D_REPLY " " D_ENERGY_TODAY , + D_REPLY " " D_ENERGY_YESTERDAY , + D_REPLY " " D_ENERGY_TOTAL , + D_KNX_RX_SLOT " 1", + D_KNX_RX_SLOT " 2", + D_KNX_RX_SLOT " 3", + D_KNX_RX_SLOT " 4", + D_KNX_RX_SLOT " 5", + nullptr +}; + + +#define D_PRFX_KNX "Knx" +#define D_CMND_KNXTXCMND "Tx_Cmnd" +#define D_CMND_KNXTXVAL "Tx_Val" +#define D_CMND_KNX_ENABLED "_Enabled" +#define D_CMND_KNX_ENHANCED "_Enhanced" +#define D_CMND_KNX_PA "_PA" +#define D_CMND_KNX_GA "_GA" +#define D_CMND_KNX_CB "_CB" + +const char kKnxCommands[] PROGMEM = D_PRFX_KNX "|" + D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|" D_CMND_KNX_ENHANCED "|" D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB ; + +void (* const KnxCommand[])(void) PROGMEM = { + &CmndKnxTxCmnd, &CmndKnxTxVal, &CmndKnxEnabled, &CmndKnxEnhanced, &CmndKnxPa, &CmndKnxGa, &CmndKnxCb }; + +uint8_t KNX_GA_Search( uint8_t param, uint8_t start = 0 ) +{ + for (uint32_t i = start; i < Settings.knx_GA_registered; ++i) + { + if ( Settings.knx_GA_param[i] == param ) + { + if ( Settings.knx_GA_addr[i] != 0 ) + { + if ( i >= start ) { return i; } + } + } + } + return KNX_Empty; +} + + +uint8_t KNX_CB_Search( uint8_t param, uint8_t start = 0 ) +{ + for (uint32_t i = start; i < Settings.knx_CB_registered; ++i) + { + if ( Settings.knx_CB_param[i] == param ) + { + if ( Settings.knx_CB_addr[i] != 0 ) + { + if ( i >= start ) { return i; } + } + } + } + return KNX_Empty; +} + + +void KNX_ADD_GA( uint8_t GAop, uint8_t GA_FNUM, uint8_t GA_AREA, uint8_t GA_FDEF ) +{ + + if ( Settings.knx_GA_registered >= MAX_KNX_GA ) { return; } + if ( GA_FNUM == 0 && GA_AREA == 0 && GA_FDEF == 0 ) { return; } + + + Settings.knx_GA_param[Settings.knx_GA_registered] = GAop; + KNX_addr.ga.area = GA_FNUM; + KNX_addr.ga.line = GA_AREA; + KNX_addr.ga.member = GA_FDEF; + Settings.knx_GA_addr[Settings.knx_GA_registered] = KNX_addr.value; + + Settings.knx_GA_registered++; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " GA #%d: %s " D_TO " %d/%d/%d"), + Settings.knx_GA_registered, + device_param_ga[GAop-1], + GA_FNUM, GA_AREA, GA_FDEF ); +} + + +void KNX_DEL_GA( uint8_t GAnum ) +{ + + uint8_t dest_offset = 0; + uint8_t src_offset = 0; + uint8_t len = 0; + + + Settings.knx_GA_param[GAnum-1] = 0; + + if (GAnum == 1) + { + + src_offset = 1; + + + + len = (Settings.knx_GA_registered - 1); + } + else if (GAnum == Settings.knx_GA_registered) + { + + } + else + { + + + + + dest_offset = GAnum -1 ; + src_offset = dest_offset + 1; + len = (Settings.knx_GA_registered - GAnum); + } + + if (len > 0) + { + memmove(Settings.knx_GA_param + dest_offset, Settings.knx_GA_param + src_offset, len * sizeof(uint8_t)); + memmove(Settings.knx_GA_addr + dest_offset, Settings.knx_GA_addr + src_offset, len * sizeof(uint16_t)); + } + + Settings.knx_GA_registered--; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " GA #%d"), + GAnum ); +} + + +void KNX_ADD_CB( uint8_t CBop, uint8_t CB_FNUM, uint8_t CB_AREA, uint8_t CB_FDEF ) +{ + + if ( Settings.knx_CB_registered >= MAX_KNX_CB ) { return; } + if ( CB_FNUM == 0 && CB_AREA == 0 && CB_FDEF == 0 ) { return; } + + + if ( device_param[CBop-1].CB_id == KNX_Empty ) + { + + device_param[CBop-1].CB_id = knx.callback_register("", KNX_CB_Action, &device_param[CBop-1]); + + + + + } + + Settings.knx_CB_param[Settings.knx_CB_registered] = CBop; + KNX_addr.ga.area = CB_FNUM; + KNX_addr.ga.line = CB_AREA; + KNX_addr.ga.member = CB_FDEF; + Settings.knx_CB_addr[Settings.knx_CB_registered] = KNX_addr.value; + + knx.callback_assign( device_param[CBop-1].CB_id, KNX_addr ); + + Settings.knx_CB_registered++; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " CB #%d: %d/%d/%d " D_TO " %s"), + Settings.knx_CB_registered, + CB_FNUM, CB_AREA, CB_FDEF, + device_param_cb[CBop-1] ); +} + + +void KNX_DEL_CB( uint8_t CBnum ) +{ + uint8_t oldparam = Settings.knx_CB_param[CBnum-1]; + uint8_t dest_offset = 0; + uint8_t src_offset = 0; + uint8_t len = 0; + + + knx.callback_unassign(CBnum-1); + Settings.knx_CB_param[CBnum-1] = 0; + + if (CBnum == 1) + { + + src_offset = 1; + + + + len = (Settings.knx_CB_registered - 1); + } + else if (CBnum == Settings.knx_CB_registered) + { + + } + else + { + + + + + dest_offset = CBnum -1 ; + src_offset = dest_offset + 1; + len = (Settings.knx_CB_registered - CBnum); + } + + if (len > 0) + { + memmove(Settings.knx_CB_param + dest_offset, Settings.knx_CB_param + src_offset, len * sizeof(uint8_t)); + memmove(Settings.knx_CB_addr + dest_offset, Settings.knx_CB_addr + src_offset, len * sizeof(uint16_t)); + } + + Settings.knx_CB_registered--; + + + if ( KNX_CB_Search( oldparam ) == KNX_Empty ) { + knx.callback_deregister( device_param[oldparam-1].CB_id ); + device_param[oldparam-1].CB_id = KNX_Empty; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " CB #%d"), CBnum ); +} + + +bool KNX_CONFIG_NOT_MATCH(void) +{ + + for (uint32_t i = 0; i < KNX_MAX_device_param; ++i) + { + if ( !device_param[i].show ) { + + + + if ( KNX_GA_Search(i+1) != KNX_Empty ) { return true; } + + if ( i < 8 ) + { + if ( KNX_CB_Search(i+1) != KNX_Empty ) { return true; } + if ( KNX_CB_Search(i+9) != KNX_Empty ) { return true; } + } + + if ( i > 15 ) + { + if ( KNX_CB_Search(i+1) != KNX_Empty ) { return true; } + } + } + } + + + for (uint32_t i = 0; i < Settings.knx_GA_registered; ++i) + { + if ( Settings.knx_GA_param[i] != 0 ) + { + if ( Settings.knx_GA_addr[i] == 0 ) + { + return true; + } + } + } + for (uint32_t i = 0; i < Settings.knx_CB_registered; ++i) + { + if ( Settings.knx_CB_param[i] != 0 ) + { + if ( Settings.knx_CB_addr[i] == 0 ) + { + return true; + } + } + } + + return false; +} + + +void KNXStart(void) +{ + knx.start(nullptr); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_START)); +} + + +void KNX_INIT(void) +{ + + if (Settings.knx_GA_registered > MAX_KNX_GA) { Settings.knx_GA_registered = MAX_KNX_GA; } + if (Settings.knx_CB_registered > MAX_KNX_CB) { Settings.knx_CB_registered = MAX_KNX_CB; } + + + KNX_physs_addr.value = Settings.knx_physsical_addr; + knx.physical_address_set( KNX_physs_addr ); +# 472 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_11_knx.ino" + for (uint32_t i = 0; i < devices_present; ++i) + { + device_param[i].show = true; + } + for (uint32_t i = GPIO_SWT1; i < GPIO_SWT4 + 1; ++i) + { + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1 + 8].show = true; } + } + for (uint32_t i = GPIO_KEY1; i < GPIO_KEY4 + 1; ++i) + { + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1 + 8].show = true; } + } + for (uint32_t i = GPIO_SWT1_NP; i < GPIO_SWT4_NP + 1; ++i) + { + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1_NP + 8].show = true; } + } + for (uint32_t i = GPIO_KEY1_NP; i < GPIO_KEY4_NP + 1; ++i) + { + if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1_NP + 8].show = true; } + } + if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } + if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } + if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } +#ifdef USE_DS18x20 + if (GetUsedInModule(GPIO_DSB, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } +#endif + if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } + if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } + if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } + +#if defined(USE_ENERGY_SENSOR) + + if ( energy_flg != ENERGY_NONE ) { + device_param[KNX_ENERGY_POWER-1].show = true; + device_param[KNX_ENERGY_DAILY-1].show = true; + device_param[KNX_ENERGY_START-1].show = true; + device_param[KNX_ENERGY_TOTAL-1].show = true; + device_param[KNX_ENERGY_VOLTAGE-1].show = true; + device_param[KNX_ENERGY_CURRENT-1].show = true; + device_param[KNX_ENERGY_POWERFACTOR-1].show = true; + } +#endif + +#ifdef USE_RULES + device_param[KNX_SLOT1-1].show = true; + device_param[KNX_SLOT2-1].show = true; + device_param[KNX_SLOT3-1].show = true; + device_param[KNX_SLOT4-1].show = true; + device_param[KNX_SLOT5-1].show = true; +#endif + + + if (KNX_CONFIG_NOT_MATCH()) { + Settings.knx_GA_registered = 0; + Settings.knx_CB_registered = 0; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " " D_KNX_PARAMETERS)); + } + + + + + uint8_t j; + for (uint32_t i = 0; i < Settings.knx_CB_registered; ++i) + { + j = Settings.knx_CB_param[i]; + if ( j > 0 ) + { + device_param[j-1].CB_id = knx.callback_register("", KNX_CB_Action, &device_param[j-1]); + + + + KNX_addr.value = Settings.knx_CB_addr[i]; + knx.callback_assign( device_param[j-1].CB_id, KNX_addr ); + } + } +} + + +void KNX_CB_Action(message_t const &msg, void *arg) +{ + device_parameters_t *chan = (device_parameters_t *)arg; + if (!(Settings.flag.knx_enabled)) { return; } + + char tempchar[33]; + + if (msg.data_len == 1) { + + tempchar[0] = msg.data[0]; + tempchar[1] = '\0'; + } else { + + float tempvar = knx.data_to_2byte_float(msg.data); + dtostrfd(tempvar,2,tempchar); + } + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %s " D_TO " %s"), + msg.received_on.ga.area, msg.received_on.ga.line, msg.received_on.ga.member, + (msg.ct == KNX_CT_WRITE) ? D_KNX_COMMAND_WRITE : (msg.ct == KNX_CT_READ) ? D_KNX_COMMAND_READ : D_KNX_COMMAND_OTHER, + tempchar, + device_param_cb[(chan->type)-1]); + + switch (msg.ct) + { + case KNX_CT_WRITE: + if (chan->type < 9) + { + ExecuteCommandPower(chan->type, msg.data[0], SRC_KNX); + } + else if (chan->type < 17) + { + if (!toggle_inhibit) { + ExecuteCommandPower((chan->type) -8, POWER_TOGGLE, SRC_KNX); + if (Settings.flag.knx_enable_enhancement) { + toggle_inhibit = TOGGLE_INHIBIT_TIME; + } + } + } +#ifdef USE_RULES + else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) + { + if (!toggle_inhibit) { + char command[25]; + if (msg.data_len == 1) { + + snprintf_P(command, sizeof(command), PSTR("event KNXRX_CMND%d=%d"), ((chan->type) - KNX_SLOT1 + 1 ), msg.data[0]); + } else { + + snprintf_P(command, sizeof(command), PSTR("event KNXRX_VAL%d=%s"), ((chan->type) - KNX_SLOT1 + 1 ), tempchar); + } + ExecuteCommand(command, SRC_KNX); + if (Settings.flag.knx_enable_enhancement) { + toggle_inhibit = TOGGLE_INHIBIT_TIME; + } + } + } +#endif + break; + + case KNX_CT_READ: + if (chan->type < 9) + { + knx.answer_1bit(msg.received_on, chan->last_state); + if (Settings.flag.knx_enable_enhancement) { + knx.answer_1bit(msg.received_on, chan->last_state); + knx.answer_1bit(msg.received_on, chan->last_state); + } + } + else if (chan->type == KNX_TEMPERATURE) + { + knx.answer_2byte_float(msg.received_on, last_temp); + if (Settings.flag.knx_enable_enhancement) { + knx.answer_2byte_float(msg.received_on, last_temp); + knx.answer_2byte_float(msg.received_on, last_temp); + } + } + else if (chan->type == KNX_HUMIDITY) + { + knx.answer_2byte_float(msg.received_on, last_hum); + if (Settings.flag.knx_enable_enhancement) { + knx.answer_2byte_float(msg.received_on, last_hum); + knx.answer_2byte_float(msg.received_on, last_hum); + } + } +#ifdef USE_RULES + else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) + { + if (!toggle_inhibit) { + char command[25]; + snprintf_P(command, sizeof(command), PSTR("event KNXRX_REQ%d"), ((chan->type) - KNX_SLOT1 + 1 ) ); + ExecuteCommand(command, SRC_KNX); + if (Settings.flag.knx_enable_enhancement) { + toggle_inhibit = TOGGLE_INHIBIT_TIME; + } + } + } +#endif + break; + } +} + + +void KnxUpdatePowerState(uint8_t device, power_t state) +{ + if (!(Settings.flag.knx_enabled)) { return; } + + device_param[device -1].last_state = bitRead(state, device -1); + + + uint8_t i = KNX_GA_Search(device); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + knx.write_1bit(KNX_addr, device_param[device -1].last_state); + if (Settings.flag.knx_enable_enhancement) { + knx.write_1bit(KNX_addr, device_param[device -1].last_state); + knx.write_1bit(KNX_addr, device_param[device -1].last_state); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), + device_param_ga[device -1], device_param[device -1].last_state, + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(device, i + 1); + } +} + + +void KnxSendButtonPower(void) +{ + if (!(Settings.flag.knx_enabled)) { return; } + + uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; + uint32_t device = XdrvMailbox.payload & 0xFF; + uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; +# 693 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_11_knx.ino" + uint8_t i = KNX_GA_Search(device + 8); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + knx.write_1bit(KNX_addr, !(state == 0)); + if (Settings.flag.knx_enable_enhancement) { + knx.write_1bit(KNX_addr, !(state == 0)); + knx.write_1bit(KNX_addr, !(state == 0)); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), + device_param_ga[device + 7], !(state == 0), + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(device + 8, i + 1); + } + +} + + +void KnxSensor(uint8_t sensor_type, float value) +{ + if (sensor_type == KNX_TEMPERATURE) + { + last_temp = value; + } else if (sensor_type == KNX_HUMIDITY) + { + last_hum = value; + } + + if (!(Settings.flag.knx_enabled)) { return; } + + uint8_t i = KNX_GA_Search(sensor_type); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + knx.write_2byte_float(KNX_addr, value); + if (Settings.flag.knx_enable_enhancement) { + knx.write_2byte_float(KNX_addr, value); + knx.write_2byte_float(KNX_addr, value); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s " D_SENT_TO " %d.%d.%d "), + device_param_ga[sensor_type -1], + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(sensor_type, i+1); + } +} + + + + + + +#ifdef USE_WEBSERVER +#ifdef USE_KNX_WEB_MENU +const char S_CONFIGURE_KNX[] PROGMEM = D_CONFIGURE_KNX; + +const char HTTP_BTN_MENU_KNX[] PROGMEM = + "

"; + +const char HTTP_FORM_KNX[] PROGMEM = + "
" + " " D_KNX_PARAMETERS " " + "
" + "
" + "" D_KNX_PHYSICAL_ADDRESS " " + " . " + " . " + "" + "

" D_KNX_PHYSICAL_ADDRESS_NOTE "

" + "" D_KNX_ENABLE "" D_KNX_ENHANCEMENT "

" + + "
" + "" D_KNX_GROUP_ADDRESS_TO_WRITE "
" + + " / " + " / " + " "; + +const char HTTP_FORM_KNX_ADD_BTN[] PROGMEM = + "

" + ""; + +const char HTTP_FORM_KNX_ADD_TABLE_ROW[] PROGMEM = + "" + ""; + +const char HTTP_FORM_KNX3[] PROGMEM = + "
%s -> %d / %d / %d

" + "
" + "" D_KNX_GROUP_ADDRESS_TO_READ "
"; + +const char HTTP_FORM_KNX4[] PROGMEM = + "-> -> ")); + WSContentSend_P(HTTP_FORM_KNX_GA, "GA_FNUM", "GA_AREA", "GA_FDEF"); + WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "GAwarning", (Settings.knx_GA_registered < MAX_KNX_GA) ? "" : "disabled", 1); + for (uint32_t i = 0; i < Settings.knx_GA_registered ; ++i) + { + if ( Settings.knx_GA_param[i] ) + { + KNX_addr.value = Settings.knx_GA_addr[i]; + WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW, device_param_ga[Settings.knx_GA_param[i]-1], KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, i +1); + } + } + + WSContentSend_P(HTTP_FORM_KNX3); + WSContentSend_P(HTTP_FORM_KNX_GA, "CB_FNUM", "CB_AREA", "CB_FDEF"); + WSContentSend_P(HTTP_FORM_KNX4); + + uint8_t j; + for (uint32_t i = 0; i < KNX_MAX_device_param ; i++) + { + + if ( (i > 8) && (i < 16) ) { j=i-8; } else { j=i; } + if ( i == 8 ) { j = 0; } + if ( device_param[j].show ) + { + WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_cb[i]); + } + } + WSContentSend_P(PSTR(" ")); + WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "CBwarning", (Settings.knx_CB_registered < MAX_KNX_CB) ? "" : "disabled", 2); + + for (uint32_t i = 0; i < Settings.knx_CB_registered ; ++i) + { + if ( Settings.knx_CB_param[i] ) + { + KNX_addr.value = Settings.knx_CB_addr[i]; + WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW2, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, device_param_cb[Settings.knx_CB_param[i]-1], i +1); + } + } + WSContentSend_P(PSTR("
")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); + } + +} + + +void KNX_Save_Settings(void) +{ + String stmp; + address_t KNX_addr; + + Settings.flag.knx_enabled = WebServer->hasArg("b1"); + Settings.flag.knx_enable_enhancement = WebServer->hasArg("b2"); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ENABLED ": %d, " D_KNX_ENHANCEMENT ": %d"), + Settings.flag.knx_enabled, Settings.flag.knx_enable_enhancement ); + + stmp = WebServer->arg("area"); + KNX_addr.pa.area = stmp.toInt(); + stmp = WebServer->arg("line"); + KNX_addr.pa.line = stmp.toInt(); + stmp = WebServer->arg("member"); + KNX_addr.pa.member = stmp.toInt(); + Settings.knx_physsical_addr = KNX_addr.value; + knx.physical_address_set( KNX_addr ); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_KNX_PHYSICAL_ADDRESS ": %d.%d.%d "), + KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA: %d"), + Settings.knx_GA_registered ); + for (uint32_t i = 0; i < Settings.knx_GA_registered ; ++i) + { + KNX_addr.value = Settings.knx_GA_addr[i]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA #%d: %s " D_TO " %d/%d/%d"), + i+1, device_param_ga[Settings.knx_GA_param[i]-1], + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); + + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB: %d"), + Settings.knx_CB_registered ); + for (uint32_t i = 0; i < Settings.knx_CB_registered ; ++i) + { + KNX_addr.value = Settings.knx_CB_addr[i]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB #%d: %d/%d/%d " D_TO " %s"), + i+1, + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, + device_param_cb[Settings.knx_CB_param[i]-1] ); + } +} + +#endif +#endif + + + + + +void CmndKnxTxCmnd(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled) { + + + + uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); + if (Settings.flag.knx_enable_enhancement) { + knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); + knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), + device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], !(XdrvMailbox.payload == 0), + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); + } + ResponseCmndIdxChar (XdrvMailbox.data ); + } +} + +void CmndKnxTxVal(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled) { + + + + uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); + while ( i != KNX_Empty ) { + KNX_addr.value = Settings.knx_GA_addr[i]; + + float tempvar = CharToFloat(XdrvMailbox.data); + dtostrfd(tempvar,2,XdrvMailbox.data); + + knx.write_2byte_float(KNX_addr, tempvar); + if (Settings.flag.knx_enable_enhancement) { + knx.write_2byte_float(KNX_addr, tempvar); + knx.write_2byte_float(KNX_addr, tempvar); + } + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"), + device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data, + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); + + i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); + } + ResponseCmndIdxChar (XdrvMailbox.data ); + } +} + +void CmndKnxEnabled(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag.knx_enabled = XdrvMailbox.payload; + } + ResponseCmndChar (GetStateText(Settings.flag.knx_enabled) ); +} + +void CmndKnxEnhanced(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + Settings.flag.knx_enable_enhancement = XdrvMailbox.payload; + } + ResponseCmndChar (GetStateText(Settings.flag.knx_enable_enhancement) ); +} + +void CmndKnxPa(void) +{ + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, ".") != nullptr) { + char sub_string[XdrvMailbox.data_len]; + + int pa_area = atoi(subStr(sub_string, XdrvMailbox.data, ".", 1)); + int pa_line = atoi(subStr(sub_string, XdrvMailbox.data, ".", 2)); + int pa_member = atoi(subStr(sub_string, XdrvMailbox.data, ".", 3)); + + if ( ((pa_area == 0) && (pa_line == 0) && (pa_member == 0)) + || (pa_area > 15) || (pa_line > 15) || (pa_member > 255) ) { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + + KNX_addr.pa.area = pa_area; + KNX_addr.pa.line = pa_line; + KNX_addr.pa.member = pa_member; + Settings.knx_physsical_addr = KNX_addr.value; + } + } + KNX_addr.value = Settings.knx_physsical_addr; + Response_P (PSTR("{\"%s\":\"%d.%d.%d\"}"), + XdrvMailbox.command, KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); +} + +void CmndKnxGa(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_GA)) { + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + char sub_string[XdrvMailbox.data_len]; + + int ga_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); + int ga_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + int ga_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + int ga_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); + + if ( ((ga_area == 0) && (ga_line == 0) && (ga_member == 0)) + || (ga_area > 31) || (ga_line > 7) || (ga_member > 255) + || (ga_option < 0) || ((ga_option > KNX_MAX_device_param ) && (ga_option != KNX_Empty)) + || (!device_param[ga_option-1].show) ) { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + + KNX_addr.ga.area = ga_area; + KNX_addr.ga.line = ga_line; + KNX_addr.ga.member = ga_member; + + if ( XdrvMailbox.index > Settings.knx_GA_registered ) { + Settings.knx_GA_registered ++; + XdrvMailbox.index = Settings.knx_GA_registered; + } + + Settings.knx_GA_addr[XdrvMailbox.index -1] = KNX_addr.value; + Settings.knx_GA_param[XdrvMailbox.index -1] = ga_option; + } else { + if ( (XdrvMailbox.payload <= Settings.knx_GA_registered) && (XdrvMailbox.payload > 0) ) { + XdrvMailbox.index = XdrvMailbox.payload; + } else { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + } + if ( XdrvMailbox.index <= Settings.knx_GA_registered ) { + KNX_addr.value = Settings.knx_GA_addr[XdrvMailbox.index -1]; + Response_P (PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), + XdrvMailbox.command, XdrvMailbox.index, device_param_ga[Settings.knx_GA_param[XdrvMailbox.index-1]-1], + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); + } + } else { + ResponseCmndNumber (Settings.knx_GA_registered ); + } + } +} + +void CmndKnxCb(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_CB)) { + if (XdrvMailbox.data_len) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + char sub_string[XdrvMailbox.data_len]; + + int cb_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); + int cb_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + int cb_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + int cb_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); + + if ( ((cb_area == 0) && (cb_line == 0) && (cb_member == 0)) + || (cb_area > 31) || (cb_line > 7) || (cb_member > 255) + || (cb_option < 0) || ((cb_option > KNX_MAX_device_param ) && (cb_option != KNX_Empty)) + || (!device_param[cb_option-1].show) ) { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + + KNX_addr.ga.area = cb_area; + KNX_addr.ga.line = cb_line; + KNX_addr.ga.member = cb_member; + + if ( XdrvMailbox.index > Settings.knx_CB_registered ) { + Settings.knx_CB_registered ++; + XdrvMailbox.index = Settings.knx_CB_registered; + } + + Settings.knx_CB_addr[XdrvMailbox.index -1] = KNX_addr.value; + Settings.knx_CB_param[XdrvMailbox.index -1] = cb_option; + } else { + if ( (XdrvMailbox.payload <= Settings.knx_CB_registered) && (XdrvMailbox.payload > 0) ) { + XdrvMailbox.index = XdrvMailbox.payload; + } else { + Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); + return; + } + } + if ( XdrvMailbox.index <= Settings.knx_CB_registered ) { + KNX_addr.value = Settings.knx_CB_addr[XdrvMailbox.index -1]; + Response_P (PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), + XdrvMailbox.command, XdrvMailbox.index, device_param_cb[Settings.knx_CB_param[XdrvMailbox.index-1]-1], + KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); + } + } else { + ResponseCmndNumber (Settings.knx_CB_registered ); + } + } +} + + + + + +bool Xdrv11(uint8_t function) +{ + bool result = false; + switch (function) { + case FUNC_LOOP: + if (!global_state.wifi_down) { knx.loop(); } + break; + case FUNC_EVERY_50_MSECOND: + if (toggle_inhibit) { + toggle_inhibit--; + } + break; + case FUNC_ANY_KEY: + KnxSendButtonPower(); + break; +#ifdef USE_WEBSERVER +#ifdef USE_KNX_WEB_MENU + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_KNX); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer->on("/kn", HandleKNXConfiguration); + break; +#endif +#endif + case FUNC_COMMAND: + result = DecodeCommand(kKnxCommands, KnxCommand); + break; + case FUNC_PRE_INIT: + KNX_INIT(); + break; + + + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_12_home_assistant.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_12_home_assistant.ino" +#ifdef USE_HOME_ASSISTANT + +#define XDRV_12 12 + + +const char kHAssJsonSensorTypes[] PROGMEM = + D_JSON_TEMPERATURE "|" D_JSON_PRESSURE "|" D_JSON_PRESSUREATSEALEVEL "|" + D_JSON_APPARENT_POWERUSAGE "|Battery|" D_JSON_CURRENT "|" D_JSON_DISTANCE "|" D_JSON_FREQUENCY "|" D_JSON_HUMIDITY "|" D_JSON_ILLUMINANCE "|" + D_JSON_MOISTURE "|PB0.3|PB0.5|PB1|PB2.5|PB5|PB10|PM1|PM2.5|PM10|" D_JSON_POWERFACTOR "|" D_JSON_POWERUSAGE "|" + D_JSON_REACTIVE_POWERUSAGE "|" D_JSON_TODAY "|" D_JSON_TOTAL "|" D_JSON_VOLTAGE "|" D_JSON_WEIGHT "|" D_JSON_YESTERDAY; +const char kHAssJsonSensorUnits[] PROGMEM = + "|||" + "W|%|A|Cm|Hz|%|LX|" + "%|ppd|ppd|ppd|ppd|ppd|ppd|µg/m³|µg/m³|µg/m³||W|" + "W|KWh|KWh|V|Kg|KWh"; +const char kHAssJsonSensorDevCla[] PROGMEM = + "dev_cla\":\"temperature|dev_cla\":\"pressure|dev_cla\":\"pressure|" + "dev_cla\":\"power|dev_cla\":\"battery|ic\":\"mdi:alpha-a-circle-outline|ic\":\"mdi:leak|ic\":\"mdi:current-ac|dev_cla\":\"humidity|dev_cla\":\"illuminance|" + "ic\":\"mdi:cup-water|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|" + "ic\":\"mdi:air-filter|ic\":\"mdi:air-filter|ic\":\"mdi:air-filter|ic\":\"mdi:alpha-f-circle-outline|dev_cla\":\"power|" + "dev_cla\":\"power|dev_cla\":\"power|dev_cla\":\"power|ic\":\"mdi:alpha-v-circle-outline|ic\":\"mdi:scale|dev_cla\":\"power"; + + +const char HASS_DISCOVER_SENSOR[] PROGMEM = + ",\"unit_of_meas\":\"%s\",\"%s\"," + "\"frc_upd\":true," + "\"val_tpl\":\"{{value_json['%s']['%s']"; + +const char HASS_DISCOVER_BASE[] PROGMEM = + "{\"name\":\"%s\"," + "\"stat_t\":\"%s\"," + "\"avty_t\":\"%s\"," + "\"pl_avail\":\"" D_ONLINE "\"," + "\"pl_not_avail\":\"" D_OFFLINE "\""; + +const char HASS_DISCOVER_RELAY[] PROGMEM = + ",\"cmd_t\":\"%s\"," + "\"val_tpl\":\"{{value_json.%s}}\"," + "\"pl_off\":\"%s\"," + "\"pl_on\":\"%s\""; + +const char HASS_DISCOVER_BUTTON_TOGGLE[] PROGMEM = + ",\"value_template\":\"{%%if is_state(entity_id,\\\"off\\\")-%%}ON{%%-endif%%}\"," + "\"off_delay\":1"; + +const char HASS_DISCOVER_BUTTON_SWITCH_ONOFF[] PROGMEM = + ",\"value_template\":\"{{value_json.%s}}\"," + "\"frc_upd\":true," + "\"pl_on\":\"%s\"," + "\"pl_off\":\"%s\""; + +const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM = + ",\"bri_cmd_t\":\"%s\"," + "\"bri_stat_t\":\"%s\"," + "\"bri_scl\":100," + "\"on_cmd_type\":\"%s\"," + "\"bri_val_tpl\":\"{{value_json." D_CMND_DIMMER "}}\""; + +const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM = + ",\"rgb_cmd_t\":\"%s2\"," + "\"rgb_stat_t\":\"%s\"," + "\"rgb_val_tpl\":\"{{value_json." D_CMND_COLOR ".split(',')[0:3]|join(',')}}\""; + +const char HASS_DISCOVER_LIGHT_WHITE[] PROGMEM = + ",\"whit_val_cmd_t\":\"%s\"," + "\"whit_val_stat_t\":\"%s\"," + "\"white_value_scale\":100," + "\"whit_val_tpl\":\"{{value_json.Channel[3]}}\""; + +const char HASS_DISCOVER_LIGHT_CT[] PROGMEM = + ",\"clr_temp_cmd_t\":\"%s\"," + "\"clr_temp_stat_t\":\"%s\"," + "\"clr_temp_val_tpl\":\"{{value_json." D_CMND_COLORTEMPERATURE "}}\""; + +const char HASS_DISCOVER_LIGHT_SCHEME[] PROGMEM = + ",\"fx_cmd_t\":\"%s\"," + "\"fx_stat_t\":\"%s\"," + "\"fx_val_tpl\":\"{{value_json." D_CMND_SCHEME "}}\"," + "\"fx_list\":[\"0\",\"1\",\"2\",\"3\",\"4\"]"; + +const char HASS_DISCOVER_SENSOR_HASS_STATUS[] PROGMEM = + ",\"json_attributes_topic\":\"%s\"," + "\"unit_of_meas\":\" \"," + "\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\"," + "\"ic\":\"mdi:information-outline\""; + +const char HASS_DISCOVER_DEVICE_INFO[] PROGMEM = + ",\"uniq_id\":\"%s\"," + "\"device\":{\"identifiers\":[\"%06X\"]," + "\"connections\":[[\"mac\",\"%s\"]]," + "\"name\":\"%s\"," + "\"model\":\"%s\"," + "\"sw_version\":\"%s%s\"," + "\"manufacturer\":\"Tasmota\"}"; + +const char HASS_DISCOVER_DEVICE_INFO_SHORT[] PROGMEM = + ",\"uniq_id\":\"%s\"," + "\"device\":{\"identifiers\":[\"%06X\"]," + "\"connections\":[[\"mac\",\"%s\"]]}"; + +uint8_t hass_init_step = 0; +uint8_t hass_mode = 0; +int hass_tele_period = 0; + +void TryResponseAppend_P(const char *format, ...) +{ + va_list args; + va_start(args, format); + char dummy[2]; + int dlen = vsnprintf_P(dummy, 1, format, args); + + int mlen = strlen(mqtt_data); + int slen = sizeof(mqtt_data) - 1 - mlen; + if (dlen >= slen) + { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: MQTT discovery failed due to too long topic or friendly name. " + "Please shorten topic and friendly name. Failed to format(%u/%u):"), + dlen, slen); + va_start(args, format); + vsnprintf_P(log_data, sizeof(log_data), format, args); + AddLog(LOG_LEVEL_ERROR); + } + else + { + va_start(args, format); + vsnprintf_P(mqtt_data + mlen, slen, format, args); + } + va_end(args); +} + +void HAssAnnounceRelayLight(void) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char stemp3[TOPSZ]; + char unique_id[30]; + bool is_light = false; + bool is_topic_light = false; + + for (uint32_t i = 1; i <= MAX_RELAYS; i++) + { + is_light = ((i == devices_present) && (light_type)); + is_topic_light = Settings.flag.hass_light || is_light; + + mqtt_data[0] = '\0'; + + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP.getChipId(), (is_topic_light) ? "RL" : "LI", i); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), + (is_topic_light) ? "switch" : "light", unique_id); + MqttPublish(stopic, true); + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP.getChipId(), (is_topic_light) ? "LI" : "RL", i); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), + (is_topic_light) ? "light" : "switch", unique_id); + + if (Settings.flag.hass_discovery && (i <= devices_present)) + { + char name[33 + 2]; + char value_template[33]; + char prefix[TOPSZ]; + char *command_topic = stemp1; + char *state_topic = stemp2; + char *availability_topic = stemp3; + + if (i > MAX_FRIENDLYNAMES) + { + snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i); + } + else + { + snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 + i - 1)); + } + GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); + GetTopic_P(command_topic, CMND, mqtt_topic, value_template); + GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2)); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); + +#ifdef USE_LIGHT + if (is_light) + { + char *brightness_command_topic = stemp1; + + GetTopic_P(brightness_command_topic, CMND, mqtt_topic, D_CMND_DIMMER); + strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_DIMMER, brightness_command_topic, state_topic, stemp3); + + if (Light.subtype >= LST_RGB) + { + char *rgb_command_topic = stemp1; + + GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_COLOR, rgb_command_topic, state_topic); + + char *effect_command_topic = stemp1; + GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_SCHEME, effect_command_topic, state_topic); + } + if (LST_RGBW == Light.subtype) + { + char *white_temp_command_topic = stemp1; + + GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_WHITE, white_temp_command_topic, state_topic); + } + if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) + { + char *color_temp_command_topic = stemp1; + + GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE); + TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic); + } + } +#endif + TryResponseAppend_P(PSTR("}")); + } + MqttPublish(stopic, true); + } +} + +void HAssAnnounceButtonSwitch(uint8_t device, char *topic, uint8_t present, uint8_t key, uint8_t toggle) +{ + + + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + + mqtt_data[0] = '\0'; + + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP.getChipId(), key ? "SW" : "BTN", device + 1); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/binary_sensor/%s/config"), unique_id); + + if (Settings.flag.hass_discovery && present) + { + char name[33 + 6]; + char value_template[33]; + char prefix[TOPSZ]; + char *state_topic = stemp1; + char *availability_topic = stemp2; + char jsoname[8]; + + snprintf_P(name, sizeof(name), PSTR("%s %s%d"), SettingsText(SET_FRIENDLYNAME1), key ? "Switch" : "Button", device + 1); + snprintf_P(jsoname, sizeof(jsoname), PSTR("%s%d"), key ? "SWITCH" : "BUTTON", device + 1); + GetPowerDevice(value_template, device + 1, sizeof(value_template), + key + Settings.flag.device_index_enable); + GetTopic_P(state_topic, STAT, mqtt_topic, (PSTR("/'%s'"), jsoname)); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); + + if (toggle) { + if (!key) { + TryResponseAppend_P(HASS_DISCOVER_BUTTON_TOGGLE); + } else { + TryResponseAppend_P(",\"value_template\":\"{%%if is_state(entity_id,\\\"on\\\")-%%}OFF{%%-else-%%}ON{%%-endif%%}\""); + } + } else { + TryResponseAppend_P(HASS_DISCOVER_BUTTON_SWITCH_ONOFF, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2), SettingsText(SET_STATE_TXT1)); + } + TryResponseAppend_P(PSTR("}")); + } + MqttPublish(stopic, true); +} + +void HAssAnnounceSwitches(void) +{ + char sw_topic[TOPSZ]; + + + char *tmp = SettingsText(SET_MQTT_SWITCH_TOPIC); + Format(sw_topic, tmp, sizeof(sw_topic)); + if (!strcmp_P(sw_topic, "0") || strlen(sw_topic) == 0) + { + for (uint32_t switch_index = 0; switch_index < MAX_SWITCHES; switch_index++) + { + uint8_t switch_present = 0; + uint8_t toggle = 1; + + if (pin[GPIO_SWT1 + switch_index] < 99) + { + switch_present = 1; + } + + + if (Settings.switchmode[switch_index] == FOLLOW || Settings.switchmode[switch_index] == FOLLOW_INV || + Settings.flag3.button_switch_force_local || + !strcmp(mqtt_topic, sw_topic) || !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), sw_topic)) + { + toggle = 0; + } + HAssAnnounceButtonSwitch(switch_index, sw_topic, switch_present, 1, toggle); + } + } +} + +void HAssAnnounceButtons(void) +{ + char key_topic[TOPSZ]; + + + char *tmp = SettingsText(SET_MQTT_BUTTON_TOPIC); + Format(key_topic, tmp, sizeof(key_topic)); + if (!strcmp_P(key_topic, "0") || strlen(key_topic) == 0) + { + for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) + { + uint8_t button_present = 0; + uint8_t toggle = 1; + + if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) + { + button_present = 1; + } + else + { + if (pin[GPIO_KEY1 + button_index] < 99) + { + button_present = 1; + } + } + + + if (Settings.flag3.button_switch_force_local || + !strcmp(mqtt_topic, key_topic) || !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), key_topic)) + { + toggle = 0; + } + HAssAnnounceButtonSwitch(button_index, key_topic, button_present, 0, toggle); + } + } +} + +void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char *MultiSubName, uint8_t subqty, uint8_t subidx) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + + mqtt_data[0] = '\0'; + + char subname[20]; + NoAlNumToUnderscore(subname, MultiSubName); + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%s"), ESP.getChipId(), sensorname, subname); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id);; + + if (Settings.flag.hass_discovery) + { + char name[33 + 42]; + char prefix[TOPSZ]; + char *state_topic = stemp1; + char *availability_topic = stemp2; + + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); + GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR)); + snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_FRIENDLYNAME1), sensorname, MultiSubName); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); + + char jname[32]; + int sensor_index = GetCommandCode(jname, sizeof(jname), subsensortype, kHAssJsonSensorTypes); + if (sensor_index > -1) { + char param1[20]; + GetTextIndexed(param1, sizeof(param1), sensor_index, kHAssJsonSensorUnits); + switch (sensor_index) { + case 0: + snprintf_P(param1, sizeof(param1), PSTR("°%c"),TempUnit()); + break; + case 1: + case 2: + snprintf_P(param1, sizeof(param1), PSTR("%s"), PressureUnit().c_str()); + break; + } + char param2[50]; + GetTextIndexed(param2, sizeof(param2), sensor_index, kHAssJsonSensorDevCla); + TryResponseAppend_P(HASS_DISCOVER_SENSOR, param1, param2, sensorname, subsensortype); + if (subidx) { + TryResponseAppend_P(PSTR("[%d]"), subqty -1); + } + } else { + TryResponseAppend_P(HASS_DISCOVER_SENSOR, " ", "ic\":\"mdi:eye", sensorname, subsensortype); + } + TryResponseAppend_P(PSTR("}}\"}")); + } + MqttPublish(stopic, true); +} + +void HAssAnnounceSensors(void) +{ + uint8_t hass_xsns_index = 0; + bool is_sensor = true; + uint8_t subqty = 0; + do + { + mqtt_data[0] = '\0'; + int tele_period_save = tele_period; + tele_period = 2; + XsnsNextCall(FUNC_JSON_APPEND, hass_xsns_index); + tele_period = tele_period_save; + + char sensordata[512]; + strlcpy(sensordata, mqtt_data, sizeof(sensordata)); + + if (strlen(sensordata)) + { + sensordata[0] = '{'; + snprintf_P(sensordata, sizeof(sensordata), PSTR("%s}"), sensordata); + + + + StaticJsonBuffer<500> jsonBuffer; + JsonObject &root = jsonBuffer.parseObject(sensordata); + if (!root.success()) + { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: jsonBuffer failed to parse '%s'"), sensordata); + continue; + } + for (auto sensor : root) + { + const char *sensorname = sensor.key; + JsonObject &sensors = sensor.value.as(); + if (!sensors.success()) + { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: JsonObject failed to parse '%s'"), sensordata); + continue; + } + for (auto subsensor : sensors) + { + + if (subsensor.value.is()) { + JsonArray& subsensors = subsensor.value.as(); + subqty = subsensors.size(); + char MultiSubName[20]; + for (int i = 1; i <= subqty; i++) { + snprintf_P(MultiSubName, sizeof(MultiSubName), PSTR("%s %d"), subsensor.key, i); + HAssAnnounceSensor(sensorname, subsensor.key, MultiSubName, i, 1); + } + } else { HAssAnnounceSensor(sensorname, subsensor.key, subsensor.key, 0, 0);} + } + } + } + yield(); + } while (hass_xsns_index != 0); +} + +void HAssAnnounceStatusSensor(void) +{ + char stopic[TOPSZ]; + char stemp1[TOPSZ]; + char stemp2[TOPSZ]; + char unique_id[30]; + + + mqtt_data[0] = '\0'; + + + snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_status"), ESP.getChipId()); + snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); + + if (Settings.flag.hass_discovery) + { + char name[33 + 7]; + char prefix[TOPSZ]; + char *state_topic = stemp1; + char *availability_topic = stemp2; + + snprintf_P(name, sizeof(name), PSTR("%s status"), SettingsText(SET_FRIENDLYNAME1)); + GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE)); + GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); + + Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + TryResponseAppend_P(HASS_DISCOVER_SENSOR_HASS_STATUS, state_topic); + TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP.getChipId(), WiFi.macAddress().c_str(), + SettingsText(SET_FRIENDLYNAME1), ModuleName().c_str(), my_version, my_image); + + TryResponseAppend_P(PSTR("}")); + } + MqttPublish(stopic, true); +} + +void HAssPublishStatus(void) +{ + Response_P(PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\"," + "\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"," + "\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\"," + "\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d," + "\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_CMND_IPADDRESS "\":\"%s\"," + "\"" D_JSON_RSSI "\":\"%d\",\"LoadAvg\":%lu}"), + my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getSdkVersion(), ModuleName().c_str(), + GetResetReason().c_str(), GetUptime().c_str(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(), + Settings.bootcount, Settings.save_flag, WiFi.localIP().toString().c_str(), + WifiGetRssiAsQuality(WiFi.RSSI()), loop_load_avg); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE)); +} + +void HAssDiscovery(void) +{ + + if (Settings.flag.hass_discovery) + { + Settings.flag.mqtt_response = 0; + Settings.flag.decimal_text = 1; + Settings.flag3.hass_tele_on_power = 1; + Settings.light_scheme = 0; + } + + if (Settings.flag.hass_discovery || (1 == hass_mode)) + { + + HAssAnnounceRelayLight(); + + + HAssAnnounceButtons(); + + + HAssAnnounceSwitches(); + + + HAssAnnounceSensors(); + + + HAssAnnounceStatusSensor(); + } +} + +void HAssDiscover(void) +{ + hass_mode = 1; + hass_init_step = 1; +} + +void HAssAnyKey(void) +{ + if (!Settings.flag.hass_discovery) + { + return; + } + + uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; + uint32_t device = XdrvMailbox.payload & 0xFF; + uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; + + char scommand[CMDSZ]; + snprintf_P(scommand, sizeof(scommand), PSTR("%s%d"), (key) ? "SWITCH" : "BUTTON", device); + char stopic[TOPSZ]; + + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P(S_JSON_COMMAND_SVALUE, PSTR(D_RSLT_STATE), GetStateText(state)); + MqttPublish(stopic); +} + + + + + +bool Xdrv12(uint8_t function) +{ + bool result = false; + + if (Settings.flag.mqtt_enabled) + { + switch (function) + { + case FUNC_EVERY_SECOND: + if (hass_init_step) + { + hass_init_step--; + if (!hass_init_step) + { + HAssDiscovery(); + } + } + else if (Settings.flag.hass_discovery && Settings.tele_period) + { + hass_tele_period++; + if (hass_tele_period >= Settings.tele_period) + { + hass_tele_period = 0; + + mqtt_data[0] = '\0'; + HAssPublishStatus(); + } + } + break; + case FUNC_ANY_KEY: + HAssAnyKey(); + break; + case FUNC_MQTT_INIT: + hass_mode = 0; + hass_init_step = 2; + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_13_display.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_13_display.ino" +#if defined(USE_I2C) || defined(USE_SPI) +#ifdef USE_DISPLAY + +#define XDRV_13 13 + +#include +#include + +Renderer *renderer; + +enum ColorType { COLOR_BW, COLOR_COLOR }; + +#ifndef MAXBUTTONS +#define MAXBUTTONS 16 +#endif + +#ifdef USE_TOUCH_BUTTONS +VButton *buttons[MAXBUTTONS]; +#endif + + + +uint16_t fg_color = 1; +uint16_t bg_color = 0; +uint8_t color_type = COLOR_BW; +uint8_t auto_draw=1; + +const uint8_t DISPLAY_MAX_DRIVERS = 16; +const uint8_t DISPLAY_MAX_COLS = 44; +const uint8_t DISPLAY_MAX_ROWS = 32; + +const uint8_t DISPLAY_LOG_ROWS = 32; + +#define D_PRFX_DISPLAY "Display" +#define D_CMND_DISP_ADDRESS "Address" +#define D_CMND_DISP_COLS "Cols" +#define D_CMND_DISP_DIMMER "Dimmer" +#define D_CMND_DISP_MODE "Mode" +#define D_CMND_DISP_MODEL "Model" +#define D_CMND_DISP_REFRESH "Refresh" +#define D_CMND_DISP_ROWS "Rows" +#define D_CMND_DISP_SIZE "Size" +#define D_CMND_DISP_FONT "Font" +#define D_CMND_DISP_ROTATE "Rotate" +#define D_CMND_DISP_TEXT "Text" +#define D_CMND_DISP_WIDTH "Width" +#define D_CMND_DISP_HEIGHT "Height" + +enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_EVERY_50_MSECOND, FUNC_DISPLAY_EVERY_SECOND, + FUNC_DISPLAY_MODEL, FUNC_DISPLAY_MODE, FUNC_DISPLAY_POWER, + FUNC_DISPLAY_CLEAR, FUNC_DISPLAY_DRAW_FRAME, + FUNC_DISPLAY_DRAW_HLINE, FUNC_DISPLAY_DRAW_VLINE, FUNC_DISPLAY_DRAW_LINE, + FUNC_DISPLAY_DRAW_CIRCLE, FUNC_DISPLAY_FILL_CIRCLE, + FUNC_DISPLAY_DRAW_RECTANGLE, FUNC_DISPLAY_FILL_RECTANGLE, + FUNC_DISPLAY_TEXT_SIZE, FUNC_DISPLAY_FONT_SIZE, FUNC_DISPLAY_ROTATION, FUNC_DISPLAY_DRAW_STRING, FUNC_DISPLAY_ONOFF }; + +enum DisplayInitModes { DISPLAY_INIT_MODE, DISPLAY_INIT_PARTIAL, DISPLAY_INIT_FULL }; + +const char kDisplayCommands[] PROGMEM = D_PRFX_DISPLAY "|" + "|" D_CMND_DISP_MODEL "|" D_CMND_DISP_WIDTH "|" D_CMND_DISP_HEIGHT "|" D_CMND_DISP_MODE "|" D_CMND_DISP_REFRESH "|" + D_CMND_DISP_DIMMER "|" D_CMND_DISP_COLS "|" D_CMND_DISP_ROWS "|" D_CMND_DISP_SIZE "|" D_CMND_DISP_FONT "|" + D_CMND_DISP_ROTATE "|" D_CMND_DISP_TEXT "|" D_CMND_DISP_ADDRESS ; + +void (* const DisplayCommand[])(void) PROGMEM = { + &CmndDisplay, &CmndDisplayModel, &CmndDisplayWidth, &CmndDisplayHeight, &CmndDisplayMode, &CmndDisplayRefresh, + &CmndDisplayDimmer, &CmndDisplayColumns, &CmndDisplayRows, &CmndDisplaySize, &CmndDisplayFont, + &CmndDisplayRotate, &CmndDisplayText, &CmndDisplayAddress }; + +char *dsp_str; + +uint16_t dsp_x; +uint16_t dsp_y; +uint16_t dsp_x2; +uint16_t dsp_y2; +uint16_t dsp_rad; +uint16_t dsp_color; +int16_t dsp_len; +int16_t disp_xpos = 0; +int16_t disp_ypos = 0; + +uint8_t disp_power = 0; +uint8_t disp_device = 0; +uint8_t disp_refresh = 1; +uint8_t disp_autodraw = 1; +uint8_t dsp_init; +uint8_t dsp_font; +uint8_t dsp_flag; +uint8_t dsp_on; + +#ifdef USE_DISPLAY_MODES1TO5 + +char **disp_log_buffer; +char **disp_screen_buffer; +char disp_temp[2]; +char disp_pres[5]; + +uint8_t disp_log_buffer_cols = 0; +uint8_t disp_log_buffer_idx = 0; +uint8_t disp_log_buffer_ptr = 0; +uint8_t disp_screen_buffer_cols = 0; +uint8_t disp_screen_buffer_rows = 0; +bool disp_subscribed = false; + +#endif + + + +void DisplayInit(uint8_t mode) +{ + if (renderer) { + renderer->DisplayInit(mode, Settings.display_size, Settings.display_rotate, Settings.display_font); + } + else { + dsp_init = mode; + XdspCall(FUNC_DISPLAY_INIT); + } +} + +void DisplayClear(void) +{ + XdspCall(FUNC_DISPLAY_CLEAR); +} + +void DisplayDrawHLine(uint16_t x, uint16_t y, int16_t len, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_len = len; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_HLINE); +} + +void DisplayDrawVLine(uint16_t x, uint16_t y, int16_t len, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_len = len; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_VLINE); +} + +void DisplayDrawLine(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_x2 = x2; + dsp_y2 = y2; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_LINE); +} + +void DisplayDrawCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_rad = rad; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_CIRCLE); +} + +void DisplayDrawFilledCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_rad = rad; + dsp_color = color; + XdspCall(FUNC_DISPLAY_FILL_CIRCLE); +} + +void DisplayDrawRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_x2 = x2; + dsp_y2 = y2; + dsp_color = color; + XdspCall(FUNC_DISPLAY_DRAW_RECTANGLE); +} + +void DisplayDrawFilledRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) +{ + dsp_x = x; + dsp_y = y; + dsp_x2 = x2; + dsp_y2 = y2; + dsp_color = color; + XdspCall(FUNC_DISPLAY_FILL_RECTANGLE); +} + +void DisplayDrawFrame(void) +{ + XdspCall(FUNC_DISPLAY_DRAW_FRAME); +} + +void DisplaySetSize(uint8_t size) +{ + Settings.display_size = size &3; + XdspCall(FUNC_DISPLAY_TEXT_SIZE); +} + +void DisplaySetFont(uint8_t font) +{ + Settings.display_font = font &3; + XdspCall(FUNC_DISPLAY_FONT_SIZE); +} + +void DisplaySetRotation(uint8_t rotation) +{ + Settings.display_rotate = rotation &3; + XdspCall(FUNC_DISPLAY_ROTATION); +} + +void DisplayDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) +{ + dsp_x = x; + dsp_y = y; + dsp_str = str; + dsp_color = color; + dsp_flag = flag; + XdspCall(FUNC_DISPLAY_DRAW_STRING); +} + +void DisplayOnOff(uint8_t on) +{ + dsp_on = on; + XdspCall(FUNC_DISPLAY_ONOFF); +} + + + + +uint8_t fatoiv(char *cp,float *res) { + uint8_t index=0; + *res=CharToFloat(cp); + while (*cp) { + if ((*cp>='0' && *cp<='9') || (*cp=='-') || (*cp=='.')) { + cp++; + index++; + } else { + break; + } + } + return index; +} + + +uint8_t atoiv(char *cp, int16_t *res) +{ + uint8_t index = 0; + *res = atoi(cp); + while (*cp) { + if ((*cp>='0' && *cp<='9') || (*cp=='-')) { + cp++; + index++; + } else { + break; + } + } + return index; +} + + +uint8_t atoiV(char *cp, uint16_t *res) +{ + uint8_t index = 0; + *res = atoi(cp); + while (*cp) { + if (*cp>='0' && *cp<='9') { + cp++; + index++; + } else { + break; + } + } + return index; +} + + +void alignright(char *string) { + uint16_t slen=strlen(string); + uint16_t len=slen; + while (len) { + + if (string[len-1]!=' ') { + break; + } + len--; + } + uint16_t diff=slen-len; + if (diff>0) { + + memmove(&string[diff],string,len); + memset(string,' ',diff); + } +} + +char *get_string(char *buff,uint8_t len,char *cp) { +uint8_t index=0; + while (*cp!=':') { + buff[index]=*cp++; + index++; + if (index>=len) break; + } + buff[index]=0; + cp++; + return cp; +} + +#define ESCAPE_CHAR '~' + + +uint32_t decode_te(char *line) { + uint32_t skip = 0; + char sbuf[3],*cp; + while (*line) { + if (*line==ESCAPE_CHAR) { + cp=line+1; + if (*cp!=0 && *cp==ESCAPE_CHAR) { + + memmove(cp,cp+1,strlen(cp)); + skip++; + } else { + + if (strlen(cp)<2) { + + return skip; + } + + sbuf[0]=*(cp); + sbuf[1]=*(cp+1); + sbuf[2]=0; + *line=strtol(sbuf,0,16); + + memmove(cp,cp+2,strlen(cp)-1); + skip += 2; + } + } + line++; + } + return skip; +} + + + +#define DISPLAY_BUFFER_COLS 128 + +void DisplayText(void) +{ + uint8_t lpos; + uint8_t escape = 0; + uint8_t var; + int16_t lin = 0; + int16_t col = 0; + int16_t fill = 0; + int16_t temp; + int16_t temp1; + float ftemp; + + char linebuf[DISPLAY_BUFFER_COLS]; + char *dp = linebuf; + char *cp = XdrvMailbox.data; + + memset(linebuf, ' ', sizeof(linebuf)); + linebuf[sizeof(linebuf)-1] = 0; + *dp = 0; + + while (*cp) { + if (!escape) { + + if (*cp == '[') { + escape = 1; + cp++; + + if ((uint32_t)dp - (uint32_t)linebuf) { + if (!fill) { *dp = 0; } + if (col > 0 && lin > 0) { + + if (!renderer) DisplayDrawStringAt(col, lin, linebuf, fg_color, 1); + else renderer->DrawStringAt(col, lin, linebuf, fg_color, 1); + } else { + + if (!renderer) DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + else renderer->DrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + } + memset(linebuf, ' ', sizeof(linebuf)); + linebuf[sizeof(linebuf)-1] = 0; + dp = linebuf; + } + } else { + + if (dp < (linebuf + DISPLAY_BUFFER_COLS)) { *dp++ = *cp++; } + } + } else { + + if (*cp == ']') { + escape = 0; + cp++; + } else { + + switch (*cp++) { + case 'z': + + if (!renderer) DisplayClear(); + else renderer->fillScreen(bg_color); + disp_xpos = 0; + disp_ypos = 0; + col = 0; + lin = 0; + break; + case 'i': + + DisplayInit(DISPLAY_INIT_PARTIAL); + break; + case 'I': + + DisplayInit(DISPLAY_INIT_FULL); + break; + case 'o': + if (!renderer) { + DisplayOnOff(0); + } else { + renderer->DisplayOnff(0); + } + break; + case 'O': + if (!renderer) { + DisplayOnOff(1); + } else { + renderer->DisplayOnff(1); + } + break; + case 'x': + + var = atoiv(cp, &disp_xpos); + cp += var; + break; + case 'y': + + var = atoiv(cp, &disp_ypos); + cp += var; + break; + case 'l': + + var = atoiv(cp, &lin); + cp += var; + + break; + case 'c': + + var = atoiv(cp, &col); + cp += var; + + break; + case 'C': + + if (*cp=='i') { + + cp++; + var = atoiv(cp, &temp); + if (renderer) ftemp=renderer->GetColorFromIndex(temp); + } else { + + var = fatoiv(cp,&ftemp); + } + fg_color=ftemp; + cp += var; + if (renderer) renderer->setTextColor(fg_color,bg_color); + break; + case 'B': + + if (*cp=='i') { + + cp++; + var = atoiv(cp, &temp); + if (renderer) ftemp=renderer->GetColorFromIndex(temp); + } else { + var = fatoiv(cp,&ftemp); + } + bg_color=ftemp; + cp += var; + if (renderer) renderer->setTextColor(fg_color,bg_color); + break; + case 'p': + + var = atoiv(cp, &fill); + cp += var; + linebuf[fill] = 0; + break; +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) + case 'P': + { char *ep=strchr(cp,':'); + if (ep) { + *ep=0; + ep++; + Draw_RGB_Bitmap(cp,disp_xpos,disp_ypos); + cp=ep; + } + } + break; +#endif + case 'h': + + var = atoiv(cp, &temp); + cp += var; + if (temp < 0) { + if (renderer) renderer->writeFastHLine(disp_xpos + temp, disp_ypos, -temp, fg_color); + else DisplayDrawHLine(disp_xpos + temp, disp_ypos, -temp, fg_color); + } else { + if (renderer) renderer->writeFastHLine(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawHLine(disp_xpos, disp_ypos, temp, fg_color); + } + disp_xpos += temp; + break; + case 'v': + + var = atoiv(cp, &temp); + cp += var; + if (temp < 0) { + if (renderer) renderer->writeFastVLine(disp_xpos, disp_ypos + temp, -temp, fg_color); + else DisplayDrawVLine(disp_xpos, disp_ypos + temp, -temp, fg_color); + } else { + if (renderer) renderer->writeFastVLine(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawVLine(disp_xpos, disp_ypos, temp, fg_color); + } + disp_ypos += temp; + break; + case 'L': + + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + if (renderer) renderer->writeLine(disp_xpos, disp_ypos, temp, temp1, fg_color); + else DisplayDrawLine(disp_xpos, disp_ypos, temp, temp1, fg_color); + disp_xpos += temp; + disp_ypos += temp1; + break; + case 'k': + + var = atoiv(cp, &temp); + cp += var; + if (renderer) renderer->drawCircle(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawCircle(disp_xpos, disp_ypos, temp, fg_color); + break; + case 'K': + + var = atoiv(cp, &temp); + cp += var; + if (renderer) renderer->fillCircle(disp_xpos, disp_ypos, temp, fg_color); + else DisplayDrawFilledCircle(disp_xpos, disp_ypos, temp, fg_color); + break; + case 'r': + + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + if (renderer) renderer->drawRect(disp_xpos, disp_ypos, temp, temp1, fg_color); + else DisplayDrawRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); + break; + case 'R': + + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + if (renderer) renderer->fillRect(disp_xpos, disp_ypos, temp, temp1, fg_color); + else DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); + break; + case 'u': + + { int16_t rad; + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + cp++; + var = atoiv(cp, &rad); + cp += var; + if (renderer) renderer->drawRoundRect(disp_xpos, disp_ypos, temp, temp1, rad, fg_color); + + } + break; + case 'U': + + { int16_t rad; + var = atoiv(cp, &temp); + cp += var; + cp++; + var = atoiv(cp, &temp1); + cp += var; + cp++; + var = atoiv(cp, &rad); + cp += var; + if (renderer) renderer->fillRoundRect(disp_xpos, disp_ypos, temp, temp1, rad, fg_color); + + } + break; + + case 't': + if (*cp=='S') { + cp++; + if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) { + snprintf_P(dp, 9, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + dp += 8; + } + } else { + if (dp < (linebuf + DISPLAY_BUFFER_COLS) -5) { + snprintf_P(dp, 6, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute); + dp += 5; + } + } + break; + case 'T': + if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) { + snprintf_P(dp, 9, PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year%2000); + dp += 8; + } + break; + case 'd': + + if (renderer) renderer->Updateframe(); + else DisplayDrawFrame(); + break; + case 'D': + + auto_draw=*cp&3; + if (renderer) renderer->setDrawMode(auto_draw>>1); + cp += 1; + break; + case 's': + + if (renderer) renderer->setTextSize(*cp&7); + else DisplaySetSize(*cp&3); + cp += 1; + break; + case 'f': + + if (renderer) renderer->setTextFont(*cp&7); + else DisplaySetFont(*cp&7); + cp += 1; + break; + case 'a': + + if (renderer) renderer->setRotation(*cp&3); + else DisplaySetRotation(*cp&3); + cp+=1; + break; + +#ifdef USE_GRAPH + case 'G': + + if (*cp=='d') { + cp++; + var=atoiv(cp,&temp); + cp+=var; + cp++; + var=atoiv(cp,&temp1); + cp+=var; + RedrawGraph(temp,temp1); + break; + } +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) + if (*cp=='s') { + cp++; + var=atoiv(cp,&temp); + cp+=var; + cp++; + + char bbuff[128]; + cp=get_string(bbuff,sizeof(bbuff),cp); + Save_graph(temp,bbuff); + break; + } + if (*cp=='r') { + cp++; + var=atoiv(cp,&temp); + cp+=var; + cp++; + + char bbuff[128]; + cp=get_string(bbuff,sizeof(bbuff),cp); + Restore_graph(temp,bbuff); + break; + } +#endif + { int16_t num,gxp,gyp,gxs,gys,dec,icol; + float ymin,ymax; + var=atoiv(cp,&num); + cp+=var; + cp++; + var=atoiv(cp,&gxp); + cp+=var; + cp++; + var=atoiv(cp,&gyp); + cp+=var; + cp++; + var=atoiv(cp,&gxs); + cp+=var; + cp++; + var=atoiv(cp,&gys); + cp+=var; + cp++; + var=atoiv(cp,&dec); + cp+=var; + cp++; + var=fatoiv(cp,&ymin); + cp+=var; + cp++; + var=fatoiv(cp,&ymax); + cp+=var; + if (color_type==COLOR_COLOR) { + + cp++; + var=atoiv(cp,&icol); + cp+=var; + } else { + icol=0; + } + DefineGraph(num,gxp,gyp,gxs,gys,dec,ymin,ymax,icol); + } + break; + case 'g': + { float temp; + int16_t num; + var=atoiv(cp,&num); + cp+=var; + cp++; + var=fatoiv(cp,&temp); + cp+=var; + AddValue(num,temp); + } + break; +#endif + +#ifdef USE_AWATCH + case 'w': + var = atoiv(cp, &temp); + cp += var; + DrawAClock(temp); + break; +#endif + +#ifdef USE_TOUCH_BUTTONS + case 'b': + { int16_t num,gxp,gyp,gxs,gys,outline,fill,textcolor,textsize; + var=atoiv(cp,&num); + cp+=var; + cp++; + uint8_t bflags=num>>8; + num=num%MAXBUTTONS; + var=atoiv(cp,&gxp); + cp+=var; + cp++; + var=atoiv(cp,&gyp); + cp+=var; + cp++; + var=atoiv(cp,&gxs); + cp+=var; + cp++; + var=atoiv(cp,&gys); + cp+=var; + cp++; + var=atoiv(cp,&outline); + cp+=var; + cp++; + var=atoiv(cp,&fill); + cp+=var; + cp++; + var=atoiv(cp,&textcolor); + cp+=var; + cp++; + var=atoiv(cp,&textsize); + cp+=var; + cp++; + + char bbuff[32]; + cp=get_string(bbuff,sizeof(bbuff),cp); + + if (buttons[num]) { + delete buttons[num]; + } + if (renderer) { + buttons[num]= new VButton(); + if (buttons[num]) { + buttons[num]->vpower=bflags; + buttons[num]->initButtonUL(renderer,gxp,gyp,gxs,gys,renderer->GetColorFromIndex(outline),\ + renderer->GetColorFromIndex(fill),renderer->GetColorFromIndex(textcolor),bbuff,textsize); + if (!bflags) { + + buttons[num]->xdrawButton(bitRead(power,num)); + } else { + + buttons[num]->vpower&=0x7f; + buttons[num]->xdrawButton(buttons[num]->vpower&0x80); + } + } + } + } + break; +#endif + default: + + Response_P(PSTR("Unknown Escape")); + goto exit; + break; + } + } + } + } + exit: + + dp -= decode_te(linebuf); + if ((uint32_t)dp - (uint32_t)linebuf) { + if (!fill) { + *dp = 0; + } else { + linebuf[abs(fill)] = 0; + } + if (fill<0) { + + alignright(linebuf); + } + if (col > 0 && lin > 0) { + + if (!renderer) DisplayDrawStringAt(col, lin, linebuf, fg_color, 1); + else renderer->DrawStringAt(col, lin, linebuf, fg_color, 1); + } else { + + if (!renderer) DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + else renderer->DrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); + } + } + + if (auto_draw&1) { + if (renderer) renderer->Updateframe(); + else DisplayDrawFrame(); + } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void DisplayClearScreenBuffer(void) +{ + if (disp_screen_buffer_cols) { + for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { + memset(disp_screen_buffer[i], 0, disp_screen_buffer_cols); + } + } +} + +void DisplayFreeScreenBuffer(void) +{ + if (disp_screen_buffer != nullptr) { + for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { + if (disp_screen_buffer[i] != nullptr) { free(disp_screen_buffer[i]); } + } + free(disp_screen_buffer); + disp_screen_buffer_cols = 0; + disp_screen_buffer_rows = 0; + } +} + +void DisplayAllocScreenBuffer(void) +{ + if (!disp_screen_buffer_cols) { + disp_screen_buffer_rows = Settings.display_rows; + disp_screen_buffer = (char**)malloc(sizeof(*disp_screen_buffer) * disp_screen_buffer_rows); + if (disp_screen_buffer != nullptr) { + for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { + disp_screen_buffer[i] = (char*)malloc(sizeof(*disp_screen_buffer[i]) * (Settings.display_cols[0] +1)); + if (disp_screen_buffer[i] == nullptr) { + DisplayFreeScreenBuffer(); + break; + } + } + } + if (disp_screen_buffer != nullptr) { + disp_screen_buffer_cols = Settings.display_cols[0] +1; + DisplayClearScreenBuffer(); + } + } +} + +void DisplayReAllocScreenBuffer(void) +{ + DisplayFreeScreenBuffer(); + DisplayAllocScreenBuffer(); +} + +void DisplayFillScreen(uint32_t line) +{ + uint32_t len = disp_screen_buffer_cols - strlen(disp_screen_buffer[line]); + if (len) { + memset(disp_screen_buffer[line] + strlen(disp_screen_buffer[line]), 0x20, len); + disp_screen_buffer[line][disp_screen_buffer_cols -1] = 0; + } +} + + + +void DisplayClearLogBuffer(void) +{ + if (disp_log_buffer_cols) { + for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { + memset(disp_log_buffer[i], 0, disp_log_buffer_cols); + } + } +} + +void DisplayFreeLogBuffer(void) +{ + if (disp_log_buffer != nullptr) { + for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { + if (disp_log_buffer[i] != nullptr) { free(disp_log_buffer[i]); } + } + free(disp_log_buffer); + disp_log_buffer_cols = 0; + } +} + +void DisplayAllocLogBuffer(void) +{ + if (!disp_log_buffer_cols) { + disp_log_buffer = (char**)malloc(sizeof(*disp_log_buffer) * DISPLAY_LOG_ROWS); + if (disp_log_buffer != nullptr) { + for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { + disp_log_buffer[i] = (char*)malloc(sizeof(*disp_log_buffer[i]) * (Settings.display_cols[0] +1)); + if (disp_log_buffer[i] == nullptr) { + DisplayFreeLogBuffer(); + break; + } + } + } + if (disp_log_buffer != nullptr) { + disp_log_buffer_cols = Settings.display_cols[0] +1; + DisplayClearLogBuffer(); + } + } +} + +void DisplayReAllocLogBuffer(void) +{ + DisplayFreeLogBuffer(); + DisplayAllocLogBuffer(); +} + +void DisplayLogBufferAdd(char* txt) +{ + if (disp_log_buffer_cols) { + strlcpy(disp_log_buffer[disp_log_buffer_idx], txt, disp_log_buffer_cols); + disp_log_buffer_idx++; + if (DISPLAY_LOG_ROWS == disp_log_buffer_idx) { disp_log_buffer_idx = 0; } + } +} + +char* DisplayLogBuffer(char temp_code) +{ + char* result = nullptr; + if (disp_log_buffer_cols) { + if (disp_log_buffer_idx != disp_log_buffer_ptr) { + result = disp_log_buffer[disp_log_buffer_ptr]; + disp_log_buffer_ptr++; + if (DISPLAY_LOG_ROWS == disp_log_buffer_ptr) { disp_log_buffer_ptr = 0; } + + char *pch = strchr(result, '~'); + if (pch != nullptr) { result[pch - result] = temp_code; } + } + } + return result; +} + +void DisplayLogBufferInit(void) +{ + if (Settings.display_mode) { + disp_log_buffer_idx = 0; + disp_log_buffer_ptr = 0; + disp_refresh = Settings.display_refresh; + + snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%c"), TempUnit()); + snprintf_P(disp_pres, sizeof(disp_pres), PressureUnit().c_str()); + + DisplayReAllocLogBuffer(); + + char buffer[40]; + snprintf_P(buffer, sizeof(buffer), PSTR(D_VERSION " %s%s"), my_version, my_image); + DisplayLogBufferAdd(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR("Display mode %d"), Settings.display_mode); + DisplayLogBufferAdd(buffer); + + snprintf_P(buffer, sizeof(buffer), PSTR(D_CMND_HOSTNAME " %s"), my_hostname); + DisplayLogBufferAdd(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_SSID " %s"), SettingsText(SET_STASSID1 + Settings.sta_active)); + DisplayLogBufferAdd(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_MAC " %s"), WiFi.macAddress().c_str()); + DisplayLogBufferAdd(buffer); + if (!global_state.wifi_down) { + snprintf_P(buffer, sizeof(buffer), PSTR("IP %s"), WiFi.localIP().toString().c_str()); + DisplayLogBufferAdd(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_RSSI " %d%%"), WifiGetRssiAsQuality(WiFi.RSSI())); + DisplayLogBufferAdd(buffer); + } + } +} + + + + + +enum SensorQuantity { + JSON_TEMPERATURE, + JSON_HUMIDITY, JSON_LIGHT, JSON_NOISE, JSON_AIRQUALITY, + JSON_PRESSURE, JSON_PRESSUREATSEALEVEL, + JSON_ILLUMINANCE, + JSON_GAS, + JSON_YESTERDAY, JSON_TOTAL, JSON_TODAY, + JSON_PERIOD, + JSON_POWERFACTOR, JSON_COUNTER, JSON_ANALOG_INPUT, JSON_UV_LEVEL, + JSON_CURRENT, + JSON_VOLTAGE, + JSON_POWERUSAGE, + JSON_CO2, + JSON_FREQUENCY }; +const char kSensorQuantity[] PROGMEM = + D_JSON_TEMPERATURE "|" + D_JSON_HUMIDITY "|" D_JSON_LIGHT "|" D_JSON_NOISE "|" D_JSON_AIRQUALITY "|" + D_JSON_PRESSURE "|" D_JSON_PRESSUREATSEALEVEL "|" + D_JSON_ILLUMINANCE "|" + D_JSON_GAS "|" + D_JSON_YESTERDAY "|" D_JSON_TOTAL "|" D_JSON_TODAY "|" + D_JSON_PERIOD "|" + D_JSON_POWERFACTOR "|" D_JSON_COUNTER "|" D_JSON_ANALOG_INPUT "|" D_JSON_UV_LEVEL "|" + D_JSON_CURRENT "|" + D_JSON_VOLTAGE "|" + D_JSON_POWERUSAGE "|" + D_JSON_CO2 "|" + D_JSON_FREQUENCY ; + +void DisplayJsonValue(const char* topic, const char* device, const char* mkey, const char* value) +{ + char quantity[TOPSZ]; + char buffer[Settings.display_cols[0] +1]; + char spaces[Settings.display_cols[0]]; + char source[Settings.display_cols[0] - Settings.display_cols[1]]; + char svalue[Settings.display_cols[1] +1]; + +#ifdef USE_DEBUG_DRIVER + ShowFreeMem(PSTR("DisplayJsonValue")); +#endif + + memset(spaces, 0x20, sizeof(spaces)); + spaces[sizeof(spaces) -1] = '\0'; + snprintf_P(source, sizeof(source), PSTR("%s%s%s%s"), topic, (strlen(topic))?"/":"", mkey, spaces); + + int quantity_code = GetCommandCode(quantity, sizeof(quantity), mkey, kSensorQuantity); + if ((-1 == quantity_code) || !strcmp_P(mkey, S_RSLT_POWER)) { + return; + } + if (JSON_TEMPERATURE == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s~%s"), value, disp_temp); + } + else if ((quantity_code >= JSON_HUMIDITY) && (quantity_code <= JSON_AIRQUALITY)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%%"), value); + } + else if ((quantity_code >= JSON_PRESSURE) && (quantity_code <= JSON_PRESSUREATSEALEVEL)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s%s"), value, disp_pres); + } + else if (JSON_ILLUMINANCE == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_LUX), value); + } + else if (JSON_GAS == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_KILOOHM), value); + } + else if ((quantity_code >= JSON_YESTERDAY) && (quantity_code <= JSON_TODAY)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_KILOWATTHOUR), value); + } + else if (JSON_PERIOD == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_WATTHOUR), value); + } + else if ((quantity_code >= JSON_POWERFACTOR) && (quantity_code <= JSON_UV_LEVEL)) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s"), value); + } + else if (JSON_CURRENT == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_AMPERE), value); + } + else if (JSON_VOLTAGE == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_VOLT), value); + } + else if (JSON_POWERUSAGE == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_WATT), value); + } + else if (JSON_CO2 == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_PARTS_PER_MILLION), value); + } + else if (JSON_FREQUENCY == quantity_code) { + snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_HERTZ), value); + } + snprintf_P(buffer, sizeof(buffer), PSTR("%s %s"), source, svalue); + + + + DisplayLogBufferAdd(buffer); +} + +void DisplayAnalyzeJson(char *topic, char *json) +{ +# 1145 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_13_display.ino" + String jsonStr = json; + + StaticJsonBuffer<1024> jsonBuf; + JsonObject &root = jsonBuf.parseObject(jsonStr); + if (root.success()) { + + const char *unit; + unit = root[D_JSON_TEMPERATURE_UNIT]; + if (unit) { + snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%s"), unit); + } + unit = root[D_JSON_PRESSURE_UNIT]; + if (unit) { + snprintf_P(disp_pres, sizeof(disp_pres), PSTR("%s"), unit); + } + + for (JsonObject::iterator it = root.begin(); it != root.end(); ++it) { + JsonVariant value = it->value; + if (value.is()) { + JsonObject& Object2 = value; + for (JsonObject::iterator it2 = Object2.begin(); it2 != Object2.end(); ++it2) { + JsonVariant value2 = it2->value; + if (value2.is()) { + JsonObject& Object3 = value2; + for (JsonObject::iterator it3 = Object3.begin(); it3 != Object3.end(); ++it3) { + const char* value = it3->value; + if (value != nullptr) { + DisplayJsonValue(topic, it->key, it3->key, value); + } + } + } else { + const char* value = it2->value; + if (value != nullptr) { + DisplayJsonValue(topic, it->key, it2->key, value); + } + } + } + } else { + const char* value = it->value; + if (value != nullptr) { + DisplayJsonValue(topic, it->key, it->key, value); + } + } + } + } +} + +void DisplayMqttSubscribe(void) +{ + + + + + + + if (Settings.display_model && (Settings.display_mode &0x04)) { + + char stopic[TOPSZ]; + char ntopic[TOPSZ]; + + ntopic[0] = '\0'; + strlcpy(stopic, SettingsText(SET_MQTT_FULLTOPIC), sizeof(stopic)); + char *tp = strtok(stopic, "/"); + while (tp != nullptr) { + if (!strcmp_P(tp, MQTT_TOKEN_PREFIX)) { + break; + } + strncat_P(ntopic, PSTR("+/"), sizeof(ntopic) - strlen(ntopic) -1); + tp = strtok(nullptr, "/"); + } + strncat(ntopic, SettingsText(SET_MQTTPREFIX3), sizeof(ntopic) - strlen(ntopic) -1); + strncat_P(ntopic, PSTR("/#"), sizeof(ntopic) - strlen(ntopic) -1); + MqttSubscribe(ntopic); + disp_subscribed = true; + } else { + disp_subscribed = false; + } +} + +bool DisplayMqttData(void) +{ + if (disp_subscribed) { + char stopic[TOPSZ]; + + snprintf_P(stopic, sizeof(stopic) , PSTR("%s/"), SettingsText(SET_MQTTPREFIX3)); + char *tp = strstr(XdrvMailbox.topic, stopic); + if (tp) { + if (Settings.display_mode &0x04) { + tp = tp + strlen(stopic); + char *topic = strtok(tp, "/"); + DisplayAnalyzeJson(topic, XdrvMailbox.data); + } + return true; + } + } + return false; +} + +void DisplayLocalSensor(void) +{ + if ((Settings.display_mode &0x02) && (0 == tele_period)) { + char no_topic[1] = { 0 }; + + DisplayAnalyzeJson(no_topic, mqtt_data); + } +} + +#endif + + + + + +void DisplayInitDriver(void) +{ + XdspCall(FUNC_DISPLAY_INIT_DRIVER); + + if (renderer) { + renderer->setTextFont(Settings.display_font); + renderer->setTextSize(Settings.display_size); + } + + + + + if (Settings.display_model) { + devices_present++; + disp_device = devices_present; + +#ifndef USE_DISPLAY_MODES1TO5 + Settings.display_mode = 0; +#else + DisplayLogBufferInit(); +#endif + } +} + +void DisplaySetPower(void) +{ + disp_power = bitRead(XdrvMailbox.index, disp_device -1); + + + + if (Settings.display_model) { + if (!renderer) { + XdspCall(FUNC_DISPLAY_POWER); + } else { + renderer->DisplayOnff(disp_power); + } + } +} + + + + + +void CmndDisplay(void) +{ + Response_P(PSTR("{\"" D_PRFX_DISPLAY "\":{\"" D_CMND_DISP_MODEL "\":%d,\"" D_CMND_DISP_WIDTH "\":%d,\"" D_CMND_DISP_HEIGHT "\":%d,\"" + D_CMND_DISP_MODE "\":%d,\"" D_CMND_DISP_DIMMER "\":%d,\"" D_CMND_DISP_SIZE "\":%d,\"" D_CMND_DISP_FONT "\":%d,\"" + D_CMND_DISP_ROTATE "\":%d,\"" D_CMND_DISP_REFRESH "\":%d,\"" D_CMND_DISP_COLS "\":[%d,%d],\"" D_CMND_DISP_ROWS "\":%d}}"), + Settings.display_model, Settings.display_width, Settings.display_height, + Settings.display_mode, Settings.display_dimmer, Settings.display_size, Settings.display_font, + Settings.display_rotate, Settings.display_refresh, Settings.display_cols[0], Settings.display_cols[1], Settings.display_rows); +} + +void CmndDisplayModel(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < DISPLAY_MAX_DRIVERS)) { + uint32_t last_display_model = Settings.display_model; + Settings.display_model = XdrvMailbox.payload; + if (XdspCall(FUNC_DISPLAY_MODEL)) { + restart_flag = 2; + } else { + Settings.display_model = last_display_model; + } + } + ResponseCmndNumber(Settings.display_model); +} + +void CmndDisplayWidth(void) +{ + if (XdrvMailbox.payload > 0) { + if (XdrvMailbox.payload != Settings.display_width) { + Settings.display_width = XdrvMailbox.payload; + restart_flag = 2; + } + } + ResponseCmndNumber(Settings.display_width); +} + +void CmndDisplayHeight(void) +{ + if (XdrvMailbox.payload > 0) { + if (XdrvMailbox.payload != Settings.display_height) { + Settings.display_height = XdrvMailbox.payload; + restart_flag = 2; + } + } + ResponseCmndNumber(Settings.display_height); +} + +void CmndDisplayMode(void) +{ +#ifdef USE_DISPLAY_MODES1TO5 + + + + + + + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { + uint32_t last_display_mode = Settings.display_mode; + Settings.display_mode = XdrvMailbox.payload; + + if (disp_subscribed != (Settings.display_mode &0x04)) { + restart_flag = 2; + } else { + if (last_display_mode && !Settings.display_mode) { + DisplayInit(DISPLAY_INIT_MODE); + if (renderer) renderer->fillScreen(bg_color); + else DisplayClear(); + } else { + DisplayLogBufferInit(); + DisplayInit(DISPLAY_INIT_MODE); + } + } + } +#endif + ResponseCmndNumber(Settings.display_mode); +} + +void CmndDisplayDimmer(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + Settings.display_dimmer = ((XdrvMailbox.payload +1) * 100) / 666; + if (Settings.display_dimmer && !(disp_power)) { + ExecuteCommandPower(disp_device, POWER_ON, SRC_DISPLAY); + } + else if (!Settings.display_dimmer && disp_power) { + ExecuteCommandPower(disp_device, POWER_OFF, SRC_DISPLAY); + } + if (renderer) renderer->dim(Settings.display_dimmer); + } + ResponseCmndNumber(Settings.display_dimmer); +} + +void CmndDisplaySize(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) { + Settings.display_size = XdrvMailbox.payload; + if (renderer) renderer->setTextSize(Settings.display_size); + else DisplaySetSize(Settings.display_size); + } + ResponseCmndNumber(Settings.display_size); +} + +void CmndDisplayFont(void) +{ + if ((XdrvMailbox.payload >=0) && (XdrvMailbox.payload <= 4)) { + Settings.display_font = XdrvMailbox.payload; + if (renderer) renderer->setTextFont(Settings.display_font); + else DisplaySetFont(Settings.display_font); + } + ResponseCmndNumber(Settings.display_font); +} + +void CmndDisplayRotate(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { + if (Settings.display_rotate != XdrvMailbox.payload) { +# 1428 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_13_display.ino" + Settings.display_rotate = XdrvMailbox.payload; + DisplayInit(DISPLAY_INIT_MODE); +#ifdef USE_DISPLAY_MODES1TO5 + DisplayLogBufferInit(); +#endif + } + } + ResponseCmndNumber(Settings.display_rotate); +} + +void CmndDisplayText(void) +{ + if (disp_device && XdrvMailbox.data_len > 0) { +#ifndef USE_DISPLAY_MODES1TO5 + DisplayText(); +#else + if (!Settings.display_mode) { + DisplayText(); + } else { + DisplayLogBufferAdd(XdrvMailbox.data); + } +#endif + ResponseCmndChar(XdrvMailbox.data); + } +} + +void CmndDisplayAddress(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 8)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { + Settings.display_address[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.display_address[XdrvMailbox.index -1]); + } +} + +void CmndDisplayRefresh(void) +{ + if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 7)) { + Settings.display_refresh = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.display_refresh); +} + +void CmndDisplayColumns(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_COLS)) { + Settings.display_cols[XdrvMailbox.index -1] = XdrvMailbox.payload; +#ifdef USE_DISPLAY_MODES1TO5 + if (1 == XdrvMailbox.index) { + DisplayLogBufferInit(); + DisplayReAllocScreenBuffer(); + } +#endif + } + ResponseCmndIdxNumber(Settings.display_cols[XdrvMailbox.index -1]); + } +} + +void CmndDisplayRows(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_ROWS)) { + Settings.display_rows = XdrvMailbox.payload; +#ifdef USE_DISPLAY_MODES1TO5 + DisplayLogBufferInit(); + DisplayReAllocScreenBuffer(); +#endif + } + ResponseCmndNumber(Settings.display_rows); +} + + + + + +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) +void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp) { + if (!renderer) return; + + + File fp; + fp=SD.open(file,FILE_READ); + if (!fp) return; + uint16_t xsize; + fp.read((uint8_t*)&xsize,2); + uint16_t ysize; + fp.read((uint8_t*)&ysize,2); + +#if 1 +#define XBUFF 128 + uint16_t xdiv=xsize/XBUFF; + renderer->setAddrWindow(xp,yp,xp+xsize,yp+ysize); + for(int16_t j=0; j=2) renderer->pushColors(rgb,len/2,true); + } + OsWatchLoop(); + } + renderer->setAddrWindow(0,0,0,0); +#else + for(int16_t j=0; jwritePixel(xp+i,yp,rgb); + } + delay(0); + OsWatchLoop(); + yp++; + } +#endif + fp.close(); +} +#endif + +#ifdef USE_AWATCH +#define MINUTE_REDUCT 4 + +#ifndef pi +#define pi 3.14159265359 +#endif + + +void DrawAClock(uint16_t rad) { + if (!renderer) return; + float frad=rad; + uint16_t hred=frad/3.0; + renderer->fillCircle(disp_xpos, disp_ypos, rad, bg_color); + renderer->drawCircle(disp_xpos, disp_ypos, rad, fg_color); + renderer->fillCircle(disp_xpos, disp_ypos, 4, fg_color); + for (uint8_t count=0; count<60; count+=5) { + float p1=((float)count*(pi/30)-(pi/2)); + uint8_t len; + if ((count%15)==0) { + len=4; + } else { + len=2; + } + renderer->writeLine(disp_xpos+((float)(rad-len)*cosf(p1)), disp_ypos+((float)(rad-len)*sinf(p1)), disp_xpos+(frad*cosf(p1)), disp_ypos+(frad*sinf(p1)), fg_color); + } + + + float hour=((float)RtcTime.hour*60.0+(float)RtcTime.minute)/60.0; + float temp=(hour*(pi/6.0)-(pi/2.0)); + renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-hred)*cosf(temp),disp_ypos+(frad-hred)*sinf(temp), fg_color); + + + temp=((float)RtcTime.minute*(pi/30.0)-(pi/2.0)); + renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-MINUTE_REDUCT)*cosf(temp),disp_ypos+(frad-MINUTE_REDUCT)*sinf(temp), fg_color); +} +#endif + + +#ifdef USE_GRAPH + +typedef union { + uint8_t data; + struct { + uint8_t overlay : 1; + uint8_t draw : 1; + uint8_t nu3 : 1; + uint8_t nu4 : 1; + uint8_t nu5 : 1; + uint8_t nu6 : 1; + uint8_t nu7 : 1; + uint8_t nu8 : 1; + }; +} GFLAGS; + +struct GRAPH { + uint16_t xp; + uint16_t yp; + uint16_t xs; + uint16_t ys; + float ymin; + float ymax; + float range; + uint32_t x_time; + uint32_t last_ms; + uint32_t last_ms_redrawn; + int16_t decimation; + uint16_t dcnt; + uint32_t summ; + uint16_t xcnt; + uint8_t *values; + uint8_t xticks; + uint8_t yticks; + uint8_t last_val; + uint8_t color_index; + GFLAGS flags; +}; + + +struct GRAPH *graph[NUM_GRAPHS]; + +#define TICKLEN 4 +void ClrGraph(uint16_t num) { + struct GRAPH *gp=graph[num]; + + uint16_t xticks=gp->xticks; + uint16_t yticks=gp->yticks; + uint16_t count; + + + if (gp->flags.overlay) return; + + renderer->fillRect(gp->xp+1,gp->yp+1,gp->xs-2,gp->ys-2,bg_color); + + if (xticks) { + float cxp=gp->xp,xd=(float)gp->xs/(float)xticks; + for (count=0; countwriteFastVLine(cxp,gp->yp+gp->ys-TICKLEN,TICKLEN,fg_color); + cxp+=xd; + } + } + if (yticks) { + if (gp->ymin<0 && gp->ymax>0) { + + float cxp=0; + float czp=gp->yp+(gp->ymax/gp->range); + while (cxpxs) { + renderer->writeFastHLine(gp->xp+cxp,czp,2,fg_color); + cxp+=6.0; + } + + float cyp=0,yd=gp->ys/yticks; + for (count=0; countgp->yp) { + renderer->writeFastHLine(gp->xp,czp-cyp,TICKLEN,fg_color); + renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,czp-cyp,TICKLEN,fg_color); + } + if ((czp+cyp)<(gp->yp+gp->ys)) { + renderer->writeFastHLine(gp->xp,czp+cyp,TICKLEN,fg_color); + renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,czp+cyp,TICKLEN,fg_color); + } + cyp+=yd; + } + } else { + float cyp=gp->yp,yd=gp->ys/yticks; + for (count=0; countwriteFastHLine(gp->xp,cyp,TICKLEN,fg_color); + renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,cyp,TICKLEN,fg_color); + cyp+=yd; + } + } + } +} + + +void DefineGraph(uint16_t num,uint16_t xp,uint16_t yp,int16_t xs,uint16_t ys,int16_t dec,float ymin, float ymax,uint8_t icol) { + if (!renderer) return; + uint8_t rflg=0; + if (xs<0) { + rflg=1; + xs=abs(xs); + } + struct GRAPH *gp; + uint16_t count; + uint16_t index=num%NUM_GRAPHS; + if (!graph[index]) { + gp=(struct GRAPH*)calloc(sizeof(struct GRAPH),1); + if (!gp) return; + graph[index]=gp; + } else { + gp=graph[index]; + if (rflg) { + RedrawGraph(index,1); + return; + } + } + + + gp->xticks=(num>>4)&0x3f; + gp->yticks=(num>>10)&0x3f; + gp->xp=xp; + gp->yp=yp; + gp->xs=xs; + gp->ys=ys; + if (!dec) dec=1; + gp->decimation=dec; + if (dec>0) { + + gp->x_time=((float)dec*60000.0)/(float)xs; + gp->last_ms=millis()+gp->x_time; + } + gp->ymin=ymin; + gp->ymax=ymax; + gp->range=(ymax-ymin)/ys; + gp->xcnt=0; + gp->dcnt=0; + gp->summ=0; + if (gp->values) free(gp->values); + gp->values=(uint8_t*) calloc(1,xs+2); + if (!gp->values) { + free(gp); + graph[index]=0; + return; + } + + gp->values[0]=0; + + gp->last_ms_redrawn=millis(); + + if (!icol) icol=1; + gp->color_index=icol; + gp->flags.overlay=0; + gp->flags.draw=1; + + + if (index>0) { + for (uint8_t count=0; countxp==gp1->xp) && (gp->yp==gp1->yp)) { + gp->flags.overlay=1; + break; + } + } + } + } + + + renderer->drawRect(xp,yp,xs,ys,fg_color); + + ClrGraph(index); + +} + + +void DisplayCheckGraph() { + int16_t count; + struct GRAPH *gp; + for (count=0;countdecimation>0) { + + while (millis()>gp->last_ms) { + gp->last_ms+=gp->x_time; + uint8_t val; + if (gp->dcnt) { + val=gp->summ/gp->dcnt; + gp->dcnt=0; + gp->summ=0; + gp->last_val=val; + } else { + val=gp->last_val; + } + AddGraph(count,val); + } + } + } + } +} + + +#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) +#include + +void Save_graph(uint8_t num, char *path) { + if (!renderer) return; + uint16_t index=num%NUM_GRAPHS; + struct GRAPH *gp=graph[index]; + if (!gp) return; + File fp; + SD.remove(path); + fp=SD.open(path,FILE_WRITE); + if (!fp) return; + char str[32]; + sprintf_P(str,PSTR("%d\t%d\t%d\t"),gp->xcnt,gp->xs,gp->ys); + fp.print(str); + dtostrfd(gp->ymin,2,str); + fp.print(str); + fp.print("\t"); + dtostrfd(gp->ymax,2,str); + fp.print(str); + fp.print("\t"); + for (uint32_t count=0;countxs;count++) { + dtostrfd(gp->values[count],0,str); + fp.print(str); + fp.print("\t"); + } + fp.print("\n"); + fp.close(); +} +void Restore_graph(uint8_t num, char *path) { + if (!renderer) return; + uint16_t index=num%NUM_GRAPHS; + struct GRAPH *gp=graph[index]; + if (!gp) return; + File fp; + fp=SD.open(path,FILE_READ); + if (!fp) return; + char vbuff[32]; + char *cp=vbuff; + uint8_t buf[2]; + uint8_t findex=0; + + for (uint32_t count=0;count<=gp->xs+4;count++) { + cp=vbuff; + findex=0; + while (fp.available()) { + fp.read(buf,1); + if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { + break; + } else { + *cp++=buf[0]; + findex++; + if (findex>=sizeof(vbuff)-1) break; + } + } + *cp=0; + if (count<=4) { + if (count==0) gp->xcnt=atoi(vbuff); + } else { + gp->values[count-5]=atoi(vbuff); + } + } + fp.close(); + RedrawGraph(num,1); +} +#endif + +void RedrawGraph(uint8_t num, uint8_t flags) { + uint16_t index=num%NUM_GRAPHS; + struct GRAPH *gp=graph[index]; + if (!gp) return; + if (!flags) { + gp->flags.draw=0; + return; + } + if (!renderer) return; + + gp->flags.draw=1; + uint16_t linecol=fg_color; + + if (color_type==COLOR_COLOR) { + linecol=renderer->GetColorFromIndex(gp->color_index); + } + + if (!gp->flags.overlay) { + + renderer->drawRect(gp->xp,gp->yp,gp->xs,gp->ys,fg_color); + + ClrGraph(index); + } + + for (uint16_t count=0;countxs-1;count++) { + renderer->writeLine(gp->xp+count,gp->yp+gp->ys-gp->values[count]-1,gp->xp+count+1,gp->yp+gp->ys-gp->values[count+1]-1,linecol); + } +} + + +void AddGraph(uint8_t num,uint8_t val) { + struct GRAPH *gp=graph[num]; + if (!renderer) return; + + uint16_t linecol=fg_color; + if (color_type==COLOR_COLOR) { + linecol=renderer->GetColorFromIndex(gp->color_index); + } + gp->xcnt++; + if (gp->xcnt>gp->xs) { + gp->xcnt=gp->xs; + int16_t count; + + for (count=0;countxs-1;count++) { + gp->values[count]=gp->values[count+1]; + } + gp->values[gp->xcnt-1]=val; + + if (!gp->flags.draw) return; + + + if (millis()-gp->last_ms_redrawn>1000) { + gp->last_ms_redrawn=millis(); + + if (!gp->flags.overlay) { + + renderer->drawRect(gp->xp,gp->yp,gp->xs,gp->ys,fg_color); + + ClrGraph(num); + } + + for (count=0;countxs-1;count++) { + renderer->writeLine(gp->xp+count,gp->yp+gp->ys-gp->values[count]-1,gp->xp+count+1,gp->yp+gp->ys-gp->values[count+1]-1,linecol); + } + } + } else { + + gp->values[gp->xcnt]=val; + if (!gp->flags.draw) return; + renderer->writeLine(gp->xp+gp->xcnt-1,gp->yp+gp->ys-gp->values[gp->xcnt-1]-1,gp->xp+gp->xcnt,gp->yp+gp->ys-gp->values[gp->xcnt]-1,linecol); + } +} + + + +void AddValue(uint8_t num,float fval) { + + num=num%NUM_GRAPHS; + struct GRAPH *gp=graph[num]; + if (!gp) return; + + if (fval>gp->ymax) fval=gp->ymax; + if (fvalymin) fval=gp->ymin; + + int16_t val; + val=(fval-gp->ymin)/gp->range; + + if (val>gp->ys-1) val=gp->ys-1; + if (val<0) val=0; + + + gp->summ+=val; + gp->dcnt++; + + + if (gp->decimation<0) { + if (gp->dcnt>=-gp->decimation) { + gp->dcnt=0; + + val=gp->summ/-gp->decimation; + gp->summ=0; + + AddGraph(num,val); + } + } +} +#endif + + + + + +bool Xdrv13(uint8_t function) +{ + bool result = false; + + if ((i2c_flg || spi_flg || soft_spi_flg) && XdspPresent()) { + switch (function) { + case FUNC_PRE_INIT: + DisplayInitDriver(); +#ifdef USE_GRAPH + for (uint8_t count=0;count + +TasmotaSerial *MP3Player; + + + + + +#define D_CMND_MP3 "MP3" + +const char S_JSON_MP3_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MP3 "%s\":%d}"; +const char S_JSON_MP3_COMMAND[] PROGMEM = "{\"" D_CMND_MP3 "%s\"}"; +const char kMP3_Commands[] PROGMEM = "Track|Play|Pause|Stop|Volume|EQ|Device|Reset|DAC"; + + + + + +enum MP3_Commands { + CMND_MP3_TRACK, + CMND_MP3_PLAY, + CMND_MP3_PAUSE, + CMND_MP3_STOP, + CMND_MP3_VOLUME, + CMND_MP3_EQ, + CMND_MP3_DEVICE, + CMND_MP3_RESET, + CMND_MP3_DAC }; + + + + + + +#define MP3_CMD_RESET_VALUE 0 + +#define MP3_CMD_TRACK 0x03 +#define MP3_CMD_PLAY 0x0d +#define MP3_CMD_PAUSE 0x0e +#define MP3_CMD_STOP 0x16 +#define MP3_CMD_VOLUME 0x06 +#define MP3_CMD_EQ 0x07 +#define MP3_CMD_DEVICE 0x09 +#define MP3_CMD_RESET 0x0C +#define MP3_CMD_DAC 0x1A + + + + + + +uint16_t MP3_Checksum(uint8_t *array) +{ + uint16_t checksum = 0; + for (uint32_t i = 0; i < 6; i++) { + checksum += array[i]; + } + checksum = checksum^0xffff; + return (checksum+1); +} + + + + + + +void MP3PlayerInit(void) { + MP3Player = new TasmotaSerial(-1, pin[GPIO_MP3_DFR562]); + + if (MP3Player->begin(9600)) { + MP3Player->flush(); + delay(1000); + MP3_CMD(MP3_CMD_RESET, MP3_CMD_RESET_VALUE); + delay(3000); + MP3_CMD(MP3_CMD_VOLUME, MP3_VOLUME); + } + return; +} +# 159 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_14_mp3.ino" +void MP3_CMD(uint8_t mp3cmd,uint16_t val) { + uint8_t i = 0; + uint8_t cmd[10] = {0x7e,0xff,6,0,0,0,0,0,0,0xef}; + cmd[3] = mp3cmd; + cmd[4] = 0; + cmd[5] = val>>8; + cmd[6] = val; + uint16_t chks = MP3_Checksum(&cmd[1]); + cmd[7] = chks>>8; + cmd[8] = chks; + MP3Player->write(cmd, sizeof(cmd)); + delay(1000); + if (mp3cmd == MP3_CMD_RESET) { + MP3_CMD(MP3_CMD_VOLUME, MP3_VOLUME); + } + return; +} + + + + + +bool MP3PlayerCmd(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t disp_len = strlen(D_CMND_MP3); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_MP3), disp_len)) { + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kMP3_Commands); + + switch (command_code) { + case CMND_MP3_TRACK: + case CMND_MP3_VOLUME: + case CMND_MP3_EQ: + case CMND_MP3_DEVICE: + case CMND_MP3_DAC: + + if (XdrvMailbox.data_len > 0) { + if (command_code == CMND_MP3_TRACK) { MP3_CMD(MP3_CMD_TRACK, XdrvMailbox.payload); } + if (command_code == CMND_MP3_VOLUME) { MP3_CMD(MP3_CMD_VOLUME, XdrvMailbox.payload * 30 / 100); } + if (command_code == CMND_MP3_EQ) { MP3_CMD(MP3_CMD_EQ, XdrvMailbox.payload); } + if (command_code == CMND_MP3_DEVICE) { MP3_CMD(MP3_CMD_DEVICE, XdrvMailbox.payload); } + if (command_code == CMND_MP3_DAC) { MP3_CMD(MP3_CMD_DAC, XdrvMailbox.payload); } + } + Response_P(S_JSON_MP3_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_MP3_PLAY: + case CMND_MP3_PAUSE: + case CMND_MP3_STOP: + case CMND_MP3_RESET: + + if (command_code == CMND_MP3_PLAY) { MP3_CMD(MP3_CMD_PLAY, 0); } + if (command_code == CMND_MP3_PAUSE) { MP3_CMD(MP3_CMD_PAUSE, 0); } + if (command_code == CMND_MP3_STOP) { MP3_CMD(MP3_CMD_STOP, 0); } + if (command_code == CMND_MP3_RESET) { MP3_CMD(MP3_CMD_RESET, 0); } + Response_P(S_JSON_MP3_COMMAND, command, XdrvMailbox.payload); + break; + default: + + serviced = false; + break; + } + } else { + return false; + } + return serviced; +} + + + + + +bool Xdrv14(uint8_t function) +{ + bool result = false; + + if (pin[GPIO_MP3_DFR562] < 99) { + switch (function) { + case FUNC_PRE_INIT: + MP3PlayerInit(); + break; + case FUNC_COMMAND: + result = MP3PlayerCmd(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_15_pca9685.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_15_pca9685.ino" +#ifdef USE_I2C +#ifdef USE_PCA9685 + + + + + + +#define XDRV_15 15 +#define XI2C_01 1 + +#define PCA9685_REG_MODE1 0x00 +#define PCA9685_REG_LED0_ON_L 0x06 +#define PCA9685_REG_PRE_SCALE 0xFE + +#ifndef USE_PCA9685_ADDR + #define USE_PCA9685_ADDR 0x40 +#endif +#ifndef USE_PCA9685_FREQ + #define USE_PCA9685_FREQ 50 +#endif + +bool pca9685_detected = false; +uint16_t pca9685_freq = USE_PCA9685_FREQ; +uint16_t pca9685_pin_pwm_value[16]; + +void PCA9685_Detect(void) +{ + if (I2cActive(USE_PCA9685_ADDR)) { return; } + + uint8_t buffer; + if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x20); + if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { + if (0x20 == buffer) { + pca9685_detected = true; + I2cSetActiveFound(USE_PCA9685_ADDR, "PCA9685"); + PCA9685_Reset(); + } + } + } +} + +void PCA9685_Reset(void) +{ + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x80); + PCA9685_SetPWMfreq(USE_PCA9685_FREQ); + for (uint32_t pin=0;pin<16;pin++) { + PCA9685_SetPWM(pin,0,false); + pca9685_pin_pwm_value[pin] = 0; + } + Response_P(PSTR("{\"PCA9685\":{\"RESET\":\"OK\"}}")); +} + +void PCA9685_SetPWMfreq(double freq) { + + + + + if (freq > 23 && freq < 1527) { + pca9685_freq=freq; + } else { + pca9685_freq=50; + } + uint8_t pre_scale_osc = round(25000000/(4096*pca9685_freq))-1; + if (1526 == pca9685_freq) pre_scale_osc=0xFF; + uint8_t current_mode1 = I2cRead8(USE_PCA9685_ADDR, PCA9685_REG_MODE1); + uint8_t sleep_mode1 = (current_mode1&0x7F) | 0x10; + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, sleep_mode1); + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_PRE_SCALE, pre_scale_osc); + I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, current_mode1 | 0xA0); +} + +void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off) { + uint8_t led_reg = PCA9685_REG_LED0_ON_L + 4 * pin; + uint32_t led_data = 0; + I2cWrite8(USE_PCA9685_ADDR, led_reg, on); + I2cWrite8(USE_PCA9685_ADDR, led_reg+1, (on >> 8)); + I2cWrite8(USE_PCA9685_ADDR, led_reg+2, off); + I2cWrite8(USE_PCA9685_ADDR, led_reg+3, (off >> 8)); +} + +void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted) { + if (4096 == pwm) { + PCA9685_SetPWM_Reg(pin, 4096, 0); + } else { + PCA9685_SetPWM_Reg(pin, 0, pwm); + } + pca9685_pin_pwm_value[pin] = pwm; +} + +bool PCA9685_Command(void) +{ + bool serviced = true; + bool validpin = false; + uint8_t paramcount = 0; + if (XdrvMailbox.data_len > 0) { + paramcount=1; + } else { + serviced = false; + return serviced; + } + char sub_string[XdrvMailbox.data_len]; + for (uint32_t ca=0;ca 1) { + uint16_t new_freq = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if ((new_freq >= 24) && (new_freq <= 1526)) { + PCA9685_SetPWMfreq(new_freq); + Response_P(PSTR("{\"PCA9685\":{\"PWMF\":%i, \"Result\":\"OK\"}}"),new_freq); + return serviced; + } + } else { + Response_P(PSTR("{\"PCA9685\":{\"PWMF\":%i}}"),pca9685_freq); + return serviced; + } + } + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"PWM")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (paramcount > 2) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "ON")) { + PCA9685_SetPWM(pin, 4096, false); + Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,4096); + serviced = true; + return serviced; + } + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "OFF")) { + PCA9685_SetPWM(pin, 0, false); + Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,0); + serviced = true; + return serviced; + } + uint16_t pwm = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((pin >= 0 && pin <= 15) && (pwm >= 0 && pwm <= 4096)) { + PCA9685_SetPWM(pin, pwm, false); + Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,pwm); + serviced = true; + return serviced; + } + } + } + } + return serviced; +} + +void PCA9685_OutputTelemetry(bool telemetry) +{ + ResponseTime_P(PSTR(",\"PCA9685\":{\"PWM_FREQ\":%i,"),pca9685_freq); + for (uint32_t pin=0;pin<16;pin++) { + ResponseAppend_P(PSTR("\"PWM%i\":%i,"),pin,pca9685_pin_pwm_value[pin]); + } + ResponseAppend_P(PSTR("\"END\":1}}")); + if (telemetry) { + MqttPublishTeleSensor(); + } +} + +bool Xdrv15(uint8_t function) +{ + if (!I2cEnabled(XI2C_01)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + PCA9685_Detect(); + } + else if (pca9685_detected) { + switch (function) { + case FUNC_EVERY_SECOND: + if (tele_period == 0) { + PCA9685_OutputTelemetry(true); + } + break; + case FUNC_COMMAND_DRIVER: + if (XDRV_15 == XdrvMailbox.index) { + result = PCA9685_Command(); + } + break; + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_16_tuyamcu.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_16_tuyamcu.ino" +#ifdef USE_LIGHT +#ifdef USE_TUYA_MCU + +#define XDRV_16 16 +#define XNRG_16 16 + +#ifndef TUYA_DIMMER_ID +#define TUYA_DIMMER_ID 0 +#endif + +#define TUYA_CMD_HEARTBEAT 0x00 +#define TUYA_CMD_QUERY_PRODUCT 0x01 +#define TUYA_CMD_MCU_CONF 0x02 +#define TUYA_CMD_WIFI_STATE 0x03 +#define TUYA_CMD_WIFI_RESET 0x04 +#define TUYA_CMD_WIFI_SELECT 0x05 +#define TUYA_CMD_SET_DP 0x06 +#define TUYA_CMD_STATE 0x07 +#define TUYA_CMD_QUERY_STATE 0x08 + +#define TUYA_LOW_POWER_CMD_WIFI_STATE 0x02 +#define TUYA_LOW_POWER_CMD_WIFI_RESET 0x03 +#define TUYA_LOW_POWER_CMD_WIFI_CONFIG 0x04 +#define TUYA_LOW_POWER_CMD_STATE 0x05 + +#define TUYA_TYPE_BOOL 0x01 +#define TUYA_TYPE_VALUE 0x02 +#define TUYA_TYPE_STRING 0x03 +#define TUYA_TYPE_ENUM 0x04 + +#define TUYA_BUFFER_SIZE 256 + +#include + +TasmotaSerial *TuyaSerial = nullptr; + +struct TUYA { + uint16_t new_dim = 0; + bool ignore_dim = false; + uint8_t cmd_status = 0; + uint8_t cmd_checksum = 0; + uint8_t data_len = 0; + uint8_t wifi_state = -2; + uint8_t heartbeat_timer = 0; +#ifdef USE_ENERGY_SENSOR + uint32_t lastPowerCheckTime = 0; +#endif + char *buffer = nullptr; + int byte_counter = 0; + bool low_power_mode = false; + bool send_success_next_second = false; +} Tuya; + + +enum TuyaSupportedFunctions { + TUYA_MCU_FUNC_NONE, + TUYA_MCU_FUNC_SWT1 = 1, + TUYA_MCU_FUNC_SWT2, + TUYA_MCU_FUNC_SWT3, + TUYA_MCU_FUNC_SWT4, + TUYA_MCU_FUNC_REL1 = 11, + TUYA_MCU_FUNC_REL2, + TUYA_MCU_FUNC_REL3, + TUYA_MCU_FUNC_REL4, + TUYA_MCU_FUNC_REL5, + TUYA_MCU_FUNC_REL6, + TUYA_MCU_FUNC_REL7, + TUYA_MCU_FUNC_REL8, + TUYA_MCU_FUNC_DIMMER = 21, + TUYA_MCU_FUNC_POWER = 31, + TUYA_MCU_FUNC_CURRENT, + TUYA_MCU_FUNC_VOLTAGE, + TUYA_MCU_FUNC_BATTERY_STATE, + TUYA_MCU_FUNC_BATTERY_PERCENTAGE, + TUYA_MCU_FUNC_REL1_INV = 41, + TUYA_MCU_FUNC_REL2_INV, + TUYA_MCU_FUNC_REL3_INV, + TUYA_MCU_FUNC_REL4_INV, + TUYA_MCU_FUNC_REL5_INV, + TUYA_MCU_FUNC_REL6_INV, + TUYA_MCU_FUNC_REL7_INV, + TUYA_MCU_FUNC_REL8_INV, + TUYA_MCU_FUNC_LOWPOWER_MODE = 51, + TUYA_MCU_FUNC_LAST = 255 +}; + +const char kTuyaCommand[] PROGMEM = "|" + D_CMND_TUYA_MCU "|" D_CMND_TUYA_MCU_SEND_STATE; + +void (* const TuyaCommand[])(void) PROGMEM = { + &CmndTuyaMcu, &CmndTuyaSend +}; +# 126 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_16_tuyamcu.ino" +void CmndTuyaSend(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { + if (XdrvMailbox.data_len > 0) { + + char *p; + char *data; + uint8_t i = 0; + uint8_t dpId = 0; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) { + if ( i == 0) { + dpId = strtoul(str, nullptr, 0); + } else { + data = str; + } + i++; + } + + if (1 == XdrvMailbox.index) { + TuyaSendBool(dpId, strtoul(data, nullptr, 0)); + } else if (2 == XdrvMailbox.index) { + TuyaSendValue(dpId, strtoull(data, nullptr, 0)); + } else if (3 == XdrvMailbox.index) { + TuyaSendString(dpId, data); + } else if (4 == XdrvMailbox.index) { + TuyaSendEnum(dpId, strtoul(data, nullptr, 0)); + } + } + ResponseCmndDone(); + } +} + + + + + + + +void CmndTuyaMcu(void) { + if (XdrvMailbox.data_len > 0) { + char *p; + uint8_t i = 0; + uint8_t parm[3] = { 0 }; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) { + parm[i] = strtoul(str, nullptr, 0); + i++; + } + + if (TuyaFuncIdValid(parm[0])) { + TuyaAddMcuFunc(parm[0], parm[1]); + restart_flag = 2; + } else { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("TYA: TuyaMcu Invalid function id=%d"), parm[0]); + } + + } + + Response_P(PSTR("{\"" D_CMND_TUYA_MCU "\":[")); + bool added = false; + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].fnid != 0) { + if (added) { + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("{\"fnId\":%d,\"dpId\":%d}" ), Settings.tuya_fnid_map[i].fnid, Settings.tuya_fnid_map[i].dpid); + added = true; + } + } + ResponseAppend_P(PSTR("]}")); +} + + + + + +void TuyaAddMcuFunc(uint8_t fnId, uint8_t dpId) { + bool added = false; + + if (fnId == 0 || dpId == 0) { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if ((dpId > 0 && Settings.tuya_fnid_map[i].dpid == dpId) || (fnId > TUYA_MCU_FUNC_NONE && Settings.tuya_fnid_map[i].fnid == fnId)) { + Settings.tuya_fnid_map[i].fnid = TUYA_MCU_FUNC_NONE; + Settings.tuya_fnid_map[i].dpid = 0; + break; + } + } + } else { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].dpid == dpId || Settings.tuya_fnid_map[i].dpid == 0 || Settings.tuya_fnid_map[i].fnid == fnId || Settings.tuya_fnid_map[i].fnid == 0) { + if (!added) { + Settings.tuya_fnid_map[i].fnid = fnId; + Settings.tuya_fnid_map[i].dpid = dpId; + added = true; + } else if (Settings.tuya_fnid_map[i].dpid == dpId || Settings.tuya_fnid_map[i].fnid == fnId) { + Settings.tuya_fnid_map[i].fnid = TUYA_MCU_FUNC_NONE; + Settings.tuya_fnid_map[i].dpid = 0; + } + } + } + } + UpdateDevices(); +} + +void UpdateDevices() { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + uint8_t fnId = Settings.tuya_fnid_map[i].fnid; + if (fnId > TUYA_MCU_FUNC_NONE && Settings.tuya_fnid_map[i].dpid > 0) { + + if (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) { + bitClear(rel_inverted, fnId - TUYA_MCU_FUNC_REL1); + } else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) { + bitSet(rel_inverted, fnId - TUYA_MCU_FUNC_REL1_INV); + } + + } + } +} + +inline bool TuyaFuncIdValid(uint8_t fnId) { + return (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) || + (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) || + fnId == TUYA_MCU_FUNC_DIMMER || + (fnId >= TUYA_MCU_FUNC_POWER && fnId <= TUYA_MCU_FUNC_VOLTAGE) || + (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) || + (fnId == TUYA_MCU_FUNC_LOWPOWER_MODE); +} + +uint8_t TuyaGetFuncId(uint8_t dpid) { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].dpid == dpid) { + return Settings.tuya_fnid_map[i].fnid; + } + } + return TUYA_MCU_FUNC_NONE; +} + +uint8_t TuyaGetDpId(uint8_t fnId) { + for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { + if (Settings.tuya_fnid_map[i].fnid == fnId) { + return Settings.tuya_fnid_map[i].dpid; + } + } + return 0; +} + +void TuyaSendCmd(uint8_t cmd, uint8_t payload[] = nullptr, uint16_t payload_len = 0) +{ + uint8_t checksum = (0xFF + cmd + (payload_len >> 8) + (payload_len & 0xFF)); + TuyaSerial->write(0x55); + TuyaSerial->write(0xAA); + TuyaSerial->write((uint8_t)0x00); + TuyaSerial->write(cmd); + TuyaSerial->write(payload_len >> 8); + TuyaSerial->write(payload_len & 0xFF); + snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Send \"55aa00%02x%02x%02x"), cmd, payload_len >> 8, payload_len & 0xFF); + for (uint32_t i = 0; i < payload_len; ++i) { + TuyaSerial->write(payload[i]); + checksum += payload[i]; + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, payload[i]); + } + TuyaSerial->write(checksum); + TuyaSerial->flush(); + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x\""), log_data, checksum); + AddLog(LOG_LEVEL_DEBUG); +} + +void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value) +{ + uint16_t payload_len = 4; + uint8_t payload_buffer[8]; + payload_buffer[0] = id; + payload_buffer[1] = type; + switch (type) { + case TUYA_TYPE_BOOL: + case TUYA_TYPE_ENUM: + payload_len += 1; + payload_buffer[2] = 0x00; + payload_buffer[3] = 0x01; + payload_buffer[4] = value[0]; + break; + case TUYA_TYPE_VALUE: + payload_len += 4; + payload_buffer[2] = 0x00; + payload_buffer[3] = 0x04; + payload_buffer[4] = value[3]; + payload_buffer[5] = value[2]; + payload_buffer[6] = value[1]; + payload_buffer[7] = value[0]; + break; + + } + + TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); +} + +void TuyaSendBool(uint8_t id, bool value) +{ + TuyaSendState(id, TUYA_TYPE_BOOL, (uint8_t*)&value); +} + +void TuyaSendValue(uint8_t id, uint32_t value) +{ + TuyaSendState(id, TUYA_TYPE_VALUE, (uint8_t*)(&value)); +} + +void TuyaSendEnum(uint8_t id, uint32_t value) +{ + TuyaSendState(id, TUYA_TYPE_ENUM, (uint8_t*)(&value)); +} + +void TuyaSendString(uint8_t id, char data[]) { + + uint16_t len = strlen(data); + uint16_t payload_len = 4 + len; + uint8_t payload_buffer[payload_len]; + payload_buffer[0] = id; + payload_buffer[1] = TUYA_TYPE_STRING; + payload_buffer[2] = len >> 8; + payload_buffer[3] = len & 0xFF; + + for (uint16_t i = 0; i < len; i++) { + payload_buffer[4+i] = data[i]; + } + + TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); +} + +bool TuyaSetPower(void) +{ + bool status = false; + + uint8_t rpower = XdrvMailbox.index; + int16_t source = XdrvMailbox.payload; + + uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_REL1 + active_device - 1); + if (dpid == 0) dpid = TuyaGetDpId(TUYA_MCU_FUNC_REL1_INV + active_device - 1); + + if (source != SRC_SWITCH && TuyaSerial) { + TuyaSendBool(dpid, bitRead(rpower, active_device-1) ^ bitRead(rel_inverted, active_device-1)); + status = true; + } + return status; +} + +bool TuyaSetChannels(void) +{ + LightSerialDuty(((uint8_t*)XdrvMailbox.data)[0]); + delay(20); + return true; +} + +void LightSerialDuty(uint16_t duty) +{ + uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_DIMMER); + if (duty > 0 && !Tuya.ignore_dim && TuyaSerial && dpid > 0) { + duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max); + if (duty < Settings.dimmer_hw_min) { duty = Settings.dimmer_hw_min; } + if (Tuya.new_dim != duty) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim value=%d (id=%d)"), duty, dpid); + TuyaSendValue(dpid, duty); + } + } else if (dpid > 0) { + Tuya.ignore_dim = false; + duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value=%d"), duty); + } else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Cannot set dimmer. Dimmer Id unknown")); + } +} + +void TuyaRequestState(void) +{ + if (TuyaSerial) { + + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Read MCU state")); + + TuyaSendCmd(TUYA_CMD_QUERY_STATE); + } +} + +void TuyaResetWifi(void) +{ + if (!Settings.flag.button_restrict) { + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " %d", 2); + ExecuteCommand(scmnd, SRC_BUTTON); + } +} + +void TuyaProcessStatePacket(void) { + char scmnd[20]; + uint8_t dpidStart = 6; + uint8_t fnId; + uint16_t dpDataLen; + + while (dpidStart + 4 < Tuya.byte_counter) { + dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3]; + fnId = TuyaGetFuncId(Tuya.buffer[dpidStart]); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: fnId=%d is set for dpId=%d"), fnId, Tuya.buffer[dpidStart]); + + if (Tuya.buffer[dpidStart + 1] == 1) { + + if (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[dpidStart + 4]?"On":"Off",bitRead(power, fnId - TUYA_MCU_FUNC_REL1)?"On":"Off"); + if ((power || Settings.light_dimmer > 0) && (Tuya.buffer[dpidStart + 4] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1))) { + ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[dpidStart + 4], SRC_SWITCH); + } + } else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d-Inverted --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[dpidStart + 4]?"Off":"On",bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1?"Off":"On"); + if (Tuya.buffer[dpidStart + 4] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1) { + ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[dpidStart + 4] ^ 1, SRC_SWITCH); + } + } else if (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Switch-%d --> MCU State: %d Current State:%d"),fnId - TUYA_MCU_FUNC_SWT1 + 1,Tuya.buffer[dpidStart + 4], SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1)); + + if (SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1) != Tuya.buffer[dpidStart + 4]) { + SwitchSetVirtual(fnId - TUYA_MCU_FUNC_SWT1, Tuya.buffer[dpidStart + 4]); + SwitchHandler(1); + } + } + + } + else if (Tuya.buffer[dpidStart + 1] == 2) { + bool tuya_energy_enabled = (XNRG_16 == energy_flg); + uint16_t packetValue = Tuya.buffer[dpidStart + 6] << 8 | Tuya.buffer[dpidStart + 7]; + if (fnId == TUYA_MCU_FUNC_DIMMER) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Dim State=%d"), packetValue); + Tuya.new_dim = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100); + if ((power || Settings.flag3.tuya_apply_o20) && + (Tuya.new_dim > 0) && (abs(Tuya.new_dim - Settings.light_dimmer) > 1)) { + Tuya.ignore_dim = true; + + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), Tuya.new_dim ); + ExecuteCommand(scmnd, SRC_SWITCH); + } + } + + #ifdef USE_ENERGY_SENSOR + else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_VOLTAGE) { + Energy.voltage[0] = (float)packetValue / 10; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Tuya.buffer[dpidStart], packetValue); + } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_CURRENT) { + Energy.current[0] = (float)packetValue / 1000; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Tuya.buffer[dpidStart], packetValue); + } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_POWER) { + Energy.active_power[0] = (float)packetValue / 10; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Tuya.buffer[dpidStart], packetValue); + + if (Tuya.lastPowerCheckTime != 0 && Energy.active_power[0] > 0) { + Energy.kWhtoday += (float)Energy.active_power[0] * (Rtc.utc_time - Tuya.lastPowerCheckTime) / 36; + EnergyUpdateToday(); + } + Tuya.lastPowerCheckTime = Rtc.utc_time; + } + #endif + + } + + + dpidStart += dpDataLen + 4; + } +} + +void TuyaLowPowerModePacketProcess(void) { + switch (Tuya.buffer[3]) { + case TUYA_CMD_QUERY_PRODUCT: + TuyaHandleProductInfoPacket(); + TuyaSetWifiLed(); + break; + + case TUYA_LOW_POWER_CMD_STATE: + TuyaProcessStatePacket(); + Tuya.send_success_next_second = true; + break; + } + +} + +void TuyaHandleProductInfoPacket(void) { + uint16_t dataLength = Tuya.buffer[4] << 8 | Tuya.buffer[5]; + char *data = &Tuya.buffer[6]; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TYA: MCU Product ID: %.*s"), dataLength, data); +} + +void TuyaSendLowPowerSuccessIfNeeded(void) { + uint8_t success = 1; + + if (Tuya.send_success_next_second) { + TuyaSendCmd(TUYA_LOW_POWER_CMD_STATE, &success, 1); + Tuya.send_success_next_second = false; + } +} + +void TuyaNormalPowerModePacketProcess(void) +{ + switch (Tuya.buffer[3]) { + case TUYA_CMD_QUERY_PRODUCT: + TuyaHandleProductInfoPacket(); + TuyaSendCmd(TUYA_CMD_MCU_CONF); + break; + + case TUYA_CMD_HEARTBEAT: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat")); + if (Tuya.buffer[6] == 0) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Detected MCU restart")); + Tuya.wifi_state = -2; + } + break; + + case TUYA_CMD_STATE: + TuyaProcessStatePacket(); + break; + + case TUYA_CMD_WIFI_RESET: + case TUYA_CMD_WIFI_SELECT: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi Reset")); + TuyaResetWifi(); + break; + + case TUYA_CMD_WIFI_STATE: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi LED set ACK")); + Tuya.wifi_state = TuyaGetTuyaWifiState(); + break; + + case TUYA_CMD_MCU_CONF: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX MCU configuration Mode=%d"), Tuya.buffer[5]); + + if (Tuya.buffer[5] == 2) { + uint8_t led1_gpio = Tuya.buffer[6]; + uint8_t key1_gpio = Tuya.buffer[7]; + bool key1_set = false; + bool led1_set = false; + for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { + if (Settings.my_gp.io[i] == GPIO_LED1) led1_set = true; + else if (Settings.my_gp.io[i] == GPIO_KEY1) key1_set = true; + } + if (!Settings.my_gp.io[led1_gpio] && !led1_set) { + Settings.my_gp.io[led1_gpio] = GPIO_LED1; + restart_flag = 2; + } + if (!Settings.my_gp.io[key1_gpio] && !key1_set) { + Settings.my_gp.io[key1_gpio] = GPIO_KEY1; + restart_flag = 2; + } + } + TuyaRequestState(); + break; + + default: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX unknown command")); + } +} + + + + + +bool TuyaModuleSelected(void) +{ + if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { + pin[GPIO_TUYA_TX] = 1; + pin[GPIO_TUYA_RX] = 3; + Settings.my_gp.io[1] = GPIO_TUYA_TX; + Settings.my_gp.io[3] = GPIO_TUYA_RX; + restart_flag = 2; + } + + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) == 0 && TUYA_DIMMER_ID > 0) { + TuyaAddMcuFunc(TUYA_MCU_FUNC_DIMMER, TUYA_DIMMER_ID); + } + + bool relaySet = false; + + for (uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS; i++) { + if ((Settings.tuya_fnid_map[i].fnid >= TUYA_MCU_FUNC_REL1 && Settings.tuya_fnid_map[i].fnid <= TUYA_MCU_FUNC_REL8 ) || + (Settings.tuya_fnid_map[i].fnid >= TUYA_MCU_FUNC_REL1_INV && Settings.tuya_fnid_map[i].fnid <= TUYA_MCU_FUNC_REL8_INV )) { + relaySet = true; + devices_present++; + } + } + + if (!relaySet) { + TuyaAddMcuFunc(TUYA_MCU_FUNC_REL1, 1); + devices_present++; + SettingsSaveAll(); + } + + if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) != 0) { + light_type = LT_SERIAL1; + } else { + light_type = LT_BASIC; + } + + if (TuyaGetDpId(TUYA_MCU_FUNC_LOWPOWER_MODE) != 0) { + Tuya.low_power_mode = true; + Settings.flag3.fast_power_cycle_disable = true; + } + + UpdateDevices(); + return true; +} + +void TuyaInit(void) +{ + Tuya.buffer = (char*)(malloc(TUYA_BUFFER_SIZE)); + if (Tuya.buffer != nullptr) { + TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 2); + if (TuyaSerial->begin(9600)) { + if (TuyaSerial->hardwareSerial()) { ClaimSerial(); } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration")); + + TuyaSendCmd(TUYA_CMD_QUERY_PRODUCT); + } + } + Tuya.heartbeat_timer = 0; +} + +void TuyaSerialInput(void) +{ + while (TuyaSerial->available()) { + yield(); + uint8_t serial_in_byte = TuyaSerial->read(); + + if (serial_in_byte == 0x55) { + Tuya.cmd_status = 1; + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + Tuya.cmd_checksum += serial_in_byte; + } + else if (Tuya.cmd_status == 1 && serial_in_byte == 0xAA) { + Tuya.cmd_status = 2; + + Tuya.byte_counter = 0; + Tuya.buffer[Tuya.byte_counter++] = 0x55; + Tuya.buffer[Tuya.byte_counter++] = 0xAA; + Tuya.cmd_checksum = 0xFF; + } + else if (Tuya.cmd_status == 2) { + if (Tuya.byte_counter == 5) { + Tuya.cmd_status = 3; + Tuya.data_len = serial_in_byte; + } + Tuya.cmd_checksum += serial_in_byte; + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + } + else if ((Tuya.cmd_status == 3) && (Tuya.byte_counter == (6 + Tuya.data_len)) && (Tuya.cmd_checksum == serial_in_byte)) { + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + + char hex_char[(Tuya.byte_counter * 2) + 2]; + uint16_t len = Tuya.buffer[4] << 8 | Tuya.buffer[5]; + Response_P(PSTR("{\"" D_JSON_TUYA_MCU_RECEIVED "\":{\"Data\":\"%s\",\"Cmnd\":%d"), ToHex_P((unsigned char*)Tuya.buffer, Tuya.byte_counter, hex_char, sizeof(hex_char)), Tuya.buffer[3]); + + if (len > 0) { + ResponseAppend_P(PSTR(",\"CmndData\":\"%s\""), ToHex_P((unsigned char*)&Tuya.buffer[6], len, hex_char, sizeof(hex_char))); + if (TUYA_CMD_STATE == Tuya.buffer[3]) { + + + uint8_t dpidStart = 6; + while (dpidStart + 4 < Tuya.byte_counter) { + uint16_t dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3]; + ResponseAppend_P(PSTR(",\"%d\":{\"DpId\":%d,\"DpIdType\":%d,\"DpIdData\":\"%s\""), Tuya.buffer[dpidStart], Tuya.buffer[dpidStart], Tuya.buffer[dpidStart + 1], ToHex_P((unsigned char*)&Tuya.buffer[dpidStart + 4], dpDataLen, hex_char, sizeof(hex_char))); + if (TUYA_TYPE_STRING == Tuya.buffer[dpidStart + 1]) { + ResponseAppend_P(PSTR(",\"Type3Data\":\"%.*s\""), dpDataLen, (char *)&Tuya.buffer[dpidStart + 4]); + } + ResponseAppend_P(PSTR("}")); + dpidStart += dpDataLen + 4; + } + } + } + + ResponseAppend_P(PSTR("}}")); + + if (Settings.flag3.tuya_serial_mqtt_publish) { + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_TUYA_MCU_RECEIVED)); + } else { + AddLog_P(LOG_LEVEL_DEBUG, mqtt_data); + } + XdrvRulesProcess(); + + if (!Tuya.low_power_mode) { + TuyaNormalPowerModePacketProcess(); + } else { + TuyaLowPowerModePacketProcess(); + } + + Tuya.byte_counter = 0; + Tuya.cmd_status = 0; + Tuya.cmd_checksum = 0; + Tuya.data_len = 0; + } + else if (Tuya.byte_counter < TUYA_BUFFER_SIZE -1) { + Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; + Tuya.cmd_checksum += serial_in_byte; + } else { + Tuya.byte_counter = 0; + Tuya.cmd_status = 0; + Tuya.cmd_checksum = 0; + Tuya.data_len = 0; + } + } +} + +bool TuyaButtonPressed(void) +{ + if (!XdrvMailbox.index && ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == Button.last_state[XdrvMailbox.index]))) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Reset GPIO triggered")); + TuyaResetWifi(); + return true; + } + return false; +} + +uint8_t TuyaGetTuyaWifiState(void) { + + uint8_t wifi_state = 0x02; + switch(WifiState()){ + case WIFI_MANAGER: + wifi_state = 0x01; + break; + case WIFI_RESTART: + wifi_state = 0x03; + break; + } + + if (MqttIsConnected()) { + wifi_state = 0x04; + } + + return wifi_state; +} + +void TuyaSetWifiLed(void) +{ + Tuya.wifi_state = TuyaGetTuyaWifiState(); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Set WiFi LED %d (%d)"), Tuya.wifi_state, WifiState()); + + if (Tuya.low_power_mode) { + TuyaSendCmd(TUYA_LOW_POWER_CMD_WIFI_STATE, &Tuya.wifi_state, 1); + } else { + TuyaSendCmd(TUYA_CMD_WIFI_STATE, &Tuya.wifi_state, 1); + } +} + +#ifdef USE_ENERGY_SENSOR + + + + +bool Xnrg16(uint8_t function) +{ + bool result = false; + + if (TUYA_DIMMER == my_module_type) { + if (FUNC_PRE_INIT == function) { + if (TuyaGetDpId(TUYA_MCU_FUNC_POWER) != 0) { + if (TuyaGetDpId(TUYA_MCU_FUNC_CURRENT) == 0) { + Energy.current_available = false; + } + if (TuyaGetDpId(TUYA_MCU_FUNC_VOLTAGE) == 0) { + Energy.voltage_available = false; + } + energy_flg = XNRG_16; + } + } + } + return result; +} +#endif + + + + + +bool Xdrv16(uint8_t function) +{ + bool result = false; + + if (TUYA_DIMMER == my_module_type) { + switch (function) { + case FUNC_LOOP: + if (TuyaSerial) { TuyaSerialInput(); } + break; + case FUNC_MODULE_INIT: + result = TuyaModuleSelected(); + break; + case FUNC_PRE_INIT: + TuyaInit(); + break; + case FUNC_SET_DEVICE_POWER: + result = TuyaSetPower(); + break; + case FUNC_BUTTON_PRESSED: + result = TuyaButtonPressed(); + break; + case FUNC_EVERY_SECOND: + if (TuyaSerial && Tuya.wifi_state != TuyaGetTuyaWifiState()) { TuyaSetWifiLed(); } + if (!Tuya.low_power_mode) { + Tuya.heartbeat_timer++; + if (Tuya.heartbeat_timer > 10) { + Tuya.heartbeat_timer = 0; + TuyaSendCmd(TUYA_CMD_HEARTBEAT); + } + } else { + TuyaSendLowPowerSuccessIfNeeded(); + } + break; + case FUNC_SET_CHANNELS: + result = TuyaSetChannels(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kTuyaCommand, TuyaCommand); + break; + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_17_rcswitch.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_17_rcswitch.ino" +#ifdef USE_RC_SWITCH + + + + +#define XDRV_17 17 + +#define D_JSON_RF_PROTOCOL "Protocol" +#define D_JSON_RF_BITS "Bits" +#define D_JSON_RF_DATA "Data" + +#define D_CMND_RFSEND "RFSend" +#define D_JSON_RF_PULSE "Pulse" +#define D_JSON_RF_REPEAT "Repeat" + +const char kRfSendCommands[] PROGMEM = "|" + D_CMND_RFSEND; + +void (* const RfSendCommand[])(void) PROGMEM = { + &CmndRfSend }; + +#include + +RCSwitch mySwitch = RCSwitch(); + +#define RF_TIME_AVOID_DUPLICATE 1000 + +uint32_t rf_lasttime = 0; + +void RfReceiveCheck(void) +{ + if (mySwitch.available()) { + + unsigned long data = mySwitch.getReceivedValue(); + unsigned int bits = mySwitch.getReceivedBitlength(); + int protocol = mySwitch.getReceivedProtocol(); + int delay = mySwitch.getReceivedDelay(); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFR: Data 0x%lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay); + + uint32_t now = millis(); + if ((now - rf_lasttime > RF_TIME_AVOID_DUPLICATE) && (data > 0)) { + rf_lasttime = now; + + char stemp[16]; + if (Settings.flag.rf_receive_decimal) { + snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)data); + } else { + snprintf_P(stemp, sizeof(stemp), PSTR("\"0x%lX\""), (uint32_t)data); + } + ResponseTime_P(PSTR(",\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_RF_DATA "\":%s,\"" D_JSON_RF_BITS "\":%d,\"" D_JSON_RF_PROTOCOL "\":%d,\"" D_JSON_RF_PULSE "\":%d}}"), + stemp, bits, protocol, delay); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED)); + XdrvRulesProcess(); +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_COUNT, data); +#endif + } + mySwitch.resetAvailable(); + } +} + +void RfInit(void) +{ + if (pin[GPIO_RFSEND] < 99) { + mySwitch.enableTransmit(pin[GPIO_RFSEND]); + } + if (pin[GPIO_RFRECV] < 99) { + pinMode( pin[GPIO_RFRECV], INPUT); + mySwitch.enableReceive(pin[GPIO_RFRECV]); + } +} + + + + + +void CmndRfSend(void) +{ + bool error = false; + + if (XdrvMailbox.data_len) { + unsigned long data = 0; + unsigned int bits = 24; + int protocol = 1; + int repeat = 10; + int pulse = 350; + + char dataBufUc[XdrvMailbox.data_len + 1]; + UpperCase(dataBufUc, XdrvMailbox.data); + StaticJsonBuffer<150> jsonBuf; + JsonObject &root = jsonBuf.parseObject(dataBufUc); + if (root.success()) { + + char parm_uc[10]; + data = strtoul(root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_DATA))], nullptr, 0); + bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_BITS))]; + protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PROTOCOL))]; + repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_REPEAT))]; + pulse = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PULSE))]; + } else { + + char *p; + uint8_t i = 0; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 5; str = strtok_r(nullptr, ", ", &p)) { + switch (i++) { + case 0: + data = strtoul(str, nullptr, 0); + break; + case 1: + bits = atoi(str); + break; + case 2: + protocol = atoi(str); + break; + case 3: + repeat = atoi(str); + break; + case 4: + pulse = atoi(str); + } + } + } + + if (!protocol) { protocol = 1; } + mySwitch.setProtocol(protocol); + if (!pulse) { pulse = 350; } + mySwitch.setPulseLength(pulse); + if (!repeat) { repeat = 10; } + mySwitch.setRepeatTransmit(repeat); + if (!bits) { bits = 24; } + if (data) { + mySwitch.send(data, bits); + ResponseCmndDone(); + } else { + error = true; + } + } else { + error = true; + } + if (error) { + Response_P(PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_NO " " D_JSON_RF_DATA ", " D_JSON_RF_BITS ", " D_JSON_RF_PROTOCOL ", " D_JSON_RF_REPEAT " " D_JSON_OR " " D_JSON_RF_PULSE "\"}")); + } +} + + + + + +bool Xdrv17(uint8_t function) +{ + bool result = false; + + if ((pin[GPIO_RFSEND] < 99) || (pin[GPIO_RFRECV] < 99)) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (pin[GPIO_RFRECV] < 99) { + RfReceiveCheck(); + } + break; + case FUNC_COMMAND: + if (pin[GPIO_RFSEND] < 99) { + result = DecodeCommand(kRfSendCommands, RfSendCommand); + } + break; + case FUNC_INIT: + RfInit(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_18_armtronix_dimmers.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_18_armtronix_dimmers.ino" +#ifdef USE_LIGHT +#ifdef USE_ARMTRONIX_DIMMERS + + + + + + + +#define XDRV_18 18 + +#include + +TasmotaSerial *ArmtronixSerial = nullptr; + +struct ARMTRONIX { + bool ignore_dim = false; + int8_t wifi_state = -2; + int8_t dim_state[2]; + int8_t knob_state[2]; +} Armtronix; + + + + + +bool ArmtronixSetChannels(void) +{ + LightSerial2Duty(((uint8_t*)XdrvMailbox.data)[0], ((uint8_t*)XdrvMailbox.data)[1]); + return true; +} + +void LightSerial2Duty(uint8_t duty1, uint8_t duty2) +{ + if (ArmtronixSerial && !Armtronix.ignore_dim) { + duty1 = ((float)duty1)/2.575757; + duty2 = ((float)duty2)/2.575757; + Armtronix.dim_state[0] = duty1; + Armtronix.dim_state[1] = duty2; + ArmtronixSerial->print("Dimmer1:"); + ArmtronixSerial->print(duty1); + ArmtronixSerial->print("\nDimmer2:"); + ArmtronixSerial->println(duty2); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Serial Packet Dim Values=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]); + + } else { + Armtronix.ignore_dim = false; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Dim Level skipped due to already set. Value=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]); + + } +} + +void ArmtronixRequestState(void) +{ + if (ArmtronixSerial) { + + AddLog_P(LOG_LEVEL_DEBUG, PSTR("ARM: Request MCU state")); + ArmtronixSerial->println("Status"); + + } +} + + + + + +bool ArmtronixModuleSelected(void) +{ + devices_present++; + light_type = LT_SERIAL2; + return true; +} + +void ArmtronixInit(void) +{ + Armtronix.dim_state[0] = -1; + Armtronix.dim_state[1] = -1; + Armtronix.knob_state[0] = -1; + Armtronix.knob_state[1] = -1; + ArmtronixSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + if (ArmtronixSerial->begin(115200)) { + if (ArmtronixSerial->hardwareSerial()) { ClaimSerial(); } + ArmtronixSerial->println("Status"); + } +} + +void ArmtronixSerialInput(void) +{ + String answer; + int8_t newDimState[2]; + uint8_t temp; + int commaIndex; + char scmnd[20]; + if (ArmtronixSerial->available()) { + yield(); + answer = ArmtronixSerial->readStringUntil('\n'); + if (answer.substring(0,7) == "Status:") { + commaIndex = 6; + for (uint32_t i =0; i<2; i++) { + newDimState[i] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + if (newDimState[i] != Armtronix.dim_state[i]) { + temp = ((float)newDimState[i])*1.01010101010101; + Armtronix.dim_state[i] = newDimState[i]; + Armtronix.ignore_dim = true; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "%d %d"),i+1, temp); + ExecuteCommand(scmnd,SRC_SWITCH); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send CMND_CHANNEL=%s"), scmnd ); + } + commaIndex = answer.indexOf(',',commaIndex+1); + } + Armtronix.knob_state[0] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + commaIndex = answer.indexOf(',',commaIndex+1); + Armtronix.knob_state[1] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); + } + } +} + +void ArmtronixSetWifiLed(void) +{ + uint8_t wifi_state = 0x02; + + switch (WifiState()) { + case WIFI_MANAGER: + wifi_state = 0x01; + break; + case WIFI_RESTART: + wifi_state = 0x03; + break; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Set WiFi LED to state %d (%d)"), wifi_state, WifiState()); + + char state = '0' + ((wifi_state & 1) > 0); + ArmtronixSerial->print("Setled:"); + ArmtronixSerial->write(state); + ArmtronixSerial->write(','); + state = '0' + ((wifi_state & 2) > 0); + ArmtronixSerial->write(state); + ArmtronixSerial->write(10); + Armtronix.wifi_state = WifiState(); +} + + + + + +bool Xdrv18(uint8_t function) +{ + bool result = false; + + if (ARMTRONIX_DIMMERS == my_module_type) { + switch (function) { + case FUNC_LOOP: + if (ArmtronixSerial) { ArmtronixSerialInput(); } + break; + case FUNC_MODULE_INIT: + result = ArmtronixModuleSelected(); + break; + case FUNC_INIT: + ArmtronixInit(); + break; + case FUNC_EVERY_SECOND: + if (ArmtronixSerial) { + if (Armtronix.wifi_state!=WifiState()) { ArmtronixSetWifiLed(); } + if (uptime &1) { + ArmtronixSerial->println("Status"); + } + } + break; + case FUNC_SET_CHANNELS: + result = ArmtronixSetChannels(); + break; + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_19_ps16dz_dimmer.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_19_ps16dz_dimmer.ino" +#ifdef USE_LIGHT +#ifdef USE_PS_16_DZ + + + + +#define XDRV_19 19 + +#define PS16DZ_BUFFER_SIZE 80 + +#include + +TasmotaSerial *PS16DZSerial = nullptr; + +struct PS16DZ { + char *rx_buffer = nullptr; + int byte_counter = 0; + uint8_t dimmer = 0; +} Ps16dz; + + + + + +void PS16DZSerialSend(const char *tx_buffer) +{ + + + PS16DZSerial->print(tx_buffer); + PS16DZSerial->write(0x1B); + PS16DZSerial->flush(); +} + +void PS16DZSerialSendOk(void) +{ + char tx_buffer[16]; + snprintf_P(tx_buffer, sizeof(tx_buffer), PSTR("AT+SEND=ok")); + PS16DZSerialSend(tx_buffer); +} + + + + +void PS16DZSerialSendUpdateCommand(void) +{ + uint8_t light_state_dimmer = light_state.getDimmer(); + + light_state_dimmer = (light_state_dimmer < Settings.dimmer_hw_min) ? Settings.dimmer_hw_min : light_state_dimmer; + light_state_dimmer = (light_state_dimmer > Settings.dimmer_hw_max) ? Settings.dimmer_hw_max : light_state_dimmer; + + char tx_buffer[80]; + snprintf_P(tx_buffer, sizeof(tx_buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"bright\":%d"), + LocalTime(), millis()%1000, power?"on":"off", light_state_dimmer); + + PS16DZSerialSend(tx_buffer); +} + + + + + +void PS16DZSerialInput(void) +{ + char scmnd[20]; + while (PS16DZSerial->available()) { + yield(); + uint8_t serial_in_byte = PS16DZSerial->read(); + if (serial_in_byte != 0x1B) { + if (Ps16dz.byte_counter >= PS16DZ_BUFFER_SIZE - 1) { + memset(Ps16dz.rx_buffer, 0, PS16DZ_BUFFER_SIZE); + Ps16dz.byte_counter = 0; + } + if (Ps16dz.byte_counter || (!Ps16dz.byte_counter && ('A' == serial_in_byte))) { + Ps16dz.rx_buffer[Ps16dz.byte_counter++] = serial_in_byte; + } + } else { + Ps16dz.rx_buffer[Ps16dz.byte_counter++] = 0x00; + + + + + if (!strncmp(Ps16dz.rx_buffer+3, "RESULT", 6)) { + + } + else if (!strncmp(Ps16dz.rx_buffer+3, "UPDATE", 6)) { + + char *end_str; + char *string = Ps16dz.rx_buffer+10; + char *token = strtok_r(string, ",", &end_str); + + bool is_switch_change = false; + bool is_brightness_change = false; + + while (token != nullptr) { + char* end_token; + char* token2 = strtok_r(token, ":", &end_token); + char* token3 = strtok_r(nullptr, ":", &end_token); + + if (!strncmp(token2, "\"switch\"", 8)) { + bool switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; + + + + is_switch_change = (switch_state != power); + if (is_switch_change) { + ExecuteCommandPower(1, switch_state, SRC_SWITCH); + } + } + else if (!strncmp(token2, "\"bright\"", 8)) { + Ps16dz.dimmer = atoi(token3); + + + + is_brightness_change = Ps16dz.dimmer != Settings.light_dimmer; + if (power && (Ps16dz.dimmer > 0) && is_brightness_change) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), Ps16dz.dimmer); + ExecuteCommand(scmnd, SRC_SWITCH); + } + } + else if (!strncmp(token2, "\"sequence\"", 10)) { + + + + } + token = strtok_r(nullptr, ",", &end_str); + } + + if (!is_brightness_change) { + + + + PS16DZSerialSendOk(); + } + } + else if (!strncmp(Ps16dz.rx_buffer+3, "SETTING", 7)) { + + + if (!Settings.flag.button_restrict) { + int state = WIFI_MANAGER; + if (!strncmp(Ps16dz.rx_buffer+10, "=exit", 5)) { state = WIFI_RETRY; } + if (state != Settings.sta_config) { + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " %d"), state); + ExecuteCommand(scmnd, SRC_BUTTON); + } + } + } + memset(Ps16dz.rx_buffer, 0, PS16DZ_BUFFER_SIZE); + Ps16dz.byte_counter = 0; + } + } +} + +bool PS16DZSerialSendUpdateCommandIfRequired(void) +{ + if (!PS16DZSerial) { return true; } + + bool is_switch_change = (XdrvMailbox.payload != SRC_SWITCH); + bool is_brightness_change = (light_state.getDimmer() != Ps16dz.dimmer); + + if (is_switch_change || is_brightness_change) { + PS16DZSerialSendUpdateCommand(); + } + + return true; +} + +void PS16DZInit(void) +{ + Ps16dz.rx_buffer = (char*)(malloc(PS16DZ_BUFFER_SIZE)); + if (Ps16dz.rx_buffer != nullptr) { + PS16DZSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + if (PS16DZSerial->begin(19200)) { + if (PS16DZSerial->hardwareSerial()) { ClaimSerial(); } + } + } +} + +bool PS16DZModuleSelected(void) +{ + devices_present++; + light_type = LT_SERIAL1; + + return true; +} + + + + + +bool Xdrv19(uint8_t function) +{ + bool result = false; + + if (PS_16_DZ == my_module_type) { + switch (function) { + case FUNC_LOOP: + if (PS16DZSerial) { PS16DZSerialInput(); } + break; + case FUNC_SET_DEVICE_POWER: + case FUNC_SET_CHANNELS: + result = PS16DZSerialSendUpdateCommandIfRequired(); + break; + case FUNC_INIT: + PS16DZInit(); + break; + case FUNC_MODULE_INIT: + result = PS16DZModuleSelected(); + break; + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_20_hue.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_20_hue.ino" +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) +# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_20_hue.ino" +#define XDRV_20 20 + +const char HUE_RESPONSE[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "HOST: 239.255.255.250:1900\r\n" + "CACHE-CONTROL: max-age=100\r\n" + "EXT:\r\n" + "LOCATION: http://%s:80/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.24.0\r\n" + "hue-bridgeid: %s\r\n"; +const char HUE_ST1[] PROGMEM = + "ST: upnp:rootdevice\r\n" + "USN: uuid:%s::upnp:rootdevice\r\n" + "\r\n"; +const char HUE_ST2[] PROGMEM = + "ST: uuid:%s\r\n" + "USN: uuid:%s\r\n" + "\r\n"; +const char HUE_ST3[] PROGMEM = + "ST: urn:schemas-upnp-org:device:basic:1\r\n" + "USN: uuid:%s\r\n" + "\r\n"; + +String HueBridgeId(void) +{ + String temp = WiFi.macAddress(); + temp.replace(":", ""); + String bridgeid = temp.substring(0, 6) + "FFFE" + temp.substring(6); + return bridgeid; +} + +String HueSerialnumber(void) +{ + String serial = WiFi.macAddress(); + serial.replace(":", ""); + serial.toLowerCase(); + return serial; +} + +String HueUuid(void) +{ + String uuid = F("f6543a06-da50-11ba-8d8f-"); + uuid += HueSerialnumber(); + return uuid; +} + +void HueRespondToMSearch(void) +{ + char message[TOPSZ]; + + TickerMSearch.detach(); + if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) { + char response[320]; + snprintf_P(response, sizeof(response), HUE_RESPONSE, WiFi.localIP().toString().c_str(), HueBridgeId().c_str()); + int len = strlen(response); + + snprintf_P(response + len, sizeof(response) - len, HUE_ST1, HueUuid().c_str()); + PortUdp.write(response); + PortUdp.endPacket(); + + snprintf_P(response + len, sizeof(response) - len, HUE_ST2, HueUuid().c_str(), HueUuid().c_str()); + PortUdp.write(response); + PortUdp.endPacket(); + + snprintf_P(response + len, sizeof(response) - len, HUE_ST3, HueUuid().c_str()); + PortUdp.write(response); + PortUdp.endPacket(); + + snprintf_P(message, sizeof(message), PSTR(D_3_RESPONSE_PACKETS_SENT)); + } else { + snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE)); + } + + PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_HUE " %s " D_TO " %s:%d"), + message, udp_remote_ip.toString().c_str(), udp_remote_port); + + udp_response_mutex = false; +} + + + + + +const char HUE_DESCRIPTION_XML[] PROGMEM = + "" + "" + "" + "1" + "0" + "" + + "http://{x1:80/" + "" + "urn:schemas-upnp-org:device:Basic:1" + "Amazon-Echo-HA-Bridge ({x1)" + + "Royal Philips Electronics" + "http://www.philips.com" + "Philips hue Personal Wireless Lighting" + "Philips hue bridge 2012" + "929000226503" + "{x3" + "uuid:{x2" + "" + "\r\n" + "\r\n"; +const char HUE_LIGHTS_STATUS_JSON1[] PROGMEM = + "{\"on\":{state}," + "{light_status}" + "\"alert\":\"none\"," + "\"effect\":\"none\"," + "\"reachable\":true}"; +const char HUE_LIGHTS_STATUS_JSON2[] PROGMEM = + ",\"type\":\"Extended color light\"," + "\"name\":\"{j1\"," + "\"modelid\":\"LCT007\"," + "\"uniqueid\":\"{j2\"," + "\"swversion\":\"5.50.1.19085\"}"; +const char HUE_GROUP0_STATUS_JSON[] PROGMEM = + "{\"name\":\"Group 0\"," + "\"lights\":[{l1]," + "\"type\":\"LightGroup\"," + "\"action\":"; + +const char HueConfigResponse_JSON[] PROGMEM = + "{\"name\":\"Philips hue\"," + "\"mac\":\"{ma\"," + "\"dhcp\":true," + "\"ipaddress\":\"{ip\"," + "\"netmask\":\"{ms\"," + "\"gateway\":\"{gw\"," + "\"proxyaddress\":\"none\"," + "\"proxyport\":0," + "\"bridgeid\":\"{br\"," + "\"UTC\":\"{dt\"," + "\"whitelist\":{\"{id\":{" + "\"last use date\":\"{dt\"," + "\"create date\":\"{dt\"," + "\"name\":\"Remote\"}}," + "\"swversion\":\"01041302\"," + "\"apiversion\":\"1.17.0\"," + "\"swupdate\":{\"updatestate\":0,\"url\":\"\",\"text\":\"\",\"notify\": false}," + "\"linkbutton\":false," + "\"portalservices\":false" + "}"; +const char HUE_LIGHT_RESPONSE_JSON[] PROGMEM = + "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; +const char HUE_ERROR_JSON[] PROGMEM = + "[{\"error\":{\"type\":901,\"address\":\"/\",\"description\":\"Internal Error\"}}]"; + + + +String GetHueDeviceId(uint8_t id) +{ + String deviceid = WiFi.macAddress() + F(":00:11-") + String(id); + deviceid.toLowerCase(); + return deviceid; +} + +String GetHueUserId(void) +{ + char userid[7]; + + snprintf_P(userid, sizeof(userid), PSTR("%03x"), ESP.getChipId()); + return String(userid); +} + +void HandleUpnpSetupHue(void) +{ + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_HUE_BRIDGE_SETUP)); + String description_xml = FPSTR(HUE_DESCRIPTION_XML); + description_xml.replace("{x1", WiFi.localIP().toString()); + description_xml.replace("{x2", HueUuid()); + description_xml.replace("{x3", HueSerialnumber()); + WSSend(200, CT_XML, description_xml); +} + +void HueNotImplemented(String *path) +{ + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API_NOT_IMPLEMENTED " (%s)"), path->c_str()); + + WSSend(200, CT_JSON, "{}"); +} + +void HueConfigResponse(String *response) +{ + *response += FPSTR(HueConfigResponse_JSON); + response->replace("{ma", WiFi.macAddress()); + response->replace("{ip", WiFi.localIP().toString()); + response->replace("{ms", WiFi.subnetMask().toString()); + response->replace("{gw", WiFi.gatewayIP().toString()); + response->replace("{br", HueBridgeId()); + response->replace("{dt", GetDateAndTime(DT_UTC)); + response->replace("{id", GetHueUserId()); +} + +void HueConfig(String *path) +{ + String response = ""; + HueConfigResponse(&response); + WSSend(200, CT_JSON, response); +} + + + +bool g_gotct = false; + + + + +uint16_t prev_hue = 0; +uint8_t prev_sat = 0; +uint8_t prev_bri = 254; +uint16_t prev_ct = 254; +char prev_x_str[24] = "\0"; +char prev_y_str[24] = "\0"; + +uint8_t getLocalLightSubtype(uint8_t device) { + if (light_type) { + if (device >= Light.device) { + if (Settings.flag3.pwm_multi_channels) { + return LST_SINGLE; + } else { + return Light.subtype; + } + } else { + return LST_NONE; + } + } else { + return LST_NONE; + } +} + +void HueLightStatus1(uint8_t device, String *response) +{ + uint16_t ct = 0; + uint8_t color_mode; + String light_status = ""; + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = 254; + uint32_t echo_gen = findEchoGeneration(); + + + uint8_t local_light_subtype = getLocalLightSubtype(device); + + bri = LightGetBri(device); + if (bri > 254) bri = 254; + if (bri < 1) bri = 1; + +#ifdef USE_SHUTTER + if (ShutterState(device)) { + bri = (float)((Settings.shutter_options[device-1] & 1) ? 100 - Settings.shutter_position[device-1] : Settings.shutter_position[device-1]) / 100; + } +#endif + + if (light_type) { + light_state.getHSB(&hue, &sat, nullptr); + + if ((bri > prev_bri ? bri - prev_bri : prev_bri - bri) < 1) + bri = prev_bri; + + if (sat > 254) sat = 254; + if ((sat > prev_sat ? sat - prev_sat : prev_sat - sat) < 1) { + sat = prev_sat; + } else { + prev_x_str[0] = prev_y_str[0] = 0; + } + + hue = changeUIntScale(hue, 0, 359, 0, 65535); + if ((hue > prev_hue ? hue - prev_hue : prev_hue - hue) < 400) { + hue = prev_hue; + } else { + prev_x_str[0] = prev_y_str[0] = 0; + } + + color_mode = light_state.getColorMode(); + ct = light_state.getCT(); + if (LCM_RGB == color_mode) { g_gotct = false; } + if (LCM_CT == color_mode) { g_gotct = true; } + + + + if ((ct > prev_ct ? ct - prev_ct : prev_ct - ct) < 1) + ct = prev_ct; + + + + } + + *response += FPSTR(HUE_LIGHTS_STATUS_JSON1); + response->replace("{state}", (power & (1 << (device-1))) ? "true" : "false"); + + if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { + light_status += "\"bri\":"; + light_status += String(bri); + light_status += ","; + } + if (LST_COLDWARM <= local_light_subtype) { + light_status += F("\"colormode\":\""); + light_status += (g_gotct ? "ct" : "hs"); + light_status += "\","; + } + if (LST_RGB <= local_light_subtype) { + if (prev_x_str[0] && prev_y_str[0]) { + light_status += "\"xy\":["; + light_status += prev_x_str; + light_status += ","; + light_status += prev_y_str; + light_status += "],"; + } else { + float x, y; + light_state.getXY(&x, &y); + light_status += "\"xy\":["; + light_status += String(x, 5); + light_status += ","; + light_status += String(y, 5); + light_status += "],"; + } + light_status += "\"hue\":"; + light_status += String(hue); + light_status += ","; + + light_status += "\"sat\":"; + light_status += String(sat); + light_status += ","; + } + if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { + light_status += "\"ct\":"; + light_status += String(ct > 0 ? ct : 284); + light_status += ","; + } + response->replace("{light_status}", light_status); +} + + + +bool HueActive(uint8_t device) { + if (device > MAX_FRIENDLYNAMES) { device = MAX_FRIENDLYNAMES; } + return '$' != *SettingsText(SET_FRIENDLYNAME1 +device -1); +} + +void HueLightStatus2(uint8_t device, String *response) +{ + *response += FPSTR(HUE_LIGHTS_STATUS_JSON2); + if (device <= MAX_FRIENDLYNAMES) { + response->replace("{j1", SettingsText(SET_FRIENDLYNAME1 +device -1)); + } else { + char fname[33]; + strcpy(fname, SettingsText(SET_FRIENDLYNAME1 + MAX_FRIENDLYNAMES -1)); + uint32_t fname_len = strlen(fname); + if (fname_len > 30) { fname_len = 30; } + fname[fname_len++] = '-'; + if (device - MAX_FRIENDLYNAMES < 10) { + fname[fname_len++] = '0' + device - MAX_FRIENDLYNAMES; + } else { + fname[fname_len++] = 'A' + device - MAX_FRIENDLYNAMES - 10; + } + fname[fname_len] = 0x00; + + response->replace("{j1", fname); + } + response->replace("{j2", GetHueDeviceId(device)); +} + + + + +uint32_t EncodeLightId(uint8_t relay_id) +{ + uint8_t mac[6]; + WiFi.macAddress(mac); + uint32_t id = 0; + + if (relay_id >= 32) { + relay_id = 0; + } + if (relay_id > 15) { + id = (1 << 28); + } + + id |= (mac[3] << 20) | (mac[4] << 12) | (mac[5] << 4) | (relay_id & 0xF); + return id; +} + + + +uint32_t DecodeLightId(uint32_t hue_id) { + uint8_t relay_id = hue_id & 0xF; + if (hue_id & (1 << 28)) { + relay_id += 16; + } + if (0 == relay_id) { + relay_id = 32; + } + return relay_id; +} + +static const char * FIRST_GEN_UA[] = { + "AEOBC", +}; + + +uint32_t findEchoGeneration(void) { + + String user_agent = WebServer->header("User-Agent"); + uint32_t gen = 2; + + for (uint32_t i = 0; i < sizeof(FIRST_GEN_UA)/sizeof(char*); i++) { + if (user_agent.indexOf(FIRST_GEN_UA[i]) >= 0) { + gen = 1; + break; + } + } + if (0 == user_agent.length()) { + gen = 1; + } + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, D_LOG_HTTP D_HUE " User-Agent: %s, gen=%d", user_agent.c_str(), gen); + + return gen; +} + +void HueGlobalConfig(String *path) { + String response; + uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; + + path->remove(0,1); + response = F("{\"lights\":{"); + bool appending = false; + for (uint32_t i = 1; i <= maxhue; i++) { + if (HueActive(i)) { + if (appending) { response += ","; } + response += "\""; + response += EncodeLightId(i); + response += F("\":{\"state\":"); + HueLightStatus1(i, &response); + HueLightStatus2(i, &response); + appending = true; + } + } + response += F("},\"groups\":{},\"schedules\":{},\"config\":"); + HueConfigResponse(&response); + response += "}"; + WSSend(200, CT_JSON, response); +} + +void HueAuthentication(String *path) +{ + char response[38]; + + snprintf_P(response, sizeof(response), PSTR("[{\"success\":{\"username\":\"%s\"}}]"), GetHueUserId().c_str()); + WSSend(200, CT_JSON, response); +} + +void HueLights(String *path) +{ + + + + String response; + int code = 200; + uint16_t tmp = 0; + uint16_t hue = 0; + uint8_t sat = 0; + uint8_t bri = 254; + uint16_t ct = 0; + bool resp = false; + bool on = false; + bool change = false; + uint8_t device = 1; + uint8_t local_light_subtype = Light.subtype; + uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; + + path->remove(0,path->indexOf("/lights")); + if (path->endsWith("/lights")) { + response = "{"; + bool appending = false; + for (uint32_t i = 1; i <= maxhue; i++) { + if (HueActive(i)) { + if (appending) { response += ","; } + response += "\""; + response += EncodeLightId(i); + response += F("\":{\"state\":"); + HueLightStatus1(i, &response); + HueLightStatus2(i, &response); + appending = true; + } + } +#ifdef USE_SCRIPT_HUE + Script_Check_Hue(&response); +#endif + response += "}"; + } + else if (path->endsWith("/state")) { + path->remove(0,8); + path->remove(path->indexOf("/state")); + device = DecodeLightId(atoi(path->c_str())); + +#ifdef USE_SCRIPT_HUE + if (device>devices_present) { + return Script_Handle_Hue(path); + } +#endif + + if ((device < 1) || (device > maxhue)) { + device = 1; + } + local_light_subtype = getLocalLightSubtype(device); + + if (WebServer->args()) { + response = "["; + + StaticJsonBuffer<400> jsonBuffer; + JsonObject &hue_json = jsonBuffer.parseObject(WebServer->arg((WebServer->args())-1)); + if (hue_json.containsKey("on")) { + + response += FPSTR(HUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(EncodeLightId(device))); + response.replace("{cm", "on"); + +#ifdef USE_SHUTTER + if (ShutterState(device)) { + if (!change) { + on = hue_json["on"]; + bri = on ? 1.0f : 0.0f; + change = true; + } + response.replace("{re", on ? "true" : "false"); + } else { +#endif + on = hue_json["on"]; + switch(on) + { + case false : ExecuteCommandPower(device, POWER_OFF, SRC_HUE); + response.replace("{re", "false"); + break; + case true : ExecuteCommandPower(device, POWER_ON, SRC_HUE); + response.replace("{re", "true"); + break; + default : response.replace("{re", (power & (1 << (device-1))) ? "true" : "false"); + break; + } + resp = true; +#ifdef USE_SHUTTER + } +#endif + } + + if (light_type && (local_light_subtype >= LST_SINGLE)) { + if (!Settings.flag3.pwm_multi_channels) { + light_state.getHSB(&hue, &sat, nullptr); + bri = light_state.getBri(); + ct = light_state.getCT(); + uint8_t color_mode = light_state.getColorMode(); + if (LCM_RGB == color_mode) { g_gotct = false; } + if (LCM_CT == color_mode) { g_gotct = true; } + + } else { + bri = LightGetBri(device); + } + } + prev_x_str[0] = prev_y_str[0] = 0; + + if (hue_json.containsKey("bri")) { + tmp = hue_json["bri"]; + prev_bri = bri = tmp; + + if (254 <= bri) { bri = 255; } + if (resp) { response += ","; } + response += FPSTR(HUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(device)); + response.replace("{cm", "bri"); + response.replace("{re", String(tmp)); + if (LST_SINGLE <= Light.subtype) { + change = true; + } + resp = true; + } + + + if (hue_json.containsKey("xy")) { + float x, y; + x = hue_json["xy"][0]; + y = hue_json["xy"][1]; + const String &x_str = hue_json["xy"][0]; + const String &y_str = hue_json["xy"][1]; + x_str.toCharArray(prev_x_str, sizeof(prev_x_str)); + y_str.toCharArray(prev_y_str, sizeof(prev_y_str)); + + uint8_t rr,gg,bb; + LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); + LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); + prev_hue = changeUIntScale(hue, 0, 359, 0, 65535); + prev_sat = (sat > 254 ? 254 : sat); + + if (resp) { response += ","; } + response += FPSTR(HUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(device)); + response.replace("{cm", "xy"); + response.replace("{re", "[" + x_str + "," + y_str + "]"); + g_gotct = false; + resp = true; + change = true; + } + if (hue_json.containsKey("hue")) { + tmp = hue_json["hue"]; + prev_hue = tmp; + + hue = changeUIntScale(tmp, 0, 65535, 0, 359); + if (resp) { response += ","; } + response += FPSTR(HUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(device)); + response.replace("{cm", "hue"); + response.replace("{re", String(tmp)); + if (LST_RGB <= Light.subtype) { + g_gotct = false; + change = true; + } + resp = true; + } + if (hue_json.containsKey("sat")) { + tmp = hue_json["sat"]; + prev_sat = sat = tmp; + + if (254 <= sat) { sat = 255; } + if (resp) { response += ","; } + response += FPSTR(HUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(device)); + response.replace("{cm", "sat"); + response.replace("{re", String(tmp)); + if (LST_RGB <= Light.subtype) { + g_gotct = false; + change = true; + } + resp = true; + } + if (hue_json.containsKey("ct")) { + ct = hue_json["ct"]; + prev_ct = ct; + if (resp) { response += ","; } + response += FPSTR(HUE_LIGHT_RESPONSE_JSON); + response.replace("{id", String(device)); + response.replace("{cm", "ct"); + response.replace("{re", String(ct)); + if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) { + g_gotct = true; + change = true; + } + resp = true; + } + if (change) { +#ifdef USE_SHUTTER + if (ShutterState(device)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Settings.shutter_invert: %d"), Settings.shutter_options[device-1] & 1); + ShutterSetPosition(device, bri * 100.0f ); + } else +#endif + if (light_type && (local_light_subtype > LST_NONE)) { + if (!Settings.flag3.pwm_multi_channels) { + if (g_gotct) { + light_controller.changeCTB(ct, bri); + } else { + light_controller.changeHSB(hue, sat, bri); + } + LightPreparePower(); + } else { + LightSetBri(device, bri); + } + if (LST_COLDWARM <= local_light_subtype) { + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_COLOR)); + } else { + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_DIMMER)); + } + XdrvRulesProcess(); + } + change = false; + } + response += "]"; + if (2 == response.length()) { + response = FPSTR(HUE_ERROR_JSON); + } + } + else { + response = FPSTR(HUE_ERROR_JSON); + } + } + else if(path->indexOf("/lights/") >= 0) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, "/lights path=%s", path->c_str()); + path->remove(0,8); + device = DecodeLightId(atoi(path->c_str())); + +#ifdef USE_SCRIPT_HUE + if (device>devices_present) { + Script_HueStatus(&response,device-devices_present-1); + goto exit; +} +#endif + + if ((device < 1) || (device > maxhue)) { + device = 1; + } + response += F("{\"state\":"); + HueLightStatus1(device, &response); + HueLightStatus2(device, &response); + } + else { + response = "{}"; + code = 406; + } + exit: + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); + WSSend(code, CT_JSON, response); +} + +void HueGroups(String *path) +{ + + + + String response = "{}"; + uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; + + if (path->endsWith("/0")) { + response = FPSTR(HUE_GROUP0_STATUS_JSON); + String lights = F("\"1\""); + for (uint32_t i = 2; i <= maxhue; i++) { + lights += ",\""; + lights += EncodeLightId(i); + lights += "\""; + } + response.replace("{l1", lights); + HueLightStatus1(1, &response); + response += F("}"); + } + + WSSend(200, CT_JSON, response); +} + +void HandleHueApi(String *path) +{ +# 784 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_20_hue.ino" + uint8_t args = 0; + + path->remove(0, 4); + uint16_t apilen = path->length(); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API " (%s)"), path->c_str()); + for (args = 0; args < WebServer->args(); args++) { + String json = WebServer->arg(args); + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_POST_ARGS " (%s)"), json.c_str()); + } + + if (path->endsWith("/invalid/")) {} + else if (!apilen) HueAuthentication(path); + else if (path->endsWith("/")) HueAuthentication(path); + else if (path->endsWith("/config")) HueConfig(path); + else if (path->indexOf("/lights") >= 0) HueLights(path); + else if (path->indexOf("/groups") >= 0) HueGroups(path); + else if (path->endsWith("/schedules")) HueNotImplemented(path); + else if (path->endsWith("/sensors")) HueNotImplemented(path); + else if (path->endsWith("/scenes")) HueNotImplemented(path); + else if (path->endsWith("/rules")) HueNotImplemented(path); + else if (path->endsWith("/resourcelinks")) HueNotImplemented(path); + else HueGlobalConfig(path); +} + + + + + +bool Xdrv20(uint8_t function) +{ + bool result = false; + +#ifdef USE_SCRIPT_HUE + if ((EMUL_HUE == Settings.flag2.emulation)) { +#else + if (devices_present && (EMUL_HUE == Settings.flag2.emulation)) { +#endif + switch (function) { + case FUNC_WEB_ADD_HANDLER: + WebServer->on("/description.xml", HandleUpnpSetupHue); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_21_wemo.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_21_wemo.ino" +#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined (USE_EMULATION_WEMO) + + + + +#define XDRV_21 21 + +const char WEMO_MSEARCH[] PROGMEM = + "HTTP/1.1 200 OK\r\n" + "CACHE-CONTROL: max-age=86400\r\n" + "DATE: Fri, 15 Apr 2016 04:56:29 GMT\r\n" + "EXT:\r\n" + "LOCATION: http://%s:80/setup.xml\r\n" + "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" + "01-NLS: b9200ebb-736d-4b93-bf03-835149d13983\r\n" + "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n" + "ST: %s\r\n" + "USN: uuid:%s::%s\r\n" + "X-User-Agent: redsonic\r\n" + "\r\n"; + +String WemoSerialnumber(void) +{ + char serial[16]; + + snprintf_P(serial, sizeof(serial), PSTR("201612K%08X"), ESP.getChipId()); + return String(serial); +} + +String WemoUuid(void) +{ + char uuid[27]; + + snprintf_P(uuid, sizeof(uuid), PSTR("Socket-1_0-%s"), WemoSerialnumber().c_str()); + return String(uuid); +} + +void WemoRespondToMSearch(int echo_type) +{ + char message[TOPSZ]; + + TickerMSearch.detach(); + if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) { + char type[24]; + if (1 == echo_type) { + strcpy_P(type, URN_BELKIN_DEVICE_CAP); + } else { + strcpy_P(type, UPNP_ROOTDEVICE); + } + char response[400]; + snprintf_P(response, sizeof(response), WEMO_MSEARCH, WiFi.localIP().toString().c_str(), type, WemoUuid().c_str(), type); + PortUdp.write(response); + PortUdp.endPacket(); + snprintf_P(message, sizeof(message), PSTR(D_RESPONSE_SENT)); + } else { + snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE)); + } + + PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_WEMO " " D_JSON_TYPE " %d, %s " D_TO " %s:%d"), + echo_type, message, udp_remote_ip.toString().c_str(), udp_remote_port); + + udp_response_mutex = false; +} + + + + + +const char WEMO_EVENTSERVICE_XML[] PROGMEM = + "" + "" + "" + "SetBinaryState" + "" + "" + "" + "BinaryState" + "BinaryState" + "in" + "" + "" + "" + "" + "GetBinaryState" + "" + "" + "" + "BinaryState" + "BinaryState" + "out" + "" + "" + "" + "" + "" + "" + "BinaryState" + "bool" + "0" + "" + "" + "level" + "string" + "0" + "" + "" + "\r\n\r\n"; + +const char WEMO_METASERVICE_XML[] PROGMEM = + "" + "" + "1" + "0" + "" + "" + "" + "GetMetaInfo" + "" + "" + "GetMetaInfo" + "MetaInfo" + "in" + "" + "" + "" + "" + "" + "MetaInfo" + "string" + "0" + "" + "" + "\r\n\r\n"; + +const char WEMO_RESPONSE_STATE_SOAP[] PROGMEM = + "" + "" + "" + "%d" + "" + "" + "\r\n"; + +const char WEMO_SETUP_XML[] PROGMEM = + "" + "" + "" + "urn:Belkin:device:controllee:1" + "{x1" + "Belkin International Inc." + "Socket" + "3.1415" + "uuid:{x2" + "{x3" + "0" + "" + "" + "urn:Belkin:service:basicevent:1" + "urn:Belkin:serviceId:basicevent1" + "/upnp/control/basicevent1" + "/upnp/event/basicevent1" + "/eventservice.xml" + "" + "" + "urn:Belkin:service:metainfo:1" + "urn:Belkin:serviceId:metainfo1" + "/upnp/control/metainfo1" + "/upnp/event/metainfo1" + "/metainfoservice.xml" + "" + "" + "" + "\r\n"; + + + +void HandleUpnpEvent(void) +{ + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_BASIC_EVENT)); + + char event[500]; + strlcpy(event, WebServer->arg(0).c_str(), sizeof(event)); + + + + + char state = 'G'; + if (strstr_P(event, PSTR("SetBinaryState")) != nullptr) { + state = 'S'; + uint8_t power = POWER_TOGGLE; + if (strstr_P(event, PSTR("State>10on("/upnp/control/basicevent1", HTTP_POST, HandleUpnpEvent); + WebServer->on("/eventservice.xml", HandleUpnpService); + WebServer->on("/metainfoservice.xml", HandleUpnpMetaService); + WebServer->on("/setup.xml", HandleUpnpSetupWemo); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" +#ifdef USE_SONOFF_IFAN + + + + +#define XDRV_22 22 + +const uint8_t MAX_FAN_SPEED = 4; + +const uint8_t kIFan02Speed[MAX_FAN_SPEED] = { 0x00, 0x01, 0x03, 0x05 }; +const uint8_t kIFan03Speed[MAX_FAN_SPEED +2] = { 0x00, 0x01, 0x03, 0x04, 0x05, 0x06 }; +const uint8_t kIFan03Sequence[MAX_FAN_SPEED][MAX_FAN_SPEED] = {{0, 2, 2, 2}, {0, 1, 2, 4}, {1, 1, 2, 5}, {4, 4, 5, 3}}; + +const char kSonoffIfanCommands[] PROGMEM = "|" + D_CMND_FANSPEED; + +void (* const SonoffIfanCommand[])(void) PROGMEM = { + &CmndFanspeed }; + +uint8_t ifan_fanspeed_timer = 0; +uint8_t ifan_fanspeed_goal = 0; +bool ifan_receive_flag = false; +bool ifan_restart_flag = true; + + + +bool IsModuleIfan(void) +{ + return ((SONOFF_IFAN02 == my_module_type) || (SONOFF_IFAN03 == my_module_type)); +} + +uint8_t MaxFanspeed(void) +{ + return MAX_FAN_SPEED; +} + +uint8_t GetFanspeed(void) +{ + if (ifan_fanspeed_timer) { + return ifan_fanspeed_goal; + } else { + + + + + + + uint8_t fanspeed = (uint8_t)(power &0xF) >> 1; + if (fanspeed) { fanspeed = (fanspeed >> 1) +1; } + return fanspeed; + } +} + + + +void SonoffIFanSetFanspeed(uint8_t fanspeed, bool sequence) +{ + ifan_fanspeed_timer = 0; + ifan_fanspeed_goal = fanspeed; + + uint8_t fanspeed_now = GetFanspeed(); + + if (fanspeed == fanspeed_now) { return; } + + uint8_t fans = kIFan02Speed[fanspeed]; + if (SONOFF_IFAN03 == my_module_type) { + if (sequence) { + fanspeed = kIFan03Sequence[fanspeed_now][ifan_fanspeed_goal]; + if (fanspeed != ifan_fanspeed_goal) { + if (0 == fanspeed_now) { + ifan_fanspeed_timer = 20; + } else { + ifan_fanspeed_timer = 2; + } + } + } + fans = kIFan03Speed[fanspeed]; + } + for (uint32_t i = 2; i < 5; i++) { + uint8_t state = (fans &1) + POWER_OFF_NO_STATE; + ExecuteCommandPower(i, state, SRC_IGNORE); + fans >>= 1; + } + +#ifdef USE_DOMOTICZ + if (sequence) { DomoticzUpdateFanState(); } +#endif +} + + + +void SonoffIfanReceived(void) +{ + char svalue[32]; + + uint8_t mode = serial_in_buffer[3]; + uint8_t action = serial_in_buffer[6]; + + if (4 == mode) { + if (action < 4) { + + + + + if (action != GetFanspeed()) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), action); + ExecuteCommand(svalue, SRC_REMOTE); +#ifdef USE_BUZZER + BuzzerEnabledBeep((action) ? action : 1, (action) ? 1 : 4); +#endif + } + } else { + + ExecuteCommandPower(1, POWER_TOGGLE, SRC_REMOTE); + } + } + if (6 == mode) { + + Settings.flag3.buzzer_enable = !Settings.flag3.buzzer_enable; + } + if (7 == mode) { + +#ifdef USE_BUZZER + BuzzerEnabledBeep(4, 1); +#endif + } + + + + serial_in_buffer[5] = 0; + serial_in_buffer[6] = 0; + for (uint32_t i = 0; i < 7; i++) { + if ((i > 1) && (i < 6)) { serial_in_buffer[6] += serial_in_buffer[i]; } + Serial.write(serial_in_buffer[i]); + } +} + +bool SonoffIfanSerialInput(void) +{ + if (SONOFF_IFAN03 == my_module_type) { + if (0xAA == serial_in_byte) { + serial_in_byte_counter = 0; + ifan_receive_flag = true; + } + if (ifan_receive_flag) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + if (serial_in_byte_counter == 8) { +# 176 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" + AddLogSerial(LOG_LEVEL_DEBUG); + uint8_t crc = 0; + for (uint32_t i = 2; i < 7; i++) { + crc += serial_in_buffer[i]; + } + if (crc == serial_in_buffer[7]) { + SonoffIfanReceived(); + ifan_receive_flag = false; + return true; + } + } + serial_in_byte = 0; + } + return false; + } +} + + + + + +void CmndFanspeed(void) +{ + if (XdrvMailbox.data_len > 0) { + if ('-' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = (int16_t)GetFanspeed() -1; + if (XdrvMailbox.payload < 0) { XdrvMailbox.payload = MAX_FAN_SPEED -1; } + } + else if ('+' == XdrvMailbox.data[0]) { + XdrvMailbox.payload = GetFanspeed() +1; + if (XdrvMailbox.payload > MAX_FAN_SPEED -1) { XdrvMailbox.payload = 0; } + } + } + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_FAN_SPEED)) { + SonoffIFanSetFanspeed(XdrvMailbox.payload, true); + } + ResponseCmndNumber(GetFanspeed()); +} + + + +bool SonoffIfanInit(void) +{ + if (SONOFF_IFAN03 == my_module_type) { + SetSerial(9600, TS_SERIAL_8N1); + } + return false; +} + +void SonoffIfanUpdate(void) +{ + if (SONOFF_IFAN03 == my_module_type) { + if (ifan_fanspeed_timer) { + ifan_fanspeed_timer--; + if (!ifan_fanspeed_timer) { + SonoffIFanSetFanspeed(ifan_fanspeed_goal, false); + } + } + } + + if (ifan_restart_flag && (4 == uptime) && (SONOFF_IFAN02 == my_module_type)) { + ifan_restart_flag = false; + SetDevicePower(1, SRC_RETRY); + SetDevicePower(power, SRC_RETRY); + } +} + + + + + +bool Xdrv22(uint8_t function) +{ + bool result = false; + + if (IsModuleIfan()) { + switch (function) { + case FUNC_EVERY_250_MSECOND: + SonoffIfanUpdate(); + break; + case FUNC_SERIAL: + result = SonoffIfanSerialInput(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kSonoffIfanCommands, SonoffIfanCommand); + break; + case FUNC_MODULE_INIT: + result = SonoffIfanInit(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" +#ifdef USE_ZIGBEE + +#define OCCUPANCY "Occupancy" + +typedef uint64_t Z_IEEEAddress; +typedef uint16_t Z_ShortAddress; + +enum ZnpCommandType { + Z_POLL = 0x00, + Z_SREQ = 0x20, + Z_AREQ = 0x40, + Z_SRSP = 0x60 +}; + +enum ZnpSubsystem { + Z_RPC_Error = 0x00, + Z_SYS = 0x01, + Z_MAC = 0x02, + Z_NWK = 0x03, + Z_AF = 0x04, + Z_ZDO = 0x05, + Z_SAPI = 0x06, + Z_UTIL = 0x07, + Z_DEBUG = 0x08, + Z_APP = 0x09 +}; + + +enum SysCommand { + SYS_RESET = 0x00, + SYS_PING = 0x01, + SYS_VERSION = 0x02, + SYS_SET_EXTADDR = 0x03, + SYS_GET_EXTADDR = 0x04, + SYS_RAM_READ = 0x05, + SYS_RAM_WRITE = 0x06, + SYS_OSAL_NV_ITEM_INIT = 0x07, + SYS_OSAL_NV_READ = 0x08, + SYS_OSAL_NV_WRITE = 0x09, + SYS_OSAL_START_TIMER = 0x0A, + SYS_OSAL_STOP_TIMER = 0x0B, + SYS_RANDOM = 0x0C, + SYS_ADC_READ = 0x0D, + SYS_GPIO = 0x0E, + SYS_STACK_TUNE = 0x0F, + SYS_SET_TIME = 0x10, + SYS_GET_TIME = 0x11, + SYS_OSAL_NV_DELETE = 0x12, + SYS_OSAL_NV_LENGTH = 0x13, + SYS_TEST_RF = 0x40, + SYS_TEST_LOOPBACK = 0x41, + SYS_RESET_IND = 0x80, + SYS_OSAL_TIMER_EXPIRED = 0x81, +}; + +enum SapiCommand { + SAPI_START_REQUEST = 0x00, + SAPI_BIND_DEVICE = 0x01, + SAPI_ALLOW_BIND = 0x02, + SAPI_SEND_DATA_REQUEST = 0x03, + SAPI_READ_CONFIGURATION = 0x04, + SAPI_WRITE_CONFIGURATION = 0x05, + SAPI_GET_DEVICE_INFO = 0x06, + SAPI_FIND_DEVICE_REQUEST = 0x07, + SAPI_PERMIT_JOINING_REQUEST = 0x08, + SAPI_SYSTEM_RESET = 0x09, + SAPI_START_CONFIRM = 0x80, + SAPI_BIND_CONFIRM = 0x81, + SAPI_ALLOW_BIND_CONFIRM = 0x82, + SAPI_SEND_DATA_CONFIRM = 0x83, + SAPI_FIND_DEVICE_CONFIRM = 0x85, + SAPI_RECEIVE_DATA_INDICATION = 0x87, +}; +enum Z_configuration { + CONF_EXTADDR = 0x01, + CONF_BOOTCOUNTER = 0x02, + CONF_STARTUP_OPTION = 0x03, + CONF_START_DELAY = 0x04, + CONF_NIB = 0x21, + CONF_DEVICE_LIST = 0x22, + CONF_ADDRMGR = 0x23, + CONF_POLL_RATE = 0x24, + CONF_QUEUED_POLL_RATE = 0x25, + CONF_RESPONSE_POLL_RATE = 0x26, + CONF_REJOIN_POLL_RATE = 0x27, + CONF_DATA_RETRIES = 0x28, + CONF_POLL_FAILURE_RETRIES = 0x29, + CONF_STACK_PROFILE = 0x2A, + CONF_INDIRECT_MSG_TIMEOUT = 0x2B, + CONF_ROUTE_EXPIRY_TIME = 0x2C, + CONF_EXTENDED_PAN_ID = 0x2D, + CONF_BCAST_RETRIES = 0x2E, + CONF_PASSIVE_ACK_TIMEOUT = 0x2F, + CONF_BCAST_DELIVERY_TIME = 0x30, + CONF_NWK_MODE = 0x31, + CONF_CONCENTRATOR_ENABLE = 0x32, + CONF_CONCENTRATOR_DISCOVERY = 0x33, + CONF_CONCENTRATOR_RADIUS = 0x34, + CONF_CONCENTRATOR_RC = 0x36, + CONF_NWK_MGR_MODE = 0x37, + CONF_SRC_RTG_EXPIRY_TIME = 0x38, + CONF_ROUTE_DISCOVERY_TIME = 0x39, + CONF_NWK_ACTIVE_KEY_INFO = 0x3A, + CONF_NWK_ALTERN_KEY_INFO = 0x3B, + CONF_ROUTER_OFF_ASSOC_CLEANUP = 0x3C, + CONF_NWK_LEAVE_REQ_ALLOWED = 0x3D, + CONF_NWK_CHILD_AGE_ENABLE = 0x3E, + CONF_DEVICE_LIST_KA_TIMEOUT = 0x3F, + CONF_BINDING_TABLE = 0x41, + CONF_GROUP_TABLE = 0x42, + CONF_APS_FRAME_RETRIES = 0x43, + CONF_APS_ACK_WAIT_DURATION = 0x44, + CONF_APS_ACK_WAIT_MULTIPLIER = 0x45, + CONF_BINDING_TIME = 0x46, + CONF_APS_USE_EXT_PANID = 0x47, + CONF_APS_USE_INSECURE_JOIN = 0x48, + CONF_COMMISSIONED_NWK_ADDR = 0x49, + CONF_APS_NONMEMBER_RADIUS = 0x4B, + CONF_APS_LINK_KEY_TABLE = 0x4C, + CONF_APS_DUPREJ_TIMEOUT_INC = 0x4D, + CONF_APS_DUPREJ_TIMEOUT_COUNT = 0x4E, + CONF_APS_DUPREJ_TABLE_SIZE = 0x4F, + CONF_DIAGNOSTIC_STATS = 0x50, + CONF_SECURITY_LEVEL = 0x61, + CONF_PRECFGKEY = 0x62, + CONF_PRECFGKEYS_ENABLE = 0x63, + CONF_SECURITY_MODE = 0x64, + CONF_SECURE_PERMIT_JOIN = 0x65, + CONF_APS_LINK_KEY_TYPE = 0x66, + CONF_APS_ALLOW_R19_SECURITY = 0x67, + CONF_IMPLICIT_CERTIFICATE = 0x69, + CONF_DEVICE_PRIVATE_KEY = 0x6A, + CONF_CA_PUBLIC_KEY = 0x6B, + CONF_KE_MAX_DEVICES = 0x6C, + CONF_USE_DEFAULT_TCLK = 0x6D, + CONF_RNG_COUNTER = 0x6F, + CONF_RANDOM_SEED = 0x70, + CONF_TRUSTCENTER_ADDR = 0x71, + CONF_USERDESC = 0x81, + CONF_NWKKEY = 0x82, + CONF_PANID = 0x83, + CONF_CHANLIST = 0x84, + CONF_LEAVE_CTRL = 0x85, + CONF_SCAN_DURATION = 0x86, + CONF_LOGICAL_TYPE = 0x87, + CONF_NWKMGR_MIN_TX = 0x88, + CONF_NWKMGR_ADDR = 0x89, + CONF_ZDO_DIRECT_CB = 0x8F, + CONF_TCLK_TABLE_START = 0x0101, + ZNP_HAS_CONFIGURED = 0xF00 +}; +# 210 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" +enum Z_Status { + Z_Success = 0x00, + Z_Failure = 0x01, + Z_InvalidParameter = 0x02, + Z_MemError = 0x03, + Z_Created = 0x09, + Z_BufferFull = 0x11 +}; + +enum Z_App_Profiles { + Z_PROF_IPM = 0x0101, + Z_PROF_HA = 0x0104, + Z_PROF_CBA = 0x0105, + Z_PROF_TA = 0x0107, + Z_PROF_PHHC = 0x0108, + Z_PROF_AMI = 0x0109, +}; + +enum Z_Device_Ids { + Z_DEVID_CONF_TOOL = 0x0005, +# 262 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" +}; +# 275 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" +enum AfCommand : uint8_t { + AF_REGISTER = 0x00, + AF_DATA_REQUEST = 0x01, + AF_DATA_REQUEST_EXT = 0x02, + AF_DATA_REQUEST_SRC_RTG = 0x03, + AF_INTER_PAN_CTL = 0x10, + AF_DATA_STORE = 0x11, + AF_DATA_RETRIEVE = 0x12, + AF_APSF_CONFIG_SET = 0x13, + AF_DATA_CONFIRM = 0x80, + AF_REFLECT_ERROR = 0x83, + AF_INCOMING_MSG = 0x81, + AF_INCOMING_MSG_EXT = 0x82 +}; + + +enum : uint8_t { + ZDO_NWK_ADDR_REQ = 0x00, + ZDO_IEEE_ADDR_REQ = 0x01, + ZDO_NODE_DESC_REQ = 0x02, + ZDO_POWER_DESC_REQ = 0x03, + ZDO_SIMPLE_DESC_REQ = 0x04, + ZDO_ACTIVE_EP_REQ = 0x05, + ZDO_MATCH_DESC_REQ = 0x06, + ZDO_COMPLEX_DESC_REQ = 0x07, + ZDO_USER_DESC_REQ = 0x08, + ZDO_DEVICE_ANNCE = 0x0A, + ZDO_USER_DESC_SET = 0x0B, + ZDO_SERVER_DISC_REQ = 0x0C, + ZDO_END_DEVICE_BIND_REQ = 0x20, + ZDO_BIND_REQ = 0x21, + ZDO_UNBIND_REQ = 0x22, + ZDO_SET_LINK_KEY = 0x23, + ZDO_REMOVE_LINK_KEY = 0x24, + ZDO_GET_LINK_KEY = 0x25, + ZDO_MGMT_NWK_DISC_REQ = 0x30, + ZDO_MGMT_LQI_REQ = 0x31, + ZDO_MGMT_RTQ_REQ = 0x32, + ZDO_MGMT_BIND_REQ = 0x33, + ZDO_MGMT_LEAVE_REQ = 0x34, + ZDO_MGMT_DIRECT_JOIN_REQ = 0x35, + ZDO_MGMT_PERMIT_JOIN_REQ = 0x36, + ZDO_MGMT_NWK_UPDATE_REQ = 0x37, + ZDO_MSG_CB_REGISTER = 0x3E, + ZDO_MGS_CB_REMOVE = 0x3F, + ZDO_STARTUP_FROM_APP = 0x40, + ZDO_AUTO_FIND_DESTINATION = 0x41, + ZDO_EXT_REMOVE_GROUP = 0x47, + ZDO_EXT_REMOVE_ALL_GROUP = 0x48, + ZDO_EXT_FIND_ALL_GROUPS_ENDPOINT = 0x49, + ZDO_EXT_FIND_GROUP = 0x4A, + ZDO_EXT_ADD_GROUP = 0x4B, + ZDO_EXT_COUNT_ALL_GROUPS = 0x4C, + ZDO_NWK_ADDR_RSP = 0x80, + ZDO_IEEE_ADDR_RSP = 0x81, + ZDO_NODE_DESC_RSP = 0x82, + ZDO_POWER_DESC_RSP = 0x83, + ZDO_SIMPLE_DESC_RSP = 0x84, + ZDO_ACTIVE_EP_RSP = 0x85, + ZDO_MATCH_DESC_RSP = 0x86, + ZDO_COMPLEX_DESC_RSP = 0x87, + ZDO_USER_DESC_RSP = 0x88, + ZDO_USER_DESC_CONF = 0x89, + ZDO_SERVER_DISC_RSP = 0x8A, + ZDO_END_DEVICE_BIND_RSP = 0xA0, + ZDO_BIND_RSP = 0xA1, + ZDO_UNBIND_RSP = 0xA2, + ZDO_MGMT_NWK_DISC_RSP = 0xB0, + ZDO_MGMT_LQI_RSP = 0xB1, + ZDO_MGMT_RTG_RSP = 0xB2, + ZDO_MGMT_BIND_RSP = 0xB3, + ZDO_MGMT_LEAVE_RSP = 0xB4, + ZDO_MGMT_DIRECT_JOIN_RSP = 0xB5, + ZDO_MGMT_PERMIT_JOIN_RSP = 0xB6, + ZDO_STATE_CHANGE_IND = 0xC0, + ZDO_END_DEVICE_ANNCE_IND = 0xC1, + ZDO_MATCH_DESC_RSP_SENT = 0xC2, + ZDO_STATUS_ERROR_RSP = 0xC3, + ZDO_SRC_RTG_IND = 0xC4, + ZDO_LEAVE_IND = 0xC9, + ZDO_TC_DEV_IND = 0xCA, + ZDO_PERMIT_JOIN_IND = 0xCB, + ZDO_MSG_CB_INCOMING = 0xFF +}; + + +enum ZdoStates { + ZDO_DEV_HOLD = 0x00, + ZDO_DEV_INIT = 0x01, + ZDO_DEV_NWK_DISC = 0x02, + ZDO_DEV_NWK_JOINING = 0x03, + ZDO_DEV_NWK_REJOIN = 0x04, + ZDO_DEV_END_DEVICE_UNAUTH = 0x05, + ZDO_DEV_END_DEVICE = 0x06, + ZDO_DEV_ROUTER = 0x07, + ZDO_DEV_COORD_STARTING = 0x08, + ZDO_DEV_ZB_COORD = 0x09, + ZDO_DEV_NWK_ORPHAN = 0x0A, +}; + + +enum Z_Util { + Z_UTIL_GET_DEVICE_INFO = 0x00, + Z_UTIL_GET_NV_INFO = 0x01, + Z_UTIL_SET_PANID = 0x02, + Z_UTIL_SET_CHANNELS = 0x03, + Z_UTIL_SET_SECLEVEL = 0x04, + Z_UTIL_SET_PRECFGKEY = 0x05, + Z_UTIL_CALLBACK_SUB_CMD = 0x06, + Z_UTIL_KEY_EVENT = 0x07, + Z_UTIL_TIME_ALIVE = 0x09, + Z_UTIL_LED_CONTROL = 0x0A, + Z_UTIL_TEST_LOOPBACK = 0x10, + Z_UTIL_DATA_REQ = 0x11, + Z_UTIL_SRC_MATCH_ENABLE = 0x20, + Z_UTIL_SRC_MATCH_ADD_ENTRY = 0x21, + Z_UTIL_SRC_MATCH_DEL_ENTRY = 0x22, + Z_UTIL_SRC_MATCH_CHECK_SRC_ADDR = 0x23, + Z_UTIL_SRC_MATCH_ACK_ALL_PENDING = 0x24, + Z_UTIL_SRC_MATCH_CHECK_ALL_PENDING = 0x25, + Z_UTIL_ADDRMGR_EXT_ADDR_LOOKUP = 0x40, + Z_UTIL_ADDRMGR_NWK_ADDR_LOOKUP = 0x41, + Z_UTIL_APSME_LINK_KEY_DATA_GET = 0x44, + Z_UTIL_APSME_LINK_KEY_NV_ID_GET = 0x45, + Z_UTIL_ASSOC_COUNT = 0x48, + Z_UTIL_ASSOC_FIND_DEVICE = 0x49, + Z_UTIL_ASSOC_GET_WITH_ADDRESS = 0x4A, + Z_UTIL_APSME_REQUEST_KEY_CMD = 0x4B, + Z_UTIL_ZCL_KEY_EST_INIT_EST = 0x80, + Z_UTIL_ZCL_KEY_EST_SIGN = 0x81, + Z_UTIL_UTIL_SYNC_REQ = 0xE0, + Z_UTIL_ZCL_KEY_ESTABLISH_IND = 0xE1 +}; + +enum ZCL_Global_Commands { + ZCL_READ_ATTRIBUTES = 0x00, + ZCL_READ_ATTRIBUTES_RESPONSE = 0x01, + ZCL_WRITE_ATTRIBUTES = 0x02, + ZCL_WRITE_ATTRIBUTES_UNDIVIDED = 0x03, + ZCL_WRITE_ATTRIBUTES_RESPONSE = 0x04, + ZCL_WRITE_ATTRIBUTES_NORESPONSE = 0x05, + ZCL_CONFIGURE_REPORTING = 0x06, + ZCL_CONFIGURE_REPORTING_RESPONSE = 0x07, + ZCL_READ_REPORTING_CONFIGURATION = 0x08, + ZCL_READ_REPORTING_CONFIGURATION_RESPONSE = 0x09, + ZCL_REPORT_ATTRIBUTES = 0x0a, + ZCL_DEFAULT_RESPONSE = 0x0b, + ZCL_DISCOVER_ATTRIBUTES = 0x0c, + ZCL_DISCOVER_ATTRIBUTES_RESPONSE = 0x0d + +}; + +const uint16_t Z_ProfileIds[] PROGMEM = { 0x0104, 0x0109, 0xA10E, 0xC05E }; +const char Z_ProfileNames[] PROGMEM = "ZigBee Home Automation|ZigBee Smart Energy|ZigBee Green Power|ZigBee Light Link"; + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_1_headers.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_1_headers.ino" +#ifdef USE_ZIGBEE + + + +void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp = true, uint8_t transacId = 1); + + + +JsonVariant &getCaseInsensitive(const JsonObject &json, const char *needle) { + + if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) { + return *(JsonVariant*)nullptr; + } + + for (auto kv : json) { + const char *key = kv.key; + JsonVariant &value = kv.value; + + if (0 == strcasecmp_P(key, needle)) { + return value; + } + } + + return *(JsonVariant*)nullptr; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" +#ifdef USE_ZIGBEE + +#include +#include + +#ifndef ZIGBEE_SAVE_DELAY_SECONDS +#define ZIGBEE_SAVE_DELAY_SECONDS 10; +#endif +const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; + +typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value); + +typedef struct Z_Device { + uint16_t shortaddr; + uint64_t longaddr; + uint32_t firstSeen; + uint32_t lastSeen; + String manufacturerId; + String modelId; + String friendlyName; + std::vector endpoints; + std::vector clusters_in; + std::vector clusters_out; + + uint32_t timer; + uint16_t cluster; + uint16_t endpoint; + uint32_t value; + Z_DeviceTimer func; + + DynamicJsonBuffer *json_buffer; + JsonObject *json; +} Z_Device; + + + + + + + +class Z_Devices { +public: + Z_Devices() {}; + + + + + + + uint16_t isKnownShortAddr(uint16_t shortaddr) const; + uint16_t isKnownLongAddr(uint64_t longaddr) const; + uint16_t isKnownIndex(uint32_t index) const; + uint16_t isKnownFriendlyName(const char * name) const; + + uint64_t getDeviceLongAddr(uint16_t shortaddr) const; + + + + void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0); + + + void addEndoint(uint16_t shortaddr, uint8_t endpoint); + + + void addEndointProfile(uint16_t shortaddr, uint8_t endpoint, uint16_t profileId); + + + void addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluster, bool out); + + uint8_t findClusterEndpointIn(uint16_t shortaddr, uint16_t cluster); + + void setManufId(uint16_t shortaddr, const char * str); + void setModelId(uint16_t shortaddr, const char * str); + void setFriendlyName(uint16_t shortaddr, const char * str); + const String * getFriendlyName(uint16_t) const; + + + void updateLastSeen(uint16_t shortaddr); + + + String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const; + + + void resetTimer(uint32_t shortaddr); + void setTimer(uint32_t shortaddr, uint32_t wait_ms, uint16_t cluster, uint16_t endpoint, uint32_t value, Z_DeviceTimer func); + void runTimer(void); + + + void jsonClear(uint16_t shortaddr); + void jsonAppend(uint16_t shortaddr, const JsonObject &values); + const JsonObject *jsonGet(uint16_t shortaddr); + void jsonPublishFlush(uint16_t shortaddr); + bool jsonIsConflict(uint16_t shortaddr, const JsonObject &values); + void jsonPublishNow(uint16_t shortaddr, JsonObject &values); + + + size_t devicesSize(void) const { + return _devices.size(); + } + const Z_Device &devicesAt(size_t i) const { + return _devices.at(i); + } + + + bool removeDevice(uint16_t shortaddr); + + + void dirty(void); + void clean(void); + + + uint16_t parseDeviceParam(const char * param, bool short_must_be_known = false) const; + +private: + std::vector _devices = {}; + uint32_t _saveTimer = 0; + + template < typename T> + static bool findInVector(const std::vector & vecOfElements, const T & element); + + template < typename T> + static int32_t findEndpointInVector(const std::vector & vecOfElements, uint8_t element); + + + static int32_t findClusterEndpoint(const std::vector & vecOfElements, uint16_t element); + + Z_Device & getShortAddr(uint16_t shortaddr); + const Z_Device & getShortAddrConst(uint16_t shortaddr) const ; + Z_Device & getLongAddr(uint64_t longaddr); + + int32_t findShortAddr(uint16_t shortaddr) const; + int32_t findLongAddr(uint64_t longaddr) const; + int32_t findFriendlyName(const char * name) const; + + void _updateLastSeen(Z_Device &device) { + if (&device != nullptr) { + device.lastSeen = Rtc.utc_time; + } + }; + + + Z_Device & createDeviceEntry(uint16_t shortaddr, uint64_t longaddr = 0); +}; + +Z_Devices zigbee_devices = Z_Devices(); + + +uint64_t localIEEEAddr = 0; + + +template < typename T> +bool Z_Devices::findInVector(const std::vector & vecOfElements, const T & element) { + + auto it = std::find(vecOfElements.begin(), vecOfElements.end(), element); + + if (it != vecOfElements.end()) { + return true; + } else { + return false; + } +} + +template < typename T> +int32_t Z_Devices::findEndpointInVector(const std::vector & vecOfElements, uint8_t element) { + + + int32_t found = 0; + for (auto &elem : vecOfElements) { + if ( ((elem >> 16) & 0xFF) == element) { return found; } + found++; + } + + return -1; +} +# 204 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" +int32_t Z_Devices::findClusterEndpoint(const std::vector & vecOfElements, uint16_t cluster) { + int32_t found = 0; + for (auto &elem : vecOfElements) { + if ((elem & 0xFFFF) == cluster) { return found; } + found++; + } + return -1; +} + + + + + +Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) { + if (!shortaddr && !longaddr) { return *(Z_Device*) nullptr; } + Z_Device device = { shortaddr, longaddr, + Rtc.utc_time, Rtc.utc_time, + String(), + String(), + String(), + std::vector(), + std::vector(), + std::vector(), + 0,0,0,0, + nullptr, + nullptr, nullptr }; + device.json_buffer = new DynamicJsonBuffer(); + _devices.push_back(device); + dirty(); + return _devices.back(); +} +# 244 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" +int32_t Z_Devices::findShortAddr(uint16_t shortaddr) const { + if (!shortaddr) { return -1; } + int32_t found = 0; + if (shortaddr) { + for (auto &elem : _devices) { + if (elem.shortaddr == shortaddr) { return found; } + found++; + } + } + return -1; +} +# 263 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" +int32_t Z_Devices::findLongAddr(uint64_t longaddr) const { + if (!longaddr) { return -1; } + int32_t found = 0; + if (longaddr) { + for (auto &elem : _devices) { + if (elem.longaddr == longaddr) { return found; } + found++; + } + } + return -1; +} +# 282 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" +int32_t Z_Devices::findFriendlyName(const char * name) const { + if (!name) { return -1; } + size_t name_len = strlen(name); + int32_t found = 0; + if (name_len) { + for (auto &elem : _devices) { + if (elem.friendlyName == name) { return found; } + found++; + } + } + return -1; +} + + +uint16_t Z_Devices::isKnownShortAddr(uint16_t shortaddr) const { + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + return shortaddr; + } else { + return 0; + } +} + +uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const { + int32_t found = findLongAddr(longaddr); + if (found >= 0) { + const Z_Device & device = devicesAt(found); + return device.shortaddr; + } else { + return 0; + } +} + +uint16_t Z_Devices::isKnownIndex(uint32_t index) const { + if (index < devicesSize()) { + const Z_Device & device = devicesAt(index); + return device.shortaddr; + } else { + return 0; + } +} + +uint16_t Z_Devices::isKnownFriendlyName(const char * name) const { + if ((!name) || (0 == strlen(name))) { return 0xFFFF; } + int32_t found = findFriendlyName(name); + if (found >= 0) { + const Z_Device & device = devicesAt(found); + return device.shortaddr; + } else { + return 0; + } +} + +uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const { + const Z_Device & device = getShortAddrConst(shortaddr); + return device.longaddr; +} + + + + +Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) { + if (!shortaddr) { return *(Z_Device*) nullptr; } + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + return _devices[found]; + } + + return createDeviceEntry(shortaddr, 0); +} + +const Z_Device & Z_Devices::getShortAddrConst(uint16_t shortaddr) const { + if (!shortaddr) { return *(Z_Device*) nullptr; } + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + return _devices[found]; + } + return *((Z_Device*)nullptr); +} + + +Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) { + if (!longaddr) { return *(Z_Device*) nullptr; } + int32_t found = findLongAddr(longaddr); + if (found > 0) { + return _devices[found]; + } + return createDeviceEntry(0, longaddr); +} + + +bool Z_Devices::removeDevice(uint16_t shortaddr) { + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + _devices.erase(_devices.begin() + found); + dirty(); + return true; + } + return false; +} + + + + + + +void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) { + int32_t s_found = findShortAddr(shortaddr); + int32_t l_found = findLongAddr(longaddr); + + if ((s_found >= 0) && (l_found >= 0)) { + if (s_found == l_found) { + updateLastSeen(shortaddr); + } else { + + _devices[l_found].shortaddr = shortaddr; + + _devices.erase(_devices.begin() + s_found); + updateLastSeen(shortaddr); + dirty(); + } + } else if (s_found >= 0) { + + + _devices[s_found].longaddr = longaddr; + updateLastSeen(shortaddr); + dirty(); + } else if (l_found >= 0) { + + _devices[l_found].shortaddr = shortaddr; + dirty(); + } else { + + if (shortaddr || longaddr) { + createDeviceEntry(shortaddr, longaddr); + } + } +} + + + + +void Z_Devices::addEndoint(uint16_t shortaddr, uint8_t endpoint) { + if (!shortaddr) { return; } + uint32_t ep_profile = (endpoint << 16); + Z_Device &device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + _updateLastSeen(device); + if (findEndpointInVector(device.endpoints, endpoint) < 0) { + device.endpoints.push_back(ep_profile); + dirty(); + } +} + +void Z_Devices::addEndointProfile(uint16_t shortaddr, uint8_t endpoint, uint16_t profileId) { + if (!shortaddr) { return; } + uint32_t ep_profile = (endpoint << 16) | profileId; + Z_Device &device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + _updateLastSeen(device); + int32_t found = findEndpointInVector(device.endpoints, endpoint); + if (found < 0) { + device.endpoints.push_back(ep_profile); + dirty(); + } else { + if (device.endpoints[found] != ep_profile) { + device.endpoints[found] = ep_profile; + dirty(); + } + } +} + +void Z_Devices::addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluster, bool out) { + if (!shortaddr) { return; } + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + _updateLastSeen(device); + uint32_t ep_cluster = (endpoint << 16) | cluster; + if (!out) { + if (!findInVector(device.clusters_in, ep_cluster)) { + device.clusters_in.push_back(ep_cluster); + dirty(); + } + } else { + if (!findInVector(device.clusters_out, ep_cluster)) { + device.clusters_out.push_back(ep_cluster); + dirty(); + } + } +} + + + +uint8_t Z_Devices::findClusterEndpointIn(uint16_t shortaddr, uint16_t cluster){ + int32_t short_found = findShortAddr(shortaddr); + if (short_found < 0) return 0; + Z_Device &device = getShortAddr(shortaddr); + if (&device == nullptr) { return 0; } + int32_t found = findClusterEndpoint(device.clusters_in, cluster); + if (found >= 0) { + return (device.clusters_in[found] >> 16) & 0xFF; + } else { + return 0; + } +} + + +void Z_Devices::setManufId(uint16_t shortaddr, const char * str) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + _updateLastSeen(device); + if (!device.manufacturerId.equals(str)) { + dirty(); + } + device.manufacturerId = str; +} +void Z_Devices::setModelId(uint16_t shortaddr, const char * str) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + _updateLastSeen(device); + if (!device.modelId.equals(str)) { + dirty(); + } + device.modelId = str; +} +void Z_Devices::setFriendlyName(uint16_t shortaddr, const char * str) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + _updateLastSeen(device); + if (!device.friendlyName.equals(str)) { + dirty(); + } + device.friendlyName = str; +} + +const String * Z_Devices::getFriendlyName(uint16_t shortaddr) const { + int32_t found = findShortAddr(shortaddr); + if (found >= 0) { + const Z_Device & device = devicesAt(found); + if (device.friendlyName.length() > 0) { + return &device.friendlyName; + } + } + return nullptr; +} + + +void Z_Devices::updateLastSeen(uint16_t shortaddr) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + _updateLastSeen(device); +} + + + + +void Z_Devices::resetTimer(uint32_t shortaddr) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + device.timer = 0; + device.func = nullptr; +} + + +void Z_Devices::setTimer(uint32_t shortaddr, uint32_t wait_ms, uint16_t cluster, uint16_t endpoint, uint32_t value, Z_DeviceTimer func) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + + device.cluster = cluster; + device.endpoint = endpoint; + device.value = value; + device.func = func; + device.timer = wait_ms + millis(); +} + + +void Z_Devices::runTimer(void) { + for (std::vector::iterator it = _devices.begin(); it != _devices.end(); ++it) { + Z_Device &device = *it; + uint16_t shortaddr = device.shortaddr; + + uint32_t timer = device.timer; + if ((timer) && TimeReached(timer)) { + device.timer = 0; + + (*device.func)(device.shortaddr, device.cluster, device.endpoint, device.value); + } + } + + if ((_saveTimer) && TimeReached(_saveTimer)) { + saveZigbeeDevices(); + _saveTimer = 0; + } +} + +void Z_Devices::jsonClear(uint16_t shortaddr) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + + device.json = nullptr; + device.json_buffer->clear(); +} + +void CopyJsonVariant(JsonObject &to, const String &key, const JsonVariant &val) { + to.remove(key); + + if (val.is()) { + String sval = val.as(); + to.set(key, sval); + } else if (val.is()) { + JsonArray &nested_arr = to.createNestedArray(key); + CopyJsonArray(nested_arr, val.as()); + } else if (val.is()) { + JsonObject &nested_obj = to.createNestedObject(key); + CopyJsonObject(nested_obj, val.as()); + } else { + to.set(key, val); + } +} + +void CopyJsonArray(JsonArray &to, const JsonArray &arr) { + for (auto v : arr) { + if (v.is()) { + String sval = v.as(); + to.add(sval); + } else if (v.is()) { + } else if (v.is()) { + } else { + to.add(v); + } + } +} + +void CopyJsonObject(JsonObject &to, const JsonObject &from) { + for (auto kv : from) { + String key_string = kv.key; + JsonVariant &val = kv.value; + + CopyJsonVariant(to, key_string, val); + } +} + + +bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const JsonObject &values) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return false; } + if (&values == nullptr) { return false; } + + if (nullptr == device.json) { + return false; + } + + for (auto kv : values) { + String key_string = kv.key; + + if (strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_LINKQUALITY))) { + if (device.json->containsKey(kv.key)) { + return true; + } + } + } + return false; +} + +void Z_Devices::jsonAppend(uint16_t shortaddr, const JsonObject &values) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + if (&values == nullptr) { return; } + + if (nullptr == device.json) { + device.json = &(device.json_buffer->createObject()); + } + + char sa[8]; + snprintf_P(sa, sizeof(sa), PSTR("0x%04X"), shortaddr); + device.json->set(F(D_JSON_ZIGBEE_DEVICE), sa); + + const String * fname = zigbee_devices.getFriendlyName(shortaddr); + if (fname) { + device.json->set(F(D_JSON_ZIGBEE_NAME), (char*)fname->c_str()); + } + + + CopyJsonObject(*device.json, values); +} + +const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return nullptr; } + return device.json; +} + +void Z_Devices::jsonPublishFlush(uint16_t shortaddr) { + Z_Device & device = getShortAddr(shortaddr); + if (&device == nullptr) { return; } + JsonObject * json = device.json; + if (json == nullptr) { return; } + + const String * fname = zigbee_devices.getFriendlyName(shortaddr); + bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); +# 693 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" + if (use_fname) { + json->remove(F(D_JSON_ZIGBEE_NAME)); + } else { + json->remove(F(D_JSON_ZIGBEE_DEVICE)); + } + + String msg = ""; + json->printTo(msg); + zigbee_devices.jsonClear(shortaddr); + + if (use_fname) { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str()); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); + XdrvRulesProcess(); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str()); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); + XdrvRulesProcess(); + } else { + Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str()); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); + XdrvRulesProcess(); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str()); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); + XdrvRulesProcess(); + } + + +} + +void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) { + jsonPublishFlush(shortaddr); + jsonAppend(shortaddr, values); + jsonPublishFlush(shortaddr); +} + +void Z_Devices::dirty(void) { + _saveTimer = kZigbeeSaveDelaySeconds * 1000 + millis(); +} +void Z_Devices::clean(void) { + _saveTimer = 0; +} + + + + + + +uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_known) const { + if (nullptr == param) { return 0; } + size_t param_len = strlen(param); + char dataBuf[param_len + 1]; + strcpy(dataBuf, param); + RemoveSpace(dataBuf); + uint16_t shortaddr = 0; + + if (strlen(dataBuf) < 4) { + + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) { + shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1); + } + } else if ((dataBuf[0] == '0') && (dataBuf[1] == 'x')) { + + if (strlen(dataBuf) < 18) { + + shortaddr = strtoull(dataBuf, nullptr, 0); + if (short_must_be_known) { + shortaddr = zigbee_devices.isKnownShortAddr(shortaddr); + } + + } else { + + uint64_t longaddr = strtoull(dataBuf, nullptr, 0); + shortaddr = zigbee_devices.isKnownLongAddr(longaddr); + } + } else { + + shortaddr = zigbee_devices.isKnownFriendlyName(dataBuf); + } + + return shortaddr; +} + + + + + +String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { + DynamicJsonBuffer jsonBuffer; + JsonArray& json = jsonBuffer.createArray(); + JsonArray& devices = json; + + for (std::vector::const_iterator it = _devices.begin(); it != _devices.end(); ++it) { + const Z_Device& device = *it; + uint16_t shortaddr = device.shortaddr; + char hex[22]; + + + if ((status_shortaddr) && (status_shortaddr != shortaddr)) { continue; } + + JsonObject& dev = devices.createNestedObject(); + + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); + dev[F(D_JSON_ZIGBEE_DEVICE)] = hex; + + if (device.friendlyName.length() > 0) { + dev[F(D_JSON_ZIGBEE_NAME)] = device.friendlyName; + } + + if (2 <= dump_mode) { + hex[0] = '0'; + hex[1] = 'x'; + Uint64toHex(device.longaddr, &hex[2], 64); + dev[F("IEEEAddr")] = hex; + if (device.modelId.length() > 0) { + dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId; + } + if (device.manufacturerId.length() > 0) { + dev[F("Manufacturer")] = device.manufacturerId; + } + } + + + if (3 <= dump_mode) { + JsonObject& dev_endpoints = dev.createNestedObject(F("Endpoints")); + for (std::vector::const_iterator ite = device.endpoints.begin() ; ite != device.endpoints.end(); ++ite) { + uint32_t ep_profile = *ite; + uint8_t endpoint = (ep_profile >> 16) & 0xFF; + uint16_t profileId = ep_profile & 0xFFFF; + + snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); + JsonObject& ep = dev_endpoints.createNestedObject(hex); + + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), profileId); + ep[F("ProfileId")] = hex; + + int32_t found = -1; + for (uint32_t i = 0; i < sizeof(Z_ProfileIds) / sizeof(Z_ProfileIds[0]); i++) { + if (pgm_read_word(&Z_ProfileIds[i]) == profileId) { + found = i; + break; + } + } + if (found > 0) { + GetTextIndexed(hex, sizeof(hex), found, Z_ProfileNames); + ep[F("ProfileIdName")] = hex; + } + + ep.createNestedArray(F("ClustersIn")); + ep.createNestedArray(F("ClustersOut")); + } + + for (std::vector::const_iterator itc = device.clusters_in.begin() ; itc != device.clusters_in.end(); ++itc) { + uint16_t cluster = *itc & 0xFFFF; + uint8_t endpoint = (*itc >> 16) & 0xFF; + + snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); + JsonArray &cluster_arr = dev_endpoints[hex][F("ClustersIn")]; + + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), cluster); + cluster_arr.add(hex); + } + + for (std::vector::const_iterator itc = device.clusters_out.begin() ; itc != device.clusters_out.end(); ++itc) { + uint16_t cluster = *itc & 0xFFFF; + uint8_t endpoint = (*itc >> 16) & 0xFF; + + snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); + JsonArray &cluster_arr = dev_endpoints[hex][F("ClustersOut")]; + + snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), cluster); + cluster_arr.add(hex); + } + } + } + String payload = ""; + payload.reserve(200); + json.printTo(payload); + return payload; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" +#ifdef USE_ZIGBEE +# 50 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" +const static uint16_t z_spi_start_sector = 0xFF; +const static uint8_t* z_spi_start = (uint8_t*) 0x402FF000; +const static uint8_t* z_dev_start = z_spi_start + 0x0800; +const static size_t z_spi_len = 0x1000; +const static size_t z_block_offset = 0x0800; +const static size_t z_block_len = 0x0800; + +class z_flashdata_t { +public: + uint32_t name; + uint16_t len; + uint16_t reserved; +}; + +const static uint32_t ZIGB_NAME = 0x3167697A; +const static size_t Z_MAX_FLASH = z_block_len - sizeof(z_flashdata_t); + + +const uint16_t Z_ClusterNumber[] PROGMEM = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0100, 0x0101, 0x0102, + 0x0201, 0x0202, 0x0203, 0x0204, + 0x0300, 0x0301, + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, + 0x0500, 0x0501, 0x0502, + 0x0700, 0x0701, 0x0702, + 0x0B00, 0x0B01, 0x0B02, 0x0B03, 0x0B04, 0x0B05, + 0x1000, + 0xFC0F, +}; + + +uint16_t fromClusterCode(uint8_t c) { + if (c >= sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0])) { + return 0xFFFF; + } + return pgm_read_word(&Z_ClusterNumber[c]); +} + + +uint8_t toClusterCode(uint16_t c) { + for (uint32_t i = 0; i < sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0]); i++) { + if (c == pgm_read_word(&Z_ClusterNumber[i])) { + return i; + } + } + return 0xFF; +} + +class SBuffer hibernateDevice(const struct Z_Device &device) { + SBuffer buf(128); + + buf.add8(0x00); + buf.add16(device.shortaddr); + buf.add64(device.longaddr); + uint32_t endpoints = device.endpoints.size(); + if (endpoints > 254) { endpoints = 254; } + buf.add8(endpoints); + + for (std::vector::const_iterator ite = device.endpoints.begin() ; ite != device.endpoints.end(); ++ite) { + uint32_t ep_profile = *ite; + uint8_t endpoint = (ep_profile >> 16) & 0xFF; + uint16_t profileId = ep_profile & 0xFFFF; + + buf.add8(endpoint); + buf.add16(profileId); + for (std::vector::const_iterator itc = device.clusters_in.begin() ; itc != device.clusters_in.end(); ++itc) { + uint16_t cluster = *itc & 0xFFFF; + uint8_t c_endpoint = (*itc >> 16) & 0xFF; + + if (endpoint == c_endpoint) { + uint8_t clusterCode = toClusterCode(cluster); + if (0xFF != clusterCode) { buf.add8(clusterCode); } + } + } + buf.add8(0xFF); + + for (std::vector::const_iterator itc = device.clusters_out.begin() ; itc != device.clusters_out.end(); ++itc) { + uint16_t cluster = *itc & 0xFFFF; + uint8_t c_endpoint = (*itc >> 16) & 0xFF; + + if (endpoint == c_endpoint) { + uint8_t clusterCode = toClusterCode(cluster); + if (0xFF != clusterCode) { buf.add8(clusterCode); } + } + } + buf.add8(0xFF); + } + + + size_t model_len = device.modelId.length(); + if (model_len > 32) { model_len = 32; } + buf.addBuffer(device.modelId.c_str(), model_len); + buf.add8(0x00); + + + size_t manuf_len = device.manufacturerId.length(); + if (manuf_len > 32) {manuf_len = 32; } + buf.addBuffer(device.manufacturerId.c_str(), manuf_len); + buf.add8(0x00); + + + size_t frname_len = device.friendlyName.length(); + if (frname_len > 32) {frname_len = 32; } + buf.addBuffer(device.friendlyName.c_str(), frname_len); + buf.add8(0x00); + + + buf.set8(0, buf.len()); + + return buf; +} + +class SBuffer hibernateDevices(void) { + SBuffer buf(2048); + + size_t devices_size = zigbee_devices.devicesSize(); + if (devices_size > 32) { devices_size = 32; } + buf.add8(devices_size); + + for (uint32_t i = 0; i < devices_size; i++) { + const Z_Device & device = zigbee_devices.devicesAt(i); + const SBuffer buf_device = hibernateDevice(device); + buf.addBuffer(buf_device); + } + + size_t buf_len = buf.len(); + if (buf_len > 2040) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Devices list too big to fit in Flash (%d)"), buf_len); + } + + + char *hex_char = (char*) malloc((buf_len * 2) + 2); + if (hex_char) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "ZbFlashStore %s"), + ToHex_P(buf.getBuffer(), buf_len, hex_char, (buf_len * 2) + 2)); + free(hex_char); + } + + return buf; +} + +void hidrateDevices(const SBuffer &buf) { + uint32_t buf_len = buf.len(); + if (buf_len <= 10) { return; } + + uint32_t k = 0; + uint32_t num_devices = buf.get8(k++); + + for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) { + uint32_t dev_record_len = buf.get8(k); + + SBuffer buf_d = buf.subBuffer(k, dev_record_len); + + uint32_t d = 1; + uint16_t shortaddr = buf_d.get16(d); d += 2; + uint64_t longaddr = buf_d.get64(d); d += 8; + zigbee_devices.updateDevice(shortaddr, longaddr); + + uint32_t endpoints = buf_d.get8(d++); + for (uint32_t j = 0; j < endpoints; j++) { + uint8_t ep = buf_d.get8(d++); + uint16_t ep_profile = buf_d.get16(d); d += 2; + zigbee_devices.addEndointProfile(shortaddr, ep, ep_profile); + + + while (d < dev_record_len) { + uint8_t ep_cluster = buf_d.get8(d++); + if (0xFF == ep_cluster) { break; } + zigbee_devices.addCluster(shortaddr, ep, fromClusterCode(ep_cluster), false); + } + + while (d < dev_record_len) { + uint8_t ep_cluster = buf_d.get8(d++); + if (0xFF == ep_cluster) { break; } + zigbee_devices.addCluster(shortaddr, ep, fromClusterCode(ep_cluster), true); + } + } + + + char empty[] = ""; + + + uint32_t s_len = buf_d.strlen_s(d); + char *ptr = s_len ? buf_d.charptr(d) : empty; + zigbee_devices.setModelId(shortaddr, ptr); + d += s_len + 1; + + + s_len = buf_d.strlen_s(d); + ptr = s_len ? buf_d.charptr(d) : empty; + zigbee_devices.setManufId(shortaddr, ptr); + d += s_len + 1; + + + s_len = buf_d.strlen_s(d); + ptr = s_len ? buf_d.charptr(d) : empty; + zigbee_devices.setFriendlyName(shortaddr, ptr); + d += s_len + 1; + + + k += dev_record_len; + } +} + +void loadZigbeeDevices(void) { + z_flashdata_t flashdata; + memcpy_P(&flashdata, z_dev_start, sizeof(z_flashdata_t)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len); + + + if ((flashdata.name == ZIGB_NAME) && (flashdata.len > 0)) { + uint16_t buf_len = flashdata.len; + + SBuffer buf(buf_len); + buf.addBuffer(z_dev_start + sizeof(z_flashdata_t), buf_len); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee devices data in Flash (%d bytes)"), buf_len); + hidrateDevices(buf); + zigbee_devices.clean(); + } else { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No zigbee devices data in Flash")); + } +} + +void saveZigbeeDevices(void) { + SBuffer buf = hibernateDevices(); + size_t buf_len = buf.len(); + if (buf_len > Z_MAX_FLASH) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Buffer too big to fit in Flash (%d bytes)"), buf_len); + return; + } + + + uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); + if (!spi_buffer) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); + return; + } + + ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + + z_flashdata_t *flashdata = (z_flashdata_t*)(spi_buffer + z_block_offset); + flashdata->name = ZIGB_NAME; + flashdata->len = buf_len; + flashdata->reserved = 0; + + memcpy(spi_buffer + z_block_offset + sizeof(z_flashdata_t), buf.getBuffer(), buf_len); + + + if (ESP.flashEraseSector(z_spi_start_sector)) { + ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + } + + free(spi_buffer); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data store in Flash (0x%08X - %d bytes)"), z_dev_start, buf_len); +} + + +void eraseZigbeeDevices(void) { + zigbee_devices.clean(); + + uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); + if (!spi_buffer) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); + return; + } + + ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + + + memset(spi_buffer + z_block_offset, 0xFF, z_block_len); + + + if (ESP.flashEraseSector(z_spi_start_sector)) { + ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); + } + + free(spi_buffer); + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased (0x%08X - %d bytes)"), z_dev_start, z_block_len); +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" +#ifdef USE_ZIGBEE + + + + + +typedef union ZCLHeaderFrameControl_t { + struct { + uint8_t frame_type : 2; + uint8_t manuf_specific : 1; + uint8_t direction : 1; + uint8_t disable_def_resp : 1; + uint8_t reserved : 3; + } b; + uint32_t d8; +} ZCLHeaderFrameControl_t; + + +class ZCLFrame { +public: + + ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id, + const char *buf, size_t buf_len, uint16_t clusterid, uint16_t groupid, + uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, + uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, + uint32_t timestamp): + _cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq), + _payload(buf_len ? buf_len : 250), + _cluster_id(clusterid), _group_id(groupid), + _srcaddr(srcaddr), _srcendpoint(srcendpoint), _dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast), + _linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber), + _timestamp(timestamp) + { + _frame_control.d8 = frame_control; + _payload.addBuffer(buf, buf_len); + }; + + + void log(void) { + char hex_char[_payload.len()*2+2]; + ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); + Response_P(PSTR("{\"" D_JSON_ZIGBEEZCL_RECEIVED "\":{" + "\"groupid\":%d," "\"clusterid\":%d," "\"srcaddr\":\"0x%04X\"," + "\"srcendpoint\":%d," "\"dstendpoint\":%d," "\"wasbroadcast\":%d," + "\"" D_CMND_ZIGBEE_LINKQUALITY "\":%d," "\"securityuse\":%d," "\"seqnumber\":%d," + "\"timestamp\":%d," + "\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," + "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"), + _group_id, _cluster_id, _srcaddr, + _srcendpoint, _dstendpoint, _wasbroadcast, + _linkquality, _securityuse, _seqnumber, + _timestamp, + _frame_control, _manuf_code, _transact_seq, _cmd_id, + hex_char); + if (Settings.flag3.tuya_serial_mqtt_publish) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); + XdrvRulesProcess(); + } else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); + } + } + + static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid, + uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, + uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, + uint32_t timestamp) { + uint32_t i = offset; + ZCLHeaderFrameControl_t frame_control; + uint16_t manuf_code = 0; + uint8_t transact_seq; + uint8_t cmd_id; + + frame_control.d8 = buf.get8(i++); + if (frame_control.b.manuf_specific) { + manuf_code = buf.get16(i); + i += 2; + } + transact_seq = buf.get8(i++); + cmd_id = buf.get8(i++); + ZCLFrame zcl_frame(frame_control.d8, manuf_code, transact_seq, cmd_id, + (const char *)(buf.buf() + i), len + offset - i, + clusterid, groupid, + srcaddr, srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber, + timestamp); + return zcl_frame; + } + + bool isClusterSpecificCommand(void) { + return _frame_control.b.frame_type & 1; + } + + static void generateAttributeName(const JsonObject& json, uint16_t cluster, uint16_t attr, char *key, size_t key_len); + void parseRawAttributes(JsonObject& json, uint8_t offset = 0); + void parseReadAttributes(JsonObject& json, uint8_t offset = 0); + void parseClusterSpecificCommand(JsonObject& json, uint8_t offset = 0); + void postProcessAttributes(uint16_t shortaddr, JsonObject& json); + + inline void setGroupId(uint16_t groupid) { + _group_id = groupid; + } + + inline void setClusterId(uint16_t clusterid) { + _cluster_id = clusterid; + } + + inline uint8_t getCmdId(void) const { + return _cmd_id; + } + + inline uint16_t getClusterId(void) const { + return _cluster_id; + } + + inline uint16_t getSrcEndpoint(void) const { + return _srcendpoint; + } + + const SBuffer &getPayload(void) const { + return _payload; + } + + uint16_t getManufCode(void) const { + return _manuf_code; + } + +private: + ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 }; + uint16_t _manuf_code = 0; + uint8_t _transact_seq = 0; + uint8_t _cmd_id = 0; + uint16_t _cluster_id = 0; + uint16_t _group_id = 0; + SBuffer _payload; + + uint16_t _srcaddr; + uint8_t _srcendpoint; + uint8_t _dstendpoint; + uint8_t _wasbroadcast; + uint8_t _linkquality; + uint8_t _securityuse; + uint8_t _seqnumber; + uint32_t _timestamp; +}; + + + + + + +uint8_t toPercentageCR2032(uint32_t voltage) { + uint32_t percentage; + if (voltage < 2100) { + percentage = 0; + } else if (voltage < 2440) { + percentage = 6 - ((2440 - voltage) * 6) / 340; + } else if (voltage < 2740) { + percentage = 18 - ((2740 - voltage) * 12) / 300; + } else if (voltage < 2900) { + percentage = 42 - ((2900 - voltage) * 24) / 160; + } else if (voltage < 3000) { + percentage = 100 - ((3000 - voltage) * 58) / 100; + } else if (voltage >= 3000) { + percentage = 100; + } + return percentage; +} + + +uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer &buf, + uint32_t offset, uint32_t len) { + + uint32_t i = offset; + uint32_t attrtype = buf.get8(i++); + + + json[attrid_str] = (char*) nullptr; + + + switch (attrtype) { + case 0x00: + case 0xFF: + break; + case 0x10: + { + uint8_t val_bool = buf.get8(i++); + if (0xFF != val_bool) { + json[attrid_str] = (bool) (val_bool ? true : false); + } + } + break; + case 0x20: + { + uint8_t uint8_val = buf.get8(i); + i += 1; + if (0xFF != uint8_val) { + json[attrid_str] = uint8_val; + } + } + break; + case 0x21: + { + uint16_t uint16_val = buf.get16(i); + i += 2; + if (0xFFFF != uint16_val) { + json[attrid_str] = uint16_val; + } + } + break; + case 0x23: + { + uint32_t uint32_val = buf.get32(i); + i += 4; + if (0xFFFFFFFF != uint32_val) { + json[attrid_str] = uint32_val; + } + } + break; + + case 0x24: + case 0x25: + case 0x26: + case 0x27: + { + uint8_t len = attrtype - 0x1F; + + char hex[2*len+1]; + ToHex_P(buf.buf(i), len, hex, sizeof(hex)); + json[attrid_str] = hex; + i += len; + } + break; + case 0x28: + { + int8_t int8_val = buf.get8(i); + i += 1; + if (0x80 != int8_val) { + json[attrid_str] = int8_val; + } + } + break; + case 0x29: + { + int16_t int16_val = buf.get16(i); + i += 2; + if (0x8000 != int16_val) { + json[attrid_str] = int16_val; + } + } + break; + case 0x2B: + { + int32_t int32_val = buf.get32(i); + i += 4; + if (0x80000000 != int32_val) { + json[attrid_str] = int32_val; + } + } + break; + + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + { + uint8_t len = attrtype - 0x27; + + char hex[2*len+1]; + ToHex_P(buf.buf(i), len, hex, sizeof(hex)); + json[attrid_str] = hex; + i += len; + } + break; + + case 0x41: + case 0x42: + case 0x43: + case 0x44: + + { + bool parse_as_string = true; + uint32_t len = (attrtype <= 0x42) ? buf.get8(i) : buf.get16(i); + i += (attrtype <= 0x42) ? 1 : 2; + if (i + len > buf.len()) { + len = buf.len() - i; + } + + + if ((0x41 == attrtype) || (0x43 == attrtype)) { parse_as_string = false; } +# 318 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" + if (parse_as_string) { + char str[len+1]; + strncpy(str, buf.charptr(i), len); + str[len] = 0x00; + json[attrid_str] = str; + } else { + + char hex[2*len+1]; + ToHex_P(buf.buf(i), len, hex, sizeof(hex)); + json[attrid_str] = hex; + } + + i += len; + break; + } + i += buf.get8(i) + 1; + break; + + case 0x08: + case 0x18: + { + uint8_t uint8_val = buf.get8(i); + i += 1; + json[attrid_str] = uint8_val; + } + break; + case 0x09: + case 0x19: + { + uint16_t uint16_val = buf.get16(i); + i += 2; + json[attrid_str] = uint16_val; + } + break; + case 0x0B: + case 0x1B: + { + uint32_t uint32_val = buf.get32(i); + i += 4; + json[attrid_str] = uint32_val; + } + break; + + case 0x30: + case 0x31: + i += attrtype - 0x2F; + break; + + + case 0x39: + { + uint32_t uint32_val = buf.get32(i); + float * float_val = (float*) &uint32_val; + i += 4; + json[attrid_str] = *float_val; + } + break; + + case 0xE0: + case 0xE1: + case 0xE2: + i += 4; + break; + + case 0xE8: + case 0xE9: + i += 2; + break; + case 0xEA: + i += 4; + break; + + case 0xF0: + i += 8; + break; + case 0xF1: + i += 16; + break; + + + case 0x0A: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + i += attrtype - 0x07; + break; + + case 0x1A: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + i += attrtype - 0x17; + break; + + case 0x38: + i += 2; + break; + case 0x3A: + { + uint64_t uint64_val = buf.get64(i); + double * double_val = (double*) &uint64_val; + i += 8; + json[attrid_str] = *double_val; + } + break; + } + + + + + + + return i - offset; +} + + +void ZCLFrame::generateAttributeName(const JsonObject& json, uint16_t cluster, uint16_t attr, char *key, size_t key_len) { + uint32_t suffix = 1; + + snprintf_P(key, key_len, PSTR("%04X/%04X"), cluster, attr); + while (json.containsKey(key)) { + suffix++; + snprintf_P(key, key_len, PSTR("%04X/%04X+%d"), cluster, attr, suffix); + } +} + + +void ZCLFrame::parseRawAttributes(JsonObject& json, uint8_t offset) { + uint32_t i = offset; + uint32_t len = _payload.len(); + + while (len >= i + 3) { + uint16_t attrid = _payload.get16(i); + i += 2; + + char key[16]; + generateAttributeName(json, _cluster_id, attrid, key, sizeof(key)); + + + if ((0x0000 == _cluster_id) && (0xFF01 == attrid)) { + if (0x42 == _payload.get8(i)) { + _payload.set8(i, 0x41); + } + } + i += parseSingleAttribute(json, key, _payload, i, len); + } +} + + +void ZCLFrame::parseReadAttributes(JsonObject& json, uint8_t offset) { + uint32_t i = offset; + uint32_t len = _payload.len(); + + while (len - i >= 4) { + uint16_t attrid = _payload.get16(i); + i += 2; + uint8_t status = _payload.get8(i++); + + if (0 == status) { + char key[16]; + generateAttributeName(json, _cluster_id, attrid, key, sizeof(key)); + + i += parseSingleAttribute(json, key, _payload, i, len); + } + } +} + + + + +void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) { + uint32_t i = offset; + uint32_t len = _payload.len(); + + char attrid_str[12]; + snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X!%02X"), _cluster_id, _cmd_id); + + char hex_char[_payload.len()*2+2]; + ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); + + json[attrid_str] = hex_char; +} + + + + +typedef int32_t (*Z_AttrConverter)(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); +typedef struct Z_AttributeConverter { + uint16_t cluster; + uint16_t attribute; + const char * name; + Z_AttrConverter func; +} Z_AttributeConverter; + + +const Z_AttributeConverter Z_PostProcess[] PROGMEM = { + { 0x0000, 0x0000, "ZCLVersion", &Z_Copy }, + { 0x0000, 0x0001, "AppVersion", &Z_Copy }, + { 0x0000, 0x0002, "StackVersion", &Z_Copy }, + { 0x0000, 0x0003, "HWVersion", &Z_Copy }, + { 0x0000, 0x0004, "Manufacturer", &Z_ManufKeep }, + { 0x0000, 0x0005, D_JSON_MODEL D_JSON_ID, &Z_ModelKeep }, + { 0x0000, 0x0006, "DateCode", &Z_Copy }, + { 0x0000, 0x0007, "PowerSource", &Z_Copy }, + { 0x0000, 0x4000, "SWBuildID", &Z_Copy }, + { 0x0000, 0xFFFF, nullptr, &Z_Remove }, + + { 0x0000, 0xFF01, nullptr, &Z_AqaraSensor }, + + + { 0x0001, 0x0000, "MainsVoltage", &Z_Copy }, + { 0x0001, 0x0001, "MainsFrequency", &Z_Copy }, + { 0x0001, 0x0020, "BatteryVoltage", &Z_Copy }, + { 0x0001, 0x0021, "BatteryPercentageRemaining",&Z_Copy }, + + + { 0x0002, 0x0000, "CurrentTemperature", &Z_Copy }, + { 0x0002, 0x0001, "MinTempExperienced", &Z_Copy }, + { 0x0002, 0x0002, "MaxTempExperienced", &Z_Copy }, + { 0x0002, 0x0003, "OverTempTotalDwell", &Z_Copy }, + + + { 0x0006, 0x0000, "Power", &Z_Copy }, + { 0x0006, 0x8000, "Power", &Z_Copy }, + + + { 0x0007, 0x0000, "SwitchType", &Z_Copy }, + + + { 0x0008, 0x0000, "Dimmer", &Z_Copy }, +# 558 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" + { 0x0009, 0x0000, "AlarmCount", &Z_Copy }, + + { 0x000A, 0x0000, "Time", &Z_Copy }, + { 0x000A, 0x0001, "TimeStatus", &Z_Copy }, + { 0x000A, 0x0002, "TimeZone", &Z_Copy }, + { 0x000A, 0x0003, "DstStart", &Z_Copy }, + { 0x000A, 0x0004, "DstStart", &Z_Copy }, + { 0x000A, 0x0005, "DstShift", &Z_Copy }, + { 0x000A, 0x0006, "StandardTime", &Z_Copy }, + { 0x000A, 0x0007, "LocalTime", &Z_Copy }, + { 0x000A, 0x0008, "LastSetTime", &Z_Copy }, + { 0x000A, 0x0009, "ValidUntilTime", &Z_Copy }, + + { 0x000B, 0x0000, "LocationType", &Z_Copy }, + { 0x000B, 0x0000, "LocationMethod", &Z_Copy }, + { 0x000B, 0x0000, "LocationAge", &Z_Copy }, + { 0x000B, 0x0000, "QualityMeasure", &Z_Copy }, + { 0x000B, 0x0000, "NumberOfDevices", &Z_Copy }, + + { 0x000C, 0x0004, "ActiveText", &Z_Copy }, + { 0x000C, 0x001C, "Description", &Z_Copy }, + { 0x000C, 0x002E, "InactiveText", &Z_Copy }, + { 0x000C, 0x0041, "MaxPresentValue", &Z_Copy }, + { 0x000C, 0x0045, "MinPresentValue", &Z_Copy }, + { 0x000C, 0x0051, "OutOfService", &Z_Copy }, + { 0x000C, 0x0055, "AqaraRotate", &Z_Copy }, + { 0x000C, 0x0057, "PriorityArray", &Z_Copy }, + { 0x000C, 0x0067, "Reliability", &Z_Copy }, + { 0x000C, 0x0068, "RelinquishDefault", &Z_Copy }, + { 0x000C, 0x006A, "Resolution", &Z_Copy }, + { 0x000C, 0x006F, "StatusFlags", &Z_Copy }, + { 0x000C, 0x0075, "EngineeringUnits", &Z_Copy }, + { 0x000C, 0x0100, "ApplicationType", &Z_Copy }, + { 0x000C, 0xFF05, "Aqara_FF05", &Z_Copy }, + + { 0x0010, 0x0004, "ActiveText", &Z_Copy }, + { 0x0010, 0x001C, "Description", &Z_Copy }, + { 0x0010, 0x002E, "InactiveText", &Z_Copy }, + { 0x0010, 0x0042, "MinimumOffTime", &Z_Copy }, + { 0x0010, 0x0043, "MinimumOnTime", &Z_Copy }, + { 0x0010, 0x0051, "OutOfService", &Z_Copy }, + { 0x0010, 0x0054, "Polarity", &Z_Copy }, + { 0x0010, 0x0055, "PresentValue", &Z_Copy }, + { 0x0010, 0x0057, "PriorityArray", &Z_Copy }, + { 0x0010, 0x0067, "Reliability", &Z_Copy }, + { 0x0010, 0x0068, "RelinquishDefault", &Z_Copy }, + { 0x0010, 0x006F, "StatusFlags", &Z_Copy }, + { 0x0010, 0x0100, "ApplicationType", &Z_Copy }, + + { 0x0011, 0x0004, "ActiveText", &Z_Copy }, + { 0x0011, 0x001C, "Description", &Z_Copy }, + { 0x0011, 0x002E, "InactiveText", &Z_Copy }, + { 0x0011, 0x0042, "MinimumOffTime", &Z_Copy }, + { 0x0011, 0x0043, "MinimumOnTime", &Z_Copy }, + { 0x0011, 0x0051, "OutOfService", &Z_Copy }, + { 0x0011, 0x0055, "PresentValue", &Z_Copy }, + { 0x0011, 0x0057, "PriorityArray", &Z_Copy }, + { 0x0011, 0x0067, "Reliability", &Z_Copy }, + { 0x0011, 0x0068, "RelinquishDefault", &Z_Copy }, + { 0x0011, 0x006F, "StatusFlags", &Z_Copy }, + { 0x0011, 0x0100, "ApplicationType", &Z_Copy }, + + { 0x0012, 0x000E, "StateText", &Z_Copy }, + { 0x0012, 0x001C, "Description", &Z_Copy }, + { 0x0012, 0x004A, "NumberOfStates", &Z_Copy }, + { 0x0012, 0x0051, "OutOfService", &Z_Copy }, + { 0x0012, 0x0055, "PresentValue", &Z_AqaraCube }, + { 0x0012, 0x0067, "Reliability", &Z_Copy }, + { 0x0012, 0x006F, "StatusFlags", &Z_Copy }, + { 0x0012, 0x0100, "ApplicationType", &Z_Copy }, + + { 0x0013, 0x000E, "StateText", &Z_Copy }, + { 0x0013, 0x001C, "Description", &Z_Copy }, + { 0x0013, 0x004A, "NumberOfStates", &Z_Copy }, + { 0x0013, 0x0051, "OutOfService", &Z_Copy }, + { 0x0013, 0x0055, "PresentValue", &Z_Copy }, + { 0x0013, 0x0057, "PriorityArray", &Z_Copy }, + { 0x0013, 0x0067, "Reliability", &Z_Copy }, + { 0x0013, 0x0068, "RelinquishDefault", &Z_Copy }, + { 0x0013, 0x006F, "StatusFlags", &Z_Copy }, + { 0x0013, 0x0100, "ApplicationType", &Z_Copy }, + + { 0x0014, 0x000E, "StateText", &Z_Copy }, + { 0x0014, 0x001C, "Description", &Z_Copy }, + { 0x0014, 0x004A, "NumberOfStates", &Z_Copy }, + { 0x0014, 0x0051, "OutOfService", &Z_Copy }, + { 0x0014, 0x0055, "PresentValue", &Z_Copy }, + { 0x0014, 0x0067, "Reliability", &Z_Copy }, + { 0x0014, 0x0068, "RelinquishDefault", &Z_Copy }, + { 0x0014, 0x006F, "StatusFlags", &Z_Copy }, + { 0x0014, 0x0100, "ApplicationType", &Z_Copy }, + + { 0x001A, 0x0000, "TotalProfileNum", &Z_Copy }, + { 0x001A, 0x0001, "MultipleScheduling", &Z_Copy }, + { 0x001A, 0x0002, "EnergyFormatting", &Z_Copy }, + { 0x001A, 0x0003, "EnergyRemote", &Z_Copy }, + { 0x001A, 0x0004, "ScheduleMode", &Z_Copy }, + + { 0x0020, 0x0000, "CheckinInterval", &Z_Copy }, + { 0x0020, 0x0001, "LongPollInterval", &Z_Copy }, + { 0x0020, 0x0002, "ShortPollInterval", &Z_Copy }, + { 0x0020, 0x0003, "FastPollTimeout", &Z_Copy }, + { 0x0020, 0x0004, "CheckinIntervalMin", &Z_Copy }, + { 0x0020, 0x0005, "LongPollIntervalMin", &Z_Copy }, + { 0x0020, 0x0006, "FastPollTimeoutMax", &Z_Copy }, + + { 0x0100, 0x0000, "PhysicalClosedLimit", &Z_Copy }, + { 0x0100, 0x0001, "MotorStepSize", &Z_Copy }, + { 0x0100, 0x0002, "Status", &Z_Copy }, + { 0x0100, 0x0010, "ClosedLimit", &Z_Copy }, + { 0x0100, 0x0011, "Mode", &Z_Copy }, + + { 0x0101, 0x0000, "LockState", &Z_Copy }, + { 0x0101, 0x0001, "LockType", &Z_Copy }, + { 0x0101, 0x0002, "ActuatorEnabled", &Z_Copy }, + { 0x0101, 0x0003, "DoorState", &Z_Copy }, + { 0x0101, 0x0004, "DoorOpenEvents", &Z_Copy }, + { 0x0101, 0x0005, "DoorClosedEvents", &Z_Copy }, + { 0x0101, 0x0006, "OpenPeriod", &Z_Copy }, + + { 0x0101, 0x0055, "AqaraVibrationMode", &Z_AqaraVibration }, + { 0x0101, 0x0503, "AqaraVibrationsOrAngle", &Z_Copy }, + { 0x0101, 0x0505, "AqaraVibration505", &Z_Copy }, + { 0x0101, 0x0508, "AqaraAccelerometer", &Z_AqaraVibration }, + + { 0x0102, 0x0000, "WindowCoveringType", &Z_Copy }, + { 0x0102, 0x0001, "PhysicalClosedLimitLift",&Z_Copy }, + { 0x0102, 0x0002, "PhysicalClosedLimitTilt",&Z_Copy }, + { 0x0102, 0x0003, "CurrentPositionLift", &Z_Copy }, + { 0x0102, 0x0004, "CurrentPositionTilt", &Z_Copy }, + { 0x0102, 0x0005, "NumberofActuationsLift",&Z_Copy }, + { 0x0102, 0x0006, "NumberofActuationsTilt",&Z_Copy }, + { 0x0102, 0x0007, "ConfigStatus", &Z_Copy }, + { 0x0102, 0x0008, "CurrentPositionLiftPercentage",&Z_Copy }, + { 0x0102, 0x0009, "CurrentPositionTiltPercentage",&Z_Copy }, + { 0x0102, 0x0010, "InstalledOpenLimitLift",&Z_Copy }, + { 0x0102, 0x0011, "InstalledClosedLimitLift",&Z_Copy }, + { 0x0102, 0x0012, "InstalledOpenLimitTilt", &Z_Copy }, + { 0x0102, 0x0013, "InstalledClosedLimitTilt", &Z_Copy }, + { 0x0102, 0x0014, "VelocityLift",&Z_Copy }, + { 0x0102, 0x0015, "AccelerationTimeLift",&Z_Copy }, + { 0x0102, 0x0016, "DecelerationTimeLift", &Z_Copy }, + { 0x0102, 0x0017, "Mode",&Z_Copy }, + { 0x0102, 0x0018, "IntermediateSetpointsLift",&Z_Copy }, + { 0x0102, 0x0019, "IntermediateSetpointsTilt",&Z_Copy }, + + + { 0x0300, 0x0000, "Hue", &Z_Copy }, + { 0x0300, 0x0001, "Sat", &Z_Copy }, + { 0x0300, 0x0002, "RemainingTime", &Z_Copy }, + { 0x0300, 0x0003, "X", &Z_Copy }, + { 0x0300, 0x0004, "Y", &Z_Copy }, + { 0x0300, 0x0005, "DriftCompensation", &Z_Copy }, + { 0x0300, 0x0006, "CompensationText", &Z_Copy }, + { 0x0300, 0x0007, "CT", &Z_Copy }, + { 0x0300, 0x0008, "ColorMode", &Z_Copy }, + { 0x0300, 0x0010, "NumberOfPrimaries", &Z_Copy }, + { 0x0300, 0x0011, "Primary1X", &Z_Copy }, + { 0x0300, 0x0012, "Primary1Y", &Z_Copy }, + { 0x0300, 0x0013, "Primary1Intensity", &Z_Copy }, + { 0x0300, 0x0015, "Primary2X", &Z_Copy }, + { 0x0300, 0x0016, "Primary2Y", &Z_Copy }, + { 0x0300, 0x0017, "Primary2Intensity", &Z_Copy }, + { 0x0300, 0x0019, "Primary3X", &Z_Copy }, + { 0x0300, 0x001A, "Primary3Y", &Z_Copy }, + { 0x0300, 0x001B, "Primary3Intensity", &Z_Copy }, + { 0x0300, 0x0030, "WhitePointX", &Z_Copy }, + { 0x0300, 0x0031, "WhitePointY", &Z_Copy }, + { 0x0300, 0x0032, "ColorPointRX", &Z_Copy }, + { 0x0300, 0x0033, "ColorPointRY", &Z_Copy }, + { 0x0300, 0x0034, "ColorPointRIntensity", &Z_Copy }, + { 0x0300, 0x0036, "ColorPointGX", &Z_Copy }, + { 0x0300, 0x0037, "ColorPointGY", &Z_Copy }, + { 0x0300, 0x0038, "ColorPointGIntensity", &Z_Copy }, + { 0x0300, 0x003A, "ColorPointBX", &Z_Copy }, + { 0x0300, 0x003B, "ColorPointBY", &Z_Copy }, + { 0x0300, 0x003C, "ColorPointBIntensity", &Z_Copy }, + + + { 0x0400, 0x0000, D_JSON_ILLUMINANCE, &Z_Copy }, + { 0x0400, 0x0001, "MinMeasuredValue", &Z_Copy }, + { 0x0400, 0x0002, "MaxMeasuredValue", &Z_Copy }, + { 0x0400, 0x0003, "Tolerance", &Z_Copy }, + { 0x0400, 0x0004, "LightSensorType", &Z_Copy }, + { 0x0400, 0xFFFF, nullptr, &Z_Remove }, + + + { 0x0401, 0x0000, "LevelStatus", &Z_Copy }, + { 0x0401, 0x0001, "LightSensorType", &Z_Copy }, + { 0x0401, 0xFFFF, nullptr, &Z_Remove }, + + + { 0x0402, 0x0000, D_JSON_TEMPERATURE, &Z_FloatDiv100 }, + { 0x0402, 0x0001, "MinMeasuredValue", &Z_FloatDiv100 }, + { 0x0402, 0x0002, "MaxMeasuredValue", &Z_FloatDiv100 }, + { 0x0402, 0x0003, "Tolerance", &Z_FloatDiv100 }, + { 0x0402, 0xFFFF, nullptr, &Z_Remove }, + + + { 0x0403, 0x0000, D_JSON_PRESSURE_UNIT, &Z_AddPressureUnit }, + { 0x0403, 0x0000, D_JSON_PRESSURE, &Z_Copy }, + { 0x0403, 0x0001, "MinMeasuredValue", &Z_Copy }, + { 0x0403, 0x0002, "MaxMeasuredValue", &Z_Copy }, + { 0x0403, 0x0003, "Tolerance", &Z_Copy }, + { 0x0403, 0x0010, "ScaledValue", &Z_Copy }, + { 0x0403, 0x0011, "MinScaledValue", &Z_Copy }, + { 0x0403, 0x0012, "MaxScaledValue", &Z_Copy }, + { 0x0403, 0x0013, "ScaledTolerance", &Z_Copy }, + { 0x0403, 0x0014, "Scale", &Z_Copy }, + { 0x0403, 0xFFFF, nullptr, &Z_Remove }, + + + { 0x0404, 0x0000, D_JSON_FLOWRATE, &Z_FloatDiv10 }, + { 0x0404, 0x0001, "MinMeasuredValue", &Z_Copy }, + { 0x0404, 0x0002, "MaxMeasuredValue", &Z_Copy }, + { 0x0404, 0x0003, "Tolerance", &Z_Copy }, + { 0x0404, 0xFFFF, nullptr, &Z_Remove }, + + + { 0x0405, 0x0000, D_JSON_HUMIDITY, &Z_FloatDiv100 }, + { 0x0405, 0x0001, "MinMeasuredValue", &Z_Copy }, + { 0x0405, 0x0002, "MaxMeasuredValue", &Z_Copy }, + { 0x0405, 0x0003, "Tolerance", &Z_Copy }, + { 0x0405, 0xFFFF, nullptr, &Z_Remove }, + + + { 0x0406, 0x0000, OCCUPANCY, &Z_Copy }, + { 0x0406, 0x0001, "OccupancySensorType", &Z_Copy }, + { 0x0406, 0xFFFF, nullptr, &Z_Remove }, + + + { 0x0B01, 0x0000, "CompanyName", &Z_Copy }, + { 0x0B01, 0x0001, "MeterTypeID", &Z_Copy }, + { 0x0B01, 0x0004, "DataQualityID", &Z_Copy }, + { 0x0B01, 0x0005, "CustomerName", &Z_Copy }, + { 0x0B01, 0x0006, "Model", &Z_Copy }, + { 0x0B01, 0x0007, "PartNumber", &Z_Copy }, + { 0x0B01, 0x000A, "SoftwareRevision", &Z_Copy }, + { 0x0B01, 0x000C, "POD", &Z_Copy }, + { 0x0B01, 0x000D, "AvailablePower", &Z_Copy }, + { 0x0B01, 0x000E, "PowerThreshold", &Z_Copy }, + + + { 0x0B05, 0x0000, "NumberOfResets", &Z_Copy }, + { 0x0B05, 0x0001, "PersistentMemoryWrites",&Z_Copy }, + { 0x0B05, 0x011C, "LastMessageLQI", &Z_Copy }, + { 0x0B05, 0x011D, "LastMessageRSSI", &Z_Copy }, + +}; + + + +int32_t Z_ManufKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = value; + zigbee_devices.setManufId(shortaddr, value.as()); + return 1; +} + +int32_t Z_ModelKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = value; + zigbee_devices.setModelId(shortaddr, value.as()); + return 1; +} + + + +int32_t Z_Remove(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + return 1; +} + + +int32_t Z_Copy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = value; + return 1; +} + + +int32_t Z_AddPressureUnit(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = F(D_UNIT_PRESSURE); + return 0; +} + + +int32_t Z_FloatDiv100(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = ((float)value) / 100.0f; + return 1; +} + +int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = ((float)value) / 10.0f; + return 1; +} + + +int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) { + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + json[F(OCCUPANCY)] = 0; + zigbee_devices.jsonPublishNow(shortaddr, json); +} + + +int32_t Z_AqaraCube(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + json[new_name] = value; + int32_t val = value; + const __FlashStringHelper *aqara_cube = F("AqaraCube"); + const __FlashStringHelper *aqara_cube_side = F("AqaraCubeSide"); + const __FlashStringHelper *aqara_cube_from_side = F("AqaraCubeFromSide"); + + switch (val) { + case 0: + json[aqara_cube] = F("shake"); + break; + case 2: + json[aqara_cube] = F("wakeup"); + break; + case 3: + json[aqara_cube] = F("fall"); + break; + case 64 ... 127: + json[aqara_cube] = F("flip90"); + json[aqara_cube_side] = val % 8; + json[aqara_cube_from_side] = (val - 64) / 8; + break; + case 128 ... 132: + json[aqara_cube] = F("flip180"); + json[aqara_cube_side] = val - 128; + break; + case 256 ... 261: + json[aqara_cube] = F("slide"); + json[aqara_cube_side] = val - 256; + break; + case 512 ... 517: + json[aqara_cube] = F("tap"); + json[aqara_cube_side] = val - 512; + break; + } +# 915 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" + return 1; +} + + +int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + + switch (attr) { + case 0x0055: + { + int32_t ivalue = value; + const __FlashStringHelper * svalue; + switch (ivalue) { + case 1: svalue = F("vibrate"); break; + case 2: svalue = F("tilt"); break; + case 3: svalue = F("drop"); break; + default: svalue = F("unknown"); break; + } + json[new_name] = svalue; + } + break; + + + + + case 0x0508: + { + + + String hex = value; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + int16_t x, y, z; + z = buf2.get16(0); + y = buf2.get16(2); + x = buf2.get16(4); + JsonArray& xyz = json.createNestedArray(new_name); + xyz.add(x); + xyz.add(y); + xyz.add(z); + + float X = x; + float Y = y; + float Z = z; + int32_t Angle_X = 0.5f + atanf(X/sqrtf(z*z+y*y)) * f_180pi; + int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(x*x+z*z)) * f_180pi; + int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(x*x+y*y)) * f_180pi; + + + + JsonArray& angles = json.createNestedArray(F("AqaraAngles")); + angles.add(Angle_X); + angles.add(Angle_Y); + angles.add(Angle_Z); + } + break; + } + return 1; +} + +int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { + String hex = value; + SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); + uint32_t i = 0; + uint32_t len = buf2.len(); + char tmp[] = "tmp"; + + JsonVariant sub_value; + + while (len - i >= 2) { + uint8_t attrid = buf2.get8(i++); + + i += parseSingleAttribute(json, tmp, buf2, i, len); + float val = json[tmp]; + json.remove(tmp); + if (0x01 == attrid) { + json[F(D_JSON_VOLTAGE)] = val / 1000.0f; + json[F("Battery")] = toPercentageCR2032(val); + } else if (0 == zcl->getManufCode()) { + + if (0x64 == attrid) { + json[F(D_JSON_TEMPERATURE)] = val / 100.0f; + } else if (0x65 == attrid) { + json[F(D_JSON_HUMIDITY)] = val / 100.0f; + } else if (0x66 == attrid) { + json[F(D_JSON_PRESSURE)] = val / 100.0f; + json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); + } else if (0x01 == attrid) { + json[F(D_JSON_VOLTAGE)] = val / 1000.0f; + json[F("Battery")] = toPercentageCR2032(val); + } + } else if (0x115F == zcl->getManufCode()) { + + json[F("AqaraUnknown")] = val; + } + } + return 1; +} + + +void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) { + + for (auto kv : json) { + String key_string = kv.key; + const char * key = key_string.c_str(); + JsonVariant& value = kv.value; + + char * delimiter = strchr(key, '/'); + char * delimiter2 = strchr(key, '+'); + if (delimiter) { + uint16_t attribute; + uint16_t suffix = 1; + uint16_t cluster = strtoul(key, &delimiter, 16); + if (!delimiter2) { + attribute = strtoul(delimiter+1, nullptr, 16); + } else { + attribute = strtoul(delimiter+1, &delimiter2, 16); + suffix = strtoul(delimiter2+1, nullptr, 10); + } + + + for (uint32_t i = 0; i < sizeof(Z_PostProcess) / sizeof(Z_PostProcess[0]); i++) { + const Z_AttributeConverter *converter = &Z_PostProcess[i]; + uint16_t conv_cluster = pgm_read_word(&converter->cluster); + uint16_t conv_attribute = pgm_read_word(&converter->attribute); + + if ((conv_cluster == cluster) && + ((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) { + String new_name_str = converter->name; + if (suffix > 1) { new_name_str += suffix; } + int32_t drop = (*converter->func)(this, shortaddr, json, key, value, new_name_str, conv_cluster, conv_attribute); + if (drop) { + json.remove(key); + } + + } + } + } + } +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" +#ifdef USE_ZIGBEE + + +typedef struct Z_CommandConverter { + const char * tasmota_cmd; + const char * zcl_cmd; +} Z_CommandConverter; + + +const Z_CommandConverter Z_Commands[] = { + { "Power", "0006!xx" }, + { "Dimmer", "0008!04/xx0A00" }, + { "Dimmer+", "0008!06/001902" }, + { "Dimmer-", "0008!06/011902" }, + { "DimmerStop", "0008!03" }, + { "ResetAlarm", "0009!00/xxyyyy" }, + { "ResetAllAlarms","0009!01" }, + { "Hue", "0300!00/xx000A00" }, + { "Sat", "0300!03/xx0A00" }, + { "HueSat", "0300!06/xxyy0A00" }, + { "Color", "0300!07/xxxxyyyy0A00" }, + { "CT", "0300!0A/xxxx0A00" }, + { "Shutter", "0102!xx" }, + { "ShutterOpen", "0102!00" }, + { "ShutterClose", "0102!01" }, + { "ShutterStop", "0102!02" }, + { "ShutterLift", "0102!05xx" }, + { "ShutterTilt", "0102!08xx" }, +}; + +#define ZLE(x) ((x) & 0xFF), ((x) >> 8) + + +const uint8_t CLUSTER_0006[] = { ZLE(0x0000) }; +const uint8_t CLUSTER_0008[] = { ZLE(0x0000) }; +const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; +const uint8_t CLUSTER_0300[] = { ZLE(0x0000), ZLE(0x0001), ZLE(0x0003), ZLE(0x0004), ZLE(0x0007) }; + +int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) { + size_t attrs_len = 0; + const uint8_t* attrs = nullptr; + + switch (cluster) { + case 0x0006: + attrs = CLUSTER_0006; + attrs_len = sizeof(CLUSTER_0006); + break; + case 0x0008: + attrs = CLUSTER_0008; + attrs_len = sizeof(CLUSTER_0008); + break; + case 0x0009: + attrs = CLUSTER_0009; + attrs_len = sizeof(CLUSTER_0009); + break; + case 0x0300: + attrs = CLUSTER_0300; + attrs_len = sizeof(CLUSTER_0300); + break; + } + if (attrs) { + ZigbeeZCLSend(shortaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false ); + } +} + + + +void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint) { + uint32_t wait_ms = 0; + + switch (cluster) { + case 0x0006: + case 0x0009: + wait_ms = 200; + break; + case 0x0008: + case 0x0300: + wait_ms = 1050; + break; + case 0x0102: + wait_ms = 10000; + break; + } + if (wait_ms) { + zigbee_devices.setTimer(shortaddr, wait_ms, cluster, endpoint, 0 , &Z_ReadAttrCallback); + } +} + +const __FlashStringHelper* zigbeeFindCommand(const char *command) { + char parm_uc[16]; + for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { + const Z_CommandConverter *conv = &Z_Commands[i]; + if (0 == strcasecmp_P(command, conv->tasmota_cmd)) { + return (const __FlashStringHelper*) conv->zcl_cmd; + } + } + + return nullptr; +} + +inline bool isXYZ(char c) { + return (c >= 'x') && (c <= 'z'); +} + + +inline char hexDigit(uint32_t h) { + uint32_t nybble = h & 0x0F; + return (nybble > 9) ? 'A' - 10 + nybble : '0' + nybble; +} + + +String zigbeeCmdAddParams(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t z) { + size_t len = strlen_P(zcl_cmd_P); + char zcl_cmd[len+1]; + strcpy_P(zcl_cmd, zcl_cmd_P); + + char *p = zcl_cmd; + while (*p) { + if (isXYZ(*p) && (*p == *(p+1))) { + uint8_t val; + switch (*p) { + case 'x': + val = x & 0xFF; + x = x >> 8; + break; + case 'y': + val = y & 0xFF; + y = y >> 8; + break; + case 'z': + val = z & 0xFF; + z = z >> 8; + break; + } + *p = hexDigit(val >> 4); + *(p+1) = hexDigit(val); + p++; + } + p++; + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SendZCLCommand_P: zcl_cmd = %s"), zcl_cmd); + + return String(zcl_cmd); +} + +const char kZ_Alias[] PROGMEM = "OFF|" D_OFF "|" D_FALSE "|" D_STOP "|" "OPEN" "|" + "ON|" D_ON "|" D_TRUE "|" D_START "|" "CLOSE" "|" + "TOGGLE|" D_TOGGLE "|" + "ALL" ; + +const uint8_t kZ_Numbers[] PROGMEM = { 0,0,0,0,0, + 1,1,1,1,1, + 2,2, + 255 }; + + +uint32_t ZigbeeAliasOrNumber(const char *state_text) { + char command[16]; + int state_number = GetCommandCode(command, sizeof(command), state_text, kZ_Alias); + if (state_number >= 0) { + + return pgm_read_byte(kZ_Numbers + state_number); + } else { + + return strtoul(state_text, nullptr, 0); + } +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" +#ifdef USE_ZIGBEE + + + +const uint8_t ZIGBEE_STATUS_OK = 0; +const uint8_t ZIGBEE_STATUS_BOOT = 1; +const uint8_t ZIGBEE_STATUS_RESET_CONF = 2; +const uint8_t ZIGBEE_STATUS_STARTING = 3; +const uint8_t ZIGBEE_STATUS_PERMITJOIN_CLOSE = 20; +const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_60 = 21; +const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_XX = 22; +const uint8_t ZIGBEE_STATUS_DEVICE_ANNOUNCE = 30; +const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; +const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; +const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; +const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; +const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; +const uint8_t ZIGBEE_STATUS_CC_INFO = 51; +const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; +const uint8_t ZIGBEE_STATUS_ABORT = 99; + +typedef int32_t (*ZB_Func)(uint8_t value); +typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, const class SBuffer &buf); + +typedef union Zigbee_Instruction { + struct { + uint8_t i; + uint8_t d8; + uint16_t d16; + } i; + const void *p; + + + +} Zigbee_Instruction; + + + + +typedef struct Zigbee_Instruction_Type { + uint8_t instr; + uint8_t data; +} Zigbee_Instruction_Type; + +enum Zigbee_StateMachine_Instruction_Set { + + ZGB_INSTR_4_BYTES = 0, + ZGB_INSTR_NOOP = 0, + ZGB_INSTR_LABEL, + ZGB_INSTR_GOTO, + ZGB_INSTR_ON_ERROR_GOTO, + ZGB_INSTR_ON_TIMEOUT_GOTO, + ZGB_INSTR_WAIT, + ZGB_INSTR_WAIT_FOREVER, + ZGB_INSTR_STOP, + + + ZGB_INSTR_8_BYTES = 0x80, + ZGB_INSTR_CALL = 0x80, + ZGB_INSTR_LOG, + ZGB_INSTR_MQTT_STATE, + ZGB_INSTR_SEND, + ZGB_INSTR_WAIT_UNTIL, + ZGB_INSTR_WAIT_RECV, + ZGB_ON_RECV_UNEXPECTED, + + + ZGB_INSTR_12_BYTES = 0xF0, + ZGB_INSTR_WAIT_RECV_CALL, +}; + +#define ZI_NOOP() { .i = { ZGB_INSTR_NOOP, 0x00, 0x0000} }, +#define ZI_LABEL(x) { .i = { ZGB_INSTR_LABEL, (x), 0x0000} }, +#define ZI_GOTO(x) { .i = { ZGB_INSTR_GOTO, (x), 0x0000} }, +#define ZI_ON_ERROR_GOTO(x) { .i = { ZGB_INSTR_ON_ERROR_GOTO, (x), 0x0000} }, +#define ZI_ON_TIMEOUT_GOTO(x) { .i = { ZGB_INSTR_ON_TIMEOUT_GOTO, (x), 0x0000} }, +#define ZI_WAIT(x) { .i = { ZGB_INSTR_WAIT, 0x00, (x)} }, +#define ZI_WAIT_FOREVER() { .i = { ZGB_INSTR_WAIT_FOREVER, 0x00, 0x0000} }, +#define ZI_STOP(x) { .i = { ZGB_INSTR_STOP, (x), 0x0000} }, + +#define ZI_CALL(f,x) { .i = { ZGB_INSTR_CALL, (x), 0x0000} }, { .p = (const void*)(f) }, +#define ZI_LOG(x,m) { .i = { ZGB_INSTR_LOG, (x), 0x0000 } }, { .p = ((const void*)(m)) }, +#define ZI_MQTT_STATE(x,m) { .i = { ZGB_INSTR_MQTT_STATE, (x), 0x0000 } }, { .p = ((const void*)(m)) }, +#define ZI_ON_RECV_UNEXPECTED(f) { .i = { ZGB_ON_RECV_UNEXPECTED, 0x00, 0x0000} }, { .p = (const void*)(f) }, +#define ZI_SEND(m) { .i = { ZGB_INSTR_SEND, sizeof(m), 0x0000} }, { .p = (const void*)(m) }, +#define ZI_WAIT_RECV(x,m) { .i = { ZGB_INSTR_WAIT_RECV, sizeof(m), (x)} }, { .p = (const void*)(m) }, +#define ZI_WAIT_UNTIL(x,m) { .i = { ZGB_INSTR_WAIT_UNTIL, sizeof(m), (x)} }, { .p = (const void*)(m) }, +#define ZI_WAIT_RECV_FUNC(x,m,f) { .i = { ZGB_INSTR_WAIT_RECV_CALL, sizeof(m), (x)} }, { .p = (const void*)(m) }, { .p = (const void*)(f) }, + + +const uint8_t ZIGBEE_LABEL_START = 10; +const uint8_t ZIGBEE_LABEL_READY = 20; +const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21; +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_CLOSE = 30; +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60 = 31; +const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX = 32; + +const uint8_t ZIGBEE_LABEL_ABORT = 99; +const uint8_t ZIGBEE_LABEL_UNSUPPORTED_VERSION = 98; + +struct ZigbeeStatus { + bool active = true; + bool state_machine = false; + bool state_waiting = false; + bool state_no_timeout = false; + bool ready = false; + uint8_t on_error_goto = ZIGBEE_LABEL_ABORT; + uint8_t on_timeout_goto = ZIGBEE_LABEL_ABORT; + int16_t pc = 0; + uint32_t next_timeout = 0; + + uint8_t *recv_filter = nullptr; + bool recv_until = false; + size_t recv_filter_len = 0; + ZB_RecvMsgFunc recv_func = nullptr; + ZB_RecvMsgFunc recv_unexpected = nullptr; + + bool init_phase = true; +}; +struct ZigbeeStatus zigbee; + +SBuffer *zigbee_buffer = nullptr; + + + + + +#define Z_B0(a) (uint8_t)( ((a) ) & 0xFF ) +#define Z_B1(a) (uint8_t)( ((a) >> 8) & 0xFF ) +#define Z_B2(a) (uint8_t)( ((a) >> 16) & 0xFF ) +#define Z_B3(a) (uint8_t)( ((a) >> 24) & 0xFF ) +#define Z_B4(a) (uint8_t)( ((a) >> 32) & 0xFF ) +#define Z_B5(a) (uint8_t)( ((a) >> 40) & 0xFF ) +#define Z_B6(a) (uint8_t)( ((a) >> 48) & 0xFF ) +#define Z_B7(a) (uint8_t)( ((a) >> 56) & 0xFF ) + +#define ZBM(n,x...) const uint8_t n[] PROGMEM = { x }; + +#define USE_ZIGBEE_CHANNEL_MASK (1 << (USE_ZIGBEE_CHANNEL)) + + + +ZBM(ZBS_RESET, Z_AREQ | Z_SYS, SYS_RESET, 0x00 ) +ZBM(ZBR_RESET, Z_AREQ | Z_SYS, SYS_RESET_IND ) + +ZBM(ZBS_VERSION, Z_SREQ | Z_SYS, SYS_VERSION ) +ZBM(ZBR_VERSION, Z_SRSP | Z_SYS, SYS_VERSION ) + + +ZBM(ZBS_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, 0x00 ) +ZBM(ZBR_ZNPHC, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_Success, 0x01 , 0x55) + + +ZBM(ZBS_PAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PANID ) +ZBM(ZBR_PAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_PANID, 0x02 , + Z_B0(USE_ZIGBEE_PANID), Z_B1(USE_ZIGBEE_PANID) ) + +ZBM(ZBS_EXTPAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_EXTENDED_PAN_ID ) +ZBM(ZBR_EXTPAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_EXTENDED_PAN_ID, + 0x08 , + Z_B0(USE_ZIGBEE_EXTPANID), Z_B1(USE_ZIGBEE_EXTPANID), Z_B2(USE_ZIGBEE_EXTPANID), Z_B3(USE_ZIGBEE_EXTPANID), + Z_B4(USE_ZIGBEE_EXTPANID), Z_B5(USE_ZIGBEE_EXTPANID), Z_B6(USE_ZIGBEE_EXTPANID), Z_B7(USE_ZIGBEE_EXTPANID), + ) + +ZBM(ZBS_CHANN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_CHANLIST ) +ZBM(ZBR_CHANN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_CHANLIST, + 0x04 , + Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK), + ) + +ZBM(ZBS_PFGK, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEY ) +ZBM(ZBR_PFGK, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_PRECFGKEY, + 0x10 , + Z_B0(USE_ZIGBEE_PRECFGKEY_L), Z_B1(USE_ZIGBEE_PRECFGKEY_L), Z_B2(USE_ZIGBEE_PRECFGKEY_L), Z_B3(USE_ZIGBEE_PRECFGKEY_L), + Z_B4(USE_ZIGBEE_PRECFGKEY_L), Z_B5(USE_ZIGBEE_PRECFGKEY_L), Z_B6(USE_ZIGBEE_PRECFGKEY_L), Z_B7(USE_ZIGBEE_PRECFGKEY_L), + Z_B0(USE_ZIGBEE_PRECFGKEY_H), Z_B1(USE_ZIGBEE_PRECFGKEY_H), Z_B2(USE_ZIGBEE_PRECFGKEY_H), Z_B3(USE_ZIGBEE_PRECFGKEY_H), + Z_B4(USE_ZIGBEE_PRECFGKEY_H), Z_B5(USE_ZIGBEE_PRECFGKEY_H), Z_B6(USE_ZIGBEE_PRECFGKEY_H), Z_B7(USE_ZIGBEE_PRECFGKEY_H), + + ) + +ZBM(ZBS_PFGKEN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEYS_ENABLE ) +ZBM(ZBR_PFGKEN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_PRECFGKEYS_ENABLE, + 0x01 , 0x00 ) + + + +ZBM(ZBR_W_OK, Z_SRSP | Z_SAPI, SAPI_WRITE_CONFIGURATION, Z_Success ) +ZBM(ZBR_WNV_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE, Z_Success ) + + +ZBM(ZBS_FACTRES, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 , 0x02 ) + +ZBM(ZBS_W_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 , Z_B0(USE_ZIGBEE_PANID), Z_B1(USE_ZIGBEE_PANID) ) + +ZBM(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_ID, 0x08 , + Z_B0(USE_ZIGBEE_EXTPANID), Z_B1(USE_ZIGBEE_EXTPANID), Z_B2(USE_ZIGBEE_EXTPANID), Z_B3(USE_ZIGBEE_EXTPANID), + Z_B4(USE_ZIGBEE_EXTPANID), Z_B5(USE_ZIGBEE_EXTPANID), Z_B6(USE_ZIGBEE_EXTPANID), Z_B7(USE_ZIGBEE_EXTPANID) + ) + +ZBM(ZBS_W_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 , + Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK), + ) + +ZBM(ZBS_W_LOGTYP, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 , 0x00 ) + +ZBM(ZBS_W_PFGK, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEY, + 0x10 , + Z_B0(USE_ZIGBEE_PRECFGKEY_L), Z_B1(USE_ZIGBEE_PRECFGKEY_L), Z_B2(USE_ZIGBEE_PRECFGKEY_L), Z_B3(USE_ZIGBEE_PRECFGKEY_L), + Z_B4(USE_ZIGBEE_PRECFGKEY_L), Z_B5(USE_ZIGBEE_PRECFGKEY_L), Z_B6(USE_ZIGBEE_PRECFGKEY_L), Z_B7(USE_ZIGBEE_PRECFGKEY_L), + Z_B0(USE_ZIGBEE_PRECFGKEY_H), Z_B1(USE_ZIGBEE_PRECFGKEY_H), Z_B2(USE_ZIGBEE_PRECFGKEY_H), Z_B3(USE_ZIGBEE_PRECFGKEY_H), + Z_B4(USE_ZIGBEE_PRECFGKEY_H), Z_B5(USE_ZIGBEE_PRECFGKEY_H), Z_B6(USE_ZIGBEE_PRECFGKEY_H), Z_B7(USE_ZIGBEE_PRECFGKEY_H), + + ) + +ZBM(ZBS_W_PFGKEN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEYS_ENABLE, 0x01 , 0x00 ) + +ZBM(ZBS_WNV_SECMODE, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(CONF_TCLK_TABLE_START), Z_B1(CONF_TCLK_TABLE_START), + 0x00 , 0x20 , + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x5a, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6c, + 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + +ZBM(ZBS_W_ZDODCB, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_ZDO_DIRECT_CB, 0x01 , 0x01 ) + +ZBM(ZBS_WNV_INITZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_ITEM_INIT, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, + 0x01, 0x00 , 0x01 , 0x00 ) + + +ZBM(ZBR_WNV_INIT_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_ITEM_INIT ) + + +ZBM(ZBS_WNV_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(ZNP_HAS_CONFIGURED), Z_B1(ZNP_HAS_CONFIGURED), + 0x00 , 0x01 , 0x55 ) + +ZBM(ZBS_STARTUPFROMAPP, Z_SREQ | Z_ZDO, ZDO_STARTUP_FROM_APP, 100, 0 ) +ZBM(ZBR_STARTUPFROMAPP, Z_SRSP | Z_ZDO, ZDO_STARTUP_FROM_APP ) +ZBM(AREQ_STARTUPFROMAPP, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND, ZDO_DEV_ZB_COORD ) + +ZBM(ZBS_GETDEVICEINFO, Z_SREQ | Z_UTIL, Z_UTIL_GET_DEVICE_INFO ) +ZBM(ZBR_GETDEVICEINFO, Z_SRSP | Z_UTIL, Z_UTIL_GET_DEVICE_INFO, Z_Success ) +# 271 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" +ZBM(ZBS_ZDO_NODEDESCREQ, Z_SREQ | Z_ZDO, ZDO_NODE_DESC_REQ, 0x00, 0x00 , 0x00, 0x00 ) +ZBM(ZBR_ZDO_NODEDESCREQ, Z_SRSP | Z_ZDO, ZDO_NODE_DESC_REQ, Z_Success ) + +ZBM(AREQ_ZDO_NODEDESCRSP, Z_AREQ | Z_ZDO, ZDO_NODE_DESC_RSP) +# 289 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" +ZBM(ZBS_ZDO_ACTIVEEPREQ, Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, 0x00, 0x00, 0x00, 0x00) +ZBM(ZBR_ZDO_ACTIVEEPREQ, Z_SRSP | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_Success) +ZBM(ZBR_ZDO_ACTIVEEPRSP_NONE, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 , Z_Success, + 0x00, 0x00 , 0x00 ) +ZBM(ZBR_ZDO_ACTIVEEPRSP_OK, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 , Z_Success, + 0x00, 0x00 , 0x02 , 0x0B, 0x01 ) + + +ZBM(ZBS_AF_REGISTER01, Z_SREQ | Z_AF, AF_REGISTER, 0x01 , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), + 0x05, 0x00 , 0x00 , 0x00 , + 0x00 , 0x00 ) +ZBM(ZBR_AF_REGISTER, Z_SRSP | Z_AF, AF_REGISTER, Z_Success) +ZBM(ZBS_AF_REGISTER0B, Z_SREQ | Z_AF, AF_REGISTER, 0x0B , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), + 0x05, 0x00 , 0x00 , 0x00 , + 0x00 , 0x00 ) + +ZBM(ZBS_PERMITJOINREQ_CLOSE, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x02 , + 0x00, 0x00 , 0x00 , 0x00 ) +ZBM(ZBS_PERMITJOINREQ_OPEN_60, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x0F , + 0xFC, 0xFF , 60 , 0x00 ) +ZBM(ZBS_PERMITJOINREQ_OPEN_XX, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x0F , + 0xFC, 0xFF , 0xFF , 0x00 ) +ZBM(ZBR_PERMITJOINREQ, Z_SRSP | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, Z_Success) +ZBM(ZBR_PERMITJOIN_AREQ_CLOSE, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0x00 ) +ZBM(ZBR_PERMITJOIN_AREQ_OPEN_60, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 60 ) +ZBM(ZBR_PERMITJOIN_AREQ_OPEN_FF, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0xFF ) +ZBM(ZBR_PERMITJOIN_AREQ_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_RSP, 0x00, 0x00 , Z_Success ) + +static const Zigbee_Instruction zb_prog[] PROGMEM = { + ZI_LABEL(0) + ZI_NOOP() + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT) + ZI_ON_RECV_UNEXPECTED(&Z_Recv_Default) + ZI_WAIT(10500) + ZI_ON_ERROR_GOTO(50) + + + + ZI_SEND(ZBS_RESET) + ZI_WAIT_RECV_FUNC(5000, ZBR_RESET, &Z_Reboot) + ZI_WAIT(100) + ZI_LOG(LOG_LEVEL_DEBUG, D_LOG_ZIGBEE "checking device configuration") + ZI_SEND(ZBS_ZNPHC) + ZI_WAIT_RECV(2000, ZBR_ZNPHC) + ZI_SEND(ZBS_VERSION) + ZI_WAIT_RECV_FUNC(2000, ZBR_VERSION, &Z_ReceiveCheckVersion) + ZI_SEND(ZBS_PAN) + ZI_WAIT_RECV(1000, ZBR_PAN) + ZI_SEND(ZBS_EXTPAN) + ZI_WAIT_RECV(1000, ZBR_EXTPAN) + ZI_SEND(ZBS_CHANN) + ZI_WAIT_RECV(1000, ZBR_CHANN) + ZI_SEND(ZBS_PFGK) + ZI_WAIT_RECV(1000, ZBR_PFGK) + ZI_SEND(ZBS_PFGKEN) + ZI_WAIT_RECV(1000, ZBR_PFGKEN) + + + + ZI_LABEL(ZIGBEE_LABEL_START) + ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, "Configured, starting coordinator") + + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + + +ZI_SEND(ZBS_STARTUPFROMAPP) + ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) + ZI_WAIT_UNTIL(10000, AREQ_STARTUPFROMAPP) + ZI_SEND(ZBS_GETDEVICEINFO) + ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo) + + ZI_SEND(ZBS_ZDO_NODEDESCREQ) + ZI_WAIT_RECV(1000, ZBR_ZDO_NODEDESCREQ) + ZI_WAIT_UNTIL(5000, AREQ_ZDO_NODEDESCRSP) + ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) + ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) + ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_NONE) + ZI_SEND(ZBS_AF_REGISTER01) + ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) + ZI_SEND(ZBS_AF_REGISTER0B) + ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) + + + ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) + ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) + ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_OK) + ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) + + + + + + + ZI_LABEL(ZIGBEE_LABEL_READY) + ZI_MQTT_STATE(ZIGBEE_STATUS_OK, "Started") + ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "Zigbee started") + ZI_CALL(&Z_State_Ready, 1) + ZI_CALL(&Z_Load_Devices, 0) + ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP) + ZI_WAIT_FOREVER() + ZI_GOTO(ZIGBEE_LABEL_READY) + + ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_CLOSE) + + ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + + + ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) + + ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60) + + ZI_SEND(ZBS_PERMITJOINREQ_OPEN_60) + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + + + ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) + + ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX) + + ZI_SEND(ZBS_PERMITJOINREQ_OPEN_XX) + ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) + + + ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) + + ZI_LABEL(50) + ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, "Reseting configuration") + + ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) + ZI_SEND(ZBS_FACTRES) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_RESET) + ZI_WAIT_RECV(5000, ZBR_RESET) + ZI_SEND(ZBS_W_PAN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_EXTPAN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_CHANN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_LOGTYP) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_PFGK) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_W_PFGKEN) + ZI_WAIT_RECV(1000, ZBR_W_OK) + ZI_SEND(ZBS_WNV_SECMODE) + ZI_WAIT_RECV(1000, ZBR_WNV_OK) + ZI_SEND(ZBS_W_ZDODCB) + ZI_WAIT_RECV(1000, ZBR_W_OK) + + ZI_SEND(ZBS_WNV_INITZNPHC) + ZI_WAIT_RECV_FUNC(1000, ZBR_WNV_INIT_OK, &Z_CheckNVWrite) + ZI_SEND(ZBS_WNV_ZNPHC) + ZI_WAIT_RECV(1000, ZBR_WNV_OK) + + + ZI_GOTO(ZIGBEE_LABEL_START) + + ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION) + ZI_MQTT_STATE(ZIGBEE_STATUS_UNSUPPORTED_VERSION, "Only ZNP 1.2 is currently supported") + ZI_GOTO(ZIGBEE_LABEL_ABORT) + + ZI_LABEL(ZIGBEE_LABEL_ABORT) + ZI_MQTT_STATE(ZIGBEE_STATUS_ABORT, "Abort") + ZI_LOG(LOG_LEVEL_ERROR, D_LOG_ZIGBEE "Abort") + ZI_STOP(ZIGBEE_LABEL_ABORT) +}; + +uint8_t ZigbeeGetInstructionSize(uint8_t instr) { + if (instr >= ZGB_INSTR_12_BYTES) { + return 3; + } else if (instr >= ZGB_INSTR_8_BYTES) { + return 2; + } else { + return 1; + } +} + +void ZigbeeGotoLabel(uint8_t label) { + + uint16_t goto_pc = 0xFFFF; + uint8_t cur_instr = 0; + uint8_t cur_d8 = 0; + uint8_t cur_instr_len = 1; + + for (uint32_t i = 0; i < sizeof(zb_prog)/sizeof(zb_prog[0]); i += cur_instr_len) { + const Zigbee_Instruction *cur_instr_line = &zb_prog[i]; + cur_instr = pgm_read_byte(&cur_instr_line->i.i); + cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); + + + if (ZGB_INSTR_LABEL == cur_instr) { + + if (label == cur_d8) { + + zigbee.pc = i; + zigbee.state_machine = true; + zigbee.state_waiting = false; + return; + } + } + + cur_instr_len = ZigbeeGetInstructionSize(cur_instr); + } + + + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Goto label not found, label=%d pc=%d"), label, zigbee.pc); + if (ZIGBEE_LABEL_ABORT != label) { + + ZigbeeGotoLabel(ZIGBEE_LABEL_ABORT); + } else { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Label Abort (%d) not present, aborting Zigbee"), ZIGBEE_LABEL_ABORT); + zigbee.state_machine = false; + zigbee.active = false; + } +} + +void ZigbeeStateMachine_Run(void) { + uint8_t cur_instr = 0; + uint8_t cur_d8 = 0; + uint16_t cur_d16 = 0; + const void* cur_ptr1 = nullptr; + const void* cur_ptr2 = nullptr; + uint32_t now = millis(); + + if (zigbee.state_waiting) { + + if ((zigbee.next_timeout) && (now > zigbee.next_timeout)) { + + if (!zigbee.state_no_timeout) { + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "timeout, goto label %d"), zigbee.on_timeout_goto); + ZigbeeGotoLabel(zigbee.on_timeout_goto); + } else { + zigbee.state_waiting = false; + } + } + } + + while ((zigbee.state_machine) && (!zigbee.state_waiting)) { + + zigbee.recv_filter = nullptr; + zigbee.recv_func = nullptr; + zigbee.recv_until = false; + zigbee.state_no_timeout = false; + + if (zigbee.pc > (sizeof(zb_prog)/sizeof(zb_prog[0]))) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Invalid pc: %d, aborting"), zigbee.pc); + zigbee.pc = -1; + } + if (zigbee.pc < 0) { + zigbee.state_machine = false; + return; + } + + + + const Zigbee_Instruction *cur_instr_line = &zb_prog[zigbee.pc]; + cur_instr = pgm_read_byte(&cur_instr_line->i.i); + cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); + cur_d16 = pgm_read_word(&cur_instr_line->i.d16); + if (cur_instr >= ZGB_INSTR_8_BYTES) { + cur_instr_line++; + cur_ptr1 = cur_instr_line->p; + } + if (cur_instr >= ZGB_INSTR_12_BYTES) { + cur_instr_line++; + cur_ptr2 = cur_instr_line->p; + } + + zigbee.pc += ZigbeeGetInstructionSize(cur_instr); + + switch (cur_instr) { + case ZGB_INSTR_NOOP: + case ZGB_INSTR_LABEL: + break; + case ZGB_INSTR_GOTO: + ZigbeeGotoLabel(cur_d8); + break; + case ZGB_INSTR_ON_ERROR_GOTO: + zigbee.on_error_goto = cur_d8; + break; + case ZGB_INSTR_ON_TIMEOUT_GOTO: + zigbee.on_timeout_goto = cur_d8; + break; + case ZGB_INSTR_WAIT: + zigbee.next_timeout = now + cur_d16; + zigbee.state_waiting = true; + zigbee.state_no_timeout = true; + break; + case ZGB_INSTR_WAIT_FOREVER: + zigbee.next_timeout = 0; + zigbee.state_waiting = true; + + break; + case ZGB_INSTR_STOP: + zigbee.state_machine = false; + if (cur_d8) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Stopping (%d)"), cur_d8); + } + break; + case ZGB_INSTR_CALL: + if (cur_ptr1) { + uint32_t res; + res = (*((ZB_Func)cur_ptr1))(cur_d8); + if (res > 0) { + ZigbeeGotoLabel(res); + continue; + } else if (res == 0) { + + } else if (res == -1) { + + } else { + ZigbeeGotoLabel(zigbee.on_error_goto); + continue; + } + } + break; + case ZGB_INSTR_LOG: + AddLog_P(cur_d8, (char*) cur_ptr1); + break; + case ZGB_INSTR_MQTT_STATE: + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{\"Status\":%d,\"Message\":\"%s\"}}"), + cur_d8, (char*) cur_ptr1); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); + break; + case ZGB_INSTR_SEND: + ZigbeeZNPSend((uint8_t*) cur_ptr1, cur_d8 ); + break; + case ZGB_INSTR_WAIT_UNTIL: + zigbee.recv_until = true; + case ZGB_INSTR_WAIT_RECV: + zigbee.recv_filter = (uint8_t *) cur_ptr1; + zigbee.recv_filter_len = cur_d8; + zigbee.next_timeout = now + cur_d16; + zigbee.state_waiting = true; + break; + case ZGB_ON_RECV_UNEXPECTED: + zigbee.recv_unexpected = (ZB_RecvMsgFunc) cur_ptr1; + break; + case ZGB_INSTR_WAIT_RECV_CALL: + zigbee.recv_filter = (uint8_t *) cur_ptr1; + zigbee.recv_filter_len = cur_d8; + zigbee.recv_func = (ZB_RecvMsgFunc) cur_ptr2; + zigbee.next_timeout = now + cur_d16; + zigbee.state_waiting = true; + break; + } + } +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" +#ifdef USE_ZIGBEE + +int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) { + + + + + + + + Z_IEEEAddress long_adr = buf.get64(3); + Z_ShortAddress short_adr = buf.get16(11); + uint8_t device_type = buf.get8(13); + uint8_t device_state = buf.get8(14); + uint8_t device_associated = buf.get8(15); + + + localIEEEAddr = long_adr; + + char hex[20]; + Uint64toHex(long_adr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" + ",\"DeviceType\":%d,\"DeviceState\":%d" + ",\"NumAssocDevices\":%d"), + ZIGBEE_STATUS_CC_INFO, hex, short_adr, device_type, device_state, + device_associated); + + if (device_associated > 0) { + uint idx = 16; + ResponseAppend_P(PSTR(",\"AssocDevicesList\":[")); + for (uint32_t i = 0; i < device_associated; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(idx)); + idx += 2; + } + ResponseAppend_P(PSTR("]")); + } + + ResponseJsonEnd(); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); + + return res; +} + +int32_t Z_CheckNVWrite(int32_t res, class SBuffer &buf) { + + + + uint8_t status = buf.get8(2); + if ((0x00 == status) || (0x09 == status)) { + return 0; + } else { + return -2; + } +} + +const char Z_RebootReason[] PROGMEM = "Power-up|External|Watchdog"; + +int32_t Z_Reboot(int32_t res, class SBuffer &buf) { + + + + uint8_t reason = buf.get8(2); + uint8_t transport_rev = buf.get8(3); + uint8_t product_id = buf.get8(4); + uint8_t major_rel = buf.get8(5); + uint8_t minor_rel = buf.get8(6); + uint8_t hw_rev = buf.get8(7); + char reason_str[12]; + + if (reason > 3) { reason = 3; } + GetTextIndexed(reason_str, sizeof(reason_str), reason, Z_RebootReason); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Message\":\"%s\",\"RestartReason\":\"%s\"" + ",\"MajorRel\":%d,\"MinorRel\":%d}}"), + ZIGBEE_STATUS_BOOT, "CC2530 booted", reason_str, + major_rel, minor_rel); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); + + if ((0x02 == major_rel) && (0x06 == minor_rel)) { + return 0; + } else { + return ZIGBEE_LABEL_UNSUPPORTED_VERSION; + } +} + +int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf) { +# 122 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" + uint8_t major_rel = buf.get8(4); + uint8_t minor_rel = buf.get8(5); + uint8_t maint_rel = buf.get8(6); + uint32_t revision = buf.get32(7); + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"MajorRel\":%d,\"MinorRel\":%d" + ",\"MaintRel\":%d,\"Revision\":%d}}"), + ZIGBEE_STATUS_CC_VERSION, major_rel, minor_rel, + maint_rel, revision); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); + + if ((0x02 == major_rel) && (0x06 == minor_rel)) { + return 0; + } else { + return ZIGBEE_LABEL_UNSUPPORTED_VERSION; + } +} + +bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match) { + if ( (pgm_read_byte(&match[0]) == buf.get8(0)) && + (pgm_read_byte(&match[1]) == buf.get8(1)) ) { + return true; + } else { + return false; + } +} + +int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) { + + uint8_t duration = buf.get8(2); + uint8_t status_code; + const char* message; + + if (0xFF == duration) { + status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_XX; + message = PSTR("Enable Pairing mode until next boot"); + } else if (duration > 0) { + status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_60; + message = PSTR("Enable Pairing mode for %d seconds"); + } else { + status_code = ZIGBEE_STATUS_PERMITJOIN_CLOSE; + message = PSTR("Disable Pairing mode"); + } + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Message\":\""), + status_code); + ResponseAppend_P(message, duration); + ResponseAppend_P(PSTR("\"}}")); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); + XdrvRulesProcess(); + return -1; +} + + +void Z_SendActiveEpReq(uint16_t shortaddr) { + uint8_t ActiveEpReq[] = { Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, + Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; + + uint8_t NodeDescReq[] = { Z_SREQ | Z_ZDO, ZDO_NODE_DESC_REQ, + Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; + + ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq)); + +} + + +void Z_SendSimpleDescReq(uint16_t shortaddr, uint8_t endpoint) { + uint8_t SimpleDescReq[] = { Z_SREQ | Z_ZDO, ZDO_SIMPLE_DESC_REQ, + Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr), + endpoint }; + + ZigbeeZNPSend(SimpleDescReq, sizeof(SimpleDescReq)); +} + +const char* Z_DeviceType[] = { "Coordinator", "Router", "End Device", "Unknown" }; +int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) { + + Z_ShortAddress srcAddr = buf.get16(2); + uint8_t status = buf.get8(4); + Z_ShortAddress nwkAddr = buf.get16(5); + uint8_t logicalType = buf.get8(7); + uint8_t apsFlags = buf.get8(8); + uint8_t MACCapabilityFlags = buf.get8(9); + uint16_t manufacturerCapabilities = buf.get16(10); + uint8_t maxBufferSize = buf.get8(12); + uint16_t maxInTransferSize = buf.get16(13); + uint16_t serverMask = buf.get16(15); + uint16_t maxOutTransferSize = buf.get16(17); + uint8_t descriptorCapabilities = buf.get8(19); + + if (0 == status) { + zigbee_devices.updateLastSeen(nwkAddr); + + uint8_t deviceType = logicalType & 0x7; + if (deviceType > 3) { deviceType = 3; } + bool complexDescriptorAvailable = (logicalType & 0x08) ? 1 : 0; + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"NodeType\":\"%s\",\"ComplexDesc\":%s}}"), + ZIGBEE_STATUS_NODE_DESC, Z_DeviceType[deviceType], + complexDescriptorAvailable ? "true" : "false" + ); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + } + + return -1; +} + +int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) { + + Z_ShortAddress srcAddr = buf.get16(2); + uint8_t status = buf.get8(4); + Z_ShortAddress nwkAddr = buf.get16(5); + uint8_t activeEpCount = buf.get8(7); + uint8_t* activeEpList = (uint8_t*) buf.charptr(8); + + + for (uint32_t i = 0; i < activeEpCount; i++) { + zigbee_devices.addEndoint(nwkAddr, activeEpList[i]); + } + + for (uint32_t i = 0; i < activeEpCount; i++) { + Z_SendSimpleDescReq(nwkAddr, activeEpList[i]); + } + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"ActiveEndpoints\":["), + ZIGBEE_STATUS_ACTIVE_EP); + for (uint32_t i = 0; i < activeEpCount; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%02X\""), activeEpList[i]); + } + ResponseAppend_P(PSTR("]}}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + return -1; +} + +void Z_SendAFInfoRequest(uint16_t shortaddr, uint8_t endpoint, uint16_t clusterid, uint8_t transacid) { + SBuffer buf(100); + buf.add8(Z_SREQ | Z_AF); + buf.add8(AF_DATA_REQUEST); + buf.add16(shortaddr); + buf.add8(endpoint); + buf.add8(0x01); + buf.add16(clusterid); + buf.add8(transacid); + buf.add8(0x30); + buf.add8(0x1E); + + buf.add8(3 + 2*sizeof(uint16_t)); + buf.add8(0x00); + buf.add8(transacid); + buf.add8(ZCL_READ_ATTRIBUTES); + buf.add16(0x0004); + buf.add16(0x0005); + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); +} + + +int32_t Z_ReceiveSimpleDesc(int32_t res, const class SBuffer &buf) { + + Z_ShortAddress srcAddr = buf.get16(2); + uint8_t status = buf.get8(4); + Z_ShortAddress nwkAddr = buf.get16(5); + uint8_t lenDescriptor = buf.get8(7); + uint8_t endpoint = buf.get8(8); + uint16_t profileId = buf.get16(9); + uint16_t deviceId = buf.get16(11); + uint8_t deviceVersion = buf.get8(13); + uint8_t numInCluster = buf.get8(14); + uint8_t numOutCluster = buf.get8(15 + numInCluster*2); + + if (0 == status) { + zigbee_devices.addEndointProfile(nwkAddr, endpoint, profileId); + for (uint32_t i = 0; i < numInCluster; i++) { + zigbee_devices.addCluster(nwkAddr, endpoint, buf.get16(15 + i*2), false); + } + for (uint32_t i = 0; i < numOutCluster; i++) { + zigbee_devices.addCluster(nwkAddr, endpoint, buf.get16(16 + numInCluster*2 + i*2), true); + } + + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"Endpoint\":\"0x%02X\"" + ",\"ProfileId\":\"0x%04X\",\"DeviceId\":\"0x%04X\",\"DeviceVersion\":%d" + "\"InClusters\":["), + ZIGBEE_STATUS_SIMPLE_DESC, endpoint, + profileId, deviceId, deviceVersion); + for (uint32_t i = 0; i < numInCluster; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(15 + i*2)); + } + ResponseAppend_P(PSTR("],\"OutClusters\":[")); + for (uint32_t i = 0; i < numOutCluster; i++) { + if (i > 0) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(16 + numInCluster*2 + i*2)); + } + ResponseAppend_P(PSTR("]}}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + + uint8_t cluster = zigbee_devices.findClusterEndpointIn(nwkAddr, 0x0000); + if (cluster) { + Z_SendAFInfoRequest(nwkAddr, cluster, 0x0000, 0x01); + } + } + return -1; +} + +int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { + Z_ShortAddress srcAddr = buf.get16(2); + Z_ShortAddress nwkAddr = buf.get16(4); + Z_IEEEAddress ieeeAddr = buf.get64(6); + uint8_t capabilities = buf.get8(14); + + zigbee_devices.updateDevice(nwkAddr, ieeeAddr); + + char hex[20]; + Uint64toHex(ieeeAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" + ",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"), + ZIGBEE_STATUS_DEVICE_ANNOUNCE, hex, nwkAddr, + (capabilities & 0x04) ? "true" : "false", + (capabilities & 0x08) ? "true" : "false", + (capabilities & 0x40) ? "true" : "false" + ); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + Z_SendActiveEpReq(nwkAddr); + return -1; +} + + +int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) { + Z_ShortAddress srcAddr = buf.get16(2); + Z_IEEEAddress ieeeAddr = buf.get64(4); + Z_ShortAddress parentNw = buf.get16(12); + + zigbee_devices.updateDevice(srcAddr, ieeeAddr); + + char hex[20]; + Uint64toHex(ieeeAddr, hex, 64); + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" + "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" + ",\"ParentNetwork\":\"0x%04X\"}}"), + ZIGBEE_STATUS_DEVICE_INDICATION, hex, srcAddr, parentNw + ); + + MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); + XdrvRulesProcess(); + + return -1; +} + + + +const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; + +void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, const JsonObject *json) { + + const JsonVariant &val_endpoint = getCaseInsensitive(*json, PSTR(OCCUPANCY)); + if (nullptr != &val_endpoint) { + uint32_t occupancy = strToUInt(val_endpoint); + + if (occupancy) { + zigbee_devices.setTimer(shortaddr, OCCUPANCY_TIMEOUT, cluster, endpoint, 0, &Z_OccupancyCallback); + } + } +} + + + +int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) { + const JsonObject *json = zigbee_devices.jsonGet(shortaddr); + if (json == nullptr) { return 0; } + + Z_AqaraOccupancy(shortaddr, cluster, endpoint, json); + + zigbee_devices.jsonPublishFlush(shortaddr); + return 1; +} + +int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { + uint16_t groupid = buf.get16(2); + uint16_t clusterid = buf.get16(4); + Z_ShortAddress srcaddr = buf.get16(6); + uint8_t srcendpoint = buf.get8(8); + uint8_t dstendpoint = buf.get8(9); + uint8_t wasbroadcast = buf.get8(10); + uint8_t linkquality = buf.get8(11); + uint8_t securityuse = buf.get8(12); + uint32_t timestamp = buf.get32(13); + uint8_t seqnumber = buf.get8(17); + + bool defer_attributes = false; + + zigbee_devices.updateLastSeen(srcaddr); + ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid, + srcaddr, + srcendpoint, dstendpoint, wasbroadcast, + linkquality, securityuse, seqnumber, + timestamp); + zcl_received.log(); + char shortaddr[8]; + snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); + + DynamicJsonBuffer jsonBuffer; + JsonObject& json = jsonBuffer.createObject(); + + if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) { + zcl_received.parseRawAttributes(json); + if (clusterid) { defer_attributes = true; } + } else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) { + zcl_received.parseReadAttributes(json); + } else if (zcl_received.isClusterSpecificCommand()) { + zcl_received.parseClusterSpecificCommand(json); + } + String msg(""); + msg.reserve(100); + json.printTo(msg); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str()); + + zcl_received.postProcessAttributes(srcaddr, json); + + json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality; + + if (defer_attributes) { + + if (zigbee_devices.jsonIsConflict(srcaddr, json)) { + + zigbee_devices.jsonPublishFlush(srcaddr); + } else { + zigbee_devices.jsonAppend(srcaddr, json); + zigbee_devices.setTimer(srcaddr, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, 0, &Z_PublishAttributes); + } + } else { + + zigbee_devices.jsonPublishNow(srcaddr, json); + } + return -1; +} + +typedef struct Z_Dispatcher { + const uint8_t* match; + ZB_RecvMsgFunc func; +} Z_Dispatcher; + + +ZBM(AREQ_AF_INCOMING_MESSAGE, Z_AREQ | Z_AF, AF_INCOMING_MSG) +ZBM(AREQ_END_DEVICE_ANNCE_IND, Z_AREQ | Z_ZDO, ZDO_END_DEVICE_ANNCE_IND) +ZBM(AREQ_END_DEVICE_TC_DEV_IND, Z_AREQ | Z_ZDO, ZDO_TC_DEV_IND) +ZBM(AREQ_PERMITJOIN_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND ) +ZBM(AREQ_ZDO_ACTIVEEPRSP, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP) +ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) + +const Z_Dispatcher Z_DispatchTable[] PROGMEM = { + { AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage }, + { AREQ_END_DEVICE_ANNCE_IND, &Z_ReceiveEndDeviceAnnonce }, + { AREQ_END_DEVICE_TC_DEV_IND, &Z_ReceiveTCDevInd }, + { AREQ_PERMITJOIN_OPEN_XX, &Z_ReceivePermitJoinStatus }, + { AREQ_ZDO_NODEDESCRSP, &Z_ReceiveNodeDesc }, + { AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp }, + { AREQ_ZDO_SIMPLEDESCRSP, &Z_ReceiveSimpleDesc }, +}; + +int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { + + if (zigbee.init_phase) { + + return -1; + } else { + for (uint32_t i = 0; i < sizeof(Z_DispatchTable)/sizeof(Z_Dispatcher); i++) { + if (Z_ReceiveMatchPrefix(buf, Z_DispatchTable[i].match)) { + (*Z_DispatchTable[i].func)(res, buf); + } + } + return -1; + } +} + +int32_t Z_Load_Devices(uint8_t value) { + + loadZigbeeDevices(); + return 0; +} + +int32_t Z_State_Ready(uint8_t value) { + zigbee.init_phase = false; + return 0; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" +#ifdef USE_ZIGBEE + +#define XDRV_23 23 + +const uint32_t ZIGBEE_BUFFER_SIZE = 256; +const uint8_t ZIGBEE_SOF = 0xFE; +const uint8_t ZIGBEE_SOF_ALT = 0xFF; + +#include +TasmotaSerial *ZigbeeSerial = nullptr; + + +const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" + D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|" + D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" + D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|" + D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND ; + +const char kZigbeeCommands[] PROGMEM = D_PRFX_ZIGBEE "|" + D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|" + D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" + D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|" + D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND ; + +void (* const ZigbeeCommand[])(void) PROGMEM = { + &CmndZbZNPSend, &CmndZbPermitJoin, + &CmndZbStatus, &CmndZbReset, &CmndZbSend, + &CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive, + &CmndZbForget, &CmndZbSave, &CmndZbName, &CmndZbBind + }; + +int32_t ZigbeeProcessInput(class SBuffer &buf) { + if (!zigbee.state_machine) { return -1; } + + + bool recv_filter_match = true; + bool recv_prefix_match = false; + if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { + if (zigbee.recv_filter_len >= 2) { + recv_prefix_match = false; + if ( (pgm_read_byte(&zigbee.recv_filter[0]) == buf.get8(0)) && + (pgm_read_byte(&zigbee.recv_filter[1]) == buf.get8(1)) ) { + recv_prefix_match = true; + } + } + + for (uint32_t i = 0; i < zigbee.recv_filter_len; i++) { + if (pgm_read_byte(&zigbee.recv_filter[i]) != buf.get8(i)) { + recv_filter_match = false; + break; + } + } + + + } + + + int32_t res = -1; + + + + + + if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { + if (!recv_prefix_match) { + res = -1; + } else { + if (recv_filter_match) { + res = 0; + } else { + if (zigbee.recv_until) { + res = -1; + } else { + res = -2; + } + } + } + } else { + res = -1; + } + + if (recv_prefix_match) { + if (zigbee.recv_func) { + res = (*zigbee.recv_func)(res, buf); + } + } + if (-1 == res) { + + if (zigbee.recv_unexpected) { + res = (*zigbee.recv_unexpected)(res, buf); + } + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZbProcessInput: res = %d"), res); + + + if (0 == res) { + + zigbee.state_waiting = false; + } else if (res > 0) { + ZigbeeGotoLabel(res); + } else if (-1 == res) { + + + } else { + + ZigbeeGotoLabel(zigbee.on_error_goto); + } +} + +void ZigbeeInput(void) +{ + static uint32_t zigbee_polling_window = 0; + static uint8_t fcs = ZIGBEE_SOF; + static uint32_t zigbee_frame_len = 5; +# 142 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" + while (ZigbeeSerial->available()) { + yield(); + uint8_t zigbee_in_byte = ZigbeeSerial->read(); + + + if (0 == zigbee_buffer->len()) { + zigbee_frame_len = 5; + fcs = ZIGBEE_SOF; + + + + if (ZIGBEE_SOF_ALT == zigbee_in_byte) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte); + zigbee_in_byte = ZIGBEE_SOF; + } + } + + if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput discarding byte %02X"), zigbee_in_byte); + continue; + } + + if (zigbee_buffer->len() < zigbee_frame_len) { + zigbee_buffer->add8(zigbee_in_byte); + zigbee_polling_window = millis(); + fcs ^= zigbee_in_byte; + } + + if (zigbee_buffer->len() >= zigbee_frame_len) { + zigbee_polling_window = 0; + break; + } + + + if (02 == zigbee_buffer->len()) { + + uint8_t len_byte = zigbee_buffer->get8(1); + if (len_byte > 250) len_byte = 250; + + zigbee_frame_len = len_byte + 5; + } + } + + if (zigbee_buffer->len() && (millis() > (zigbee_polling_window + ZIGBEE_POLLING))) { + char hex_char[(zigbee_buffer->len() * 2) + 2]; + ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char)); + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric()); + + if (zigbee_buffer->len() != zigbee_frame_len) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received frame of wrong size %s, len %d, expected %d"), hex_char, zigbee_buffer->len(), zigbee_frame_len); + } else if (0x00 != fcs) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received bad FCS frame %s, %d"), hex_char, fcs); + } else { + + + + SBuffer znp_buffer = zigbee_buffer->subBuffer(2, zigbee_frame_len - 3); + + ToHex_P((unsigned char*)znp_buffer.getBuffer(), znp_buffer.len(), hex_char, sizeof(hex_char)); + Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char); + if (Settings.flag3.tuya_serial_mqtt_publish) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); + XdrvRulesProcess(); + } else { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); + } + + ZigbeeProcessInput(znp_buffer); + } + zigbee_buffer->setLen(0); + } +} + + + +void ZigbeeInit(void) +{ + zigbee.active = false; + if ((pin[GPIO_ZIGBEE_RX] < 99) && (pin[GPIO_ZIGBEE_TX] < 99)) { + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]); + + ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], seriallog_level ? 1 : 2, 0, 256); + ZigbeeSerial->begin(115200); + if (ZigbeeSerial->hardwareSerial()) { + ClaimSerial(); + uint32_t aligned_buffer = ((uint32_t)serial_in_buffer + 3) & ~3; + zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer) - 3, (char*) aligned_buffer); + } else { + zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); + } + zigbee.active = true; + zigbee.init_phase = true; + zigbee.state_machine = true; + ZigbeeSerial->flush(); + } +} + + + + + +uint32_t strToUInt(const JsonVariant &val) { + + if (val.is()) { + return val.as(); + } else { + if (val.is()) { + String sval = val.as(); + return strtoull(sval.c_str(), nullptr, 0); + } + } + return 0; +} + +const unsigned char ZIGBEE_FACTORY_RESET[] PROGMEM = + { Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 , 0x01 }; + + +void CmndZbReset(void) { + if (ZigbeeSerial) { + switch (XdrvMailbox.payload) { + case 1: + ZigbeeZNPSend(ZIGBEE_FACTORY_RESET, sizeof(ZIGBEE_FACTORY_RESET)); + eraseZigbeeDevices(); + restart_flag = 2; + ResponseCmndChar(D_JSON_ZIGBEE_CC2530 " " D_JSON_RESET_AND_RESTARTING); + break; + default: + ResponseCmndChar(D_JSON_ONE_TO_RESET); + } + } +} + +void CmndZbZNPSendOrReceive(bool send) +{ + if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) { + uint8_t code; + + char *codes = RemoveSpace(XdrvMailbox.data); + int32_t size = strlen(XdrvMailbox.data); + + SBuffer buf((size+1)/2); + + while (size > 1) { + char stemp[3]; + strlcpy(stemp, codes, sizeof(stemp)); + code = strtol(stemp, nullptr, 16); + buf.add8(code); + size -= 2; + codes += 2; + } + if (send) { + ZigbeeZNPSend(buf.getBuffer(), buf.len()); + } else { + ZigbeeProcessInput(buf); + } + } + ResponseCmndDone(); +} + + +void CmndZbZNPReceive(void) +{ + CmndZbZNPSendOrReceive(false); +} + +void CmndZbZNPSend(void) +{ + CmndZbZNPSendOrReceive(true); +} + +void ZigbeeZNPSend(const uint8_t *msg, size_t len) { + if ((len < 2) || (len > 252)) { + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEEZNPSENT ": bad message len %d"), len); + return; + } + uint8_t data_len = len - 2; + + if (ZigbeeSerial) { + uint8_t fcs = data_len; + + ZigbeeSerial->write(ZIGBEE_SOF); + + ZigbeeSerial->write(data_len); + + for (uint32_t i = 0; i < len; i++) { + uint8_t b = pgm_read_byte(msg + i); + ZigbeeSerial->write(b); + fcs ^= b; + + } + ZigbeeSerial->write(fcs); + + } + + char hex_char[(len * 2) + 2]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %s"), + ToHex_P(msg, len, hex_char, sizeof(hex_char))); +} + +void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp, uint8_t transacId) { + SBuffer buf(25+len); + buf.add8(Z_SREQ | Z_AF); + buf.add8(AF_DATA_REQUEST); + buf.add16(dtsAddr); + buf.add8(endpoint); + buf.add8(0x01); + buf.add16(clusterId); + buf.add8(transacId); + buf.add8(0x30); + buf.add8(0x1E); + + buf.add8(3 + len); + buf.add8((disableDefResp ? 0x10 : 0x00) | (clusterSpecific ? 0x01 : 0x00)); + buf.add8(transacId); + buf.add8(cmdId); + if (len > 0) { + buf.addBuffer(msg, len); + } + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); +} + +inline int8_t hexValue(char c) { + if ((c >= '0') && (c <= '9')) { + return c - '0'; + } + if ((c >= 'A') && (c <= 'F')) { + return 10 + c - 'A'; + } + if ((c >= 'a') && (c <= 'f')) { + return 10 + c - 'a'; + } + return -1; +} + +uint32_t parseHex(const char **data, size_t max_len = 8) { + uint32_t ret = 0; + for (uint32_t i = 0; i < max_len; i++) { + int8_t v = hexValue(**data); + if (v < 0) { break; } + ret = (ret << 4) | v; + *data += 1; + } + return ret; +} + +void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data) { + + uint16_t cluster = 0x0000; + uint8_t cmd = ZCL_READ_ATTRIBUTES; + bool clusterSpecific = false; + + + + cluster = parseHex(&data, 4); + + + if (('_' == *data) || ('!' == *data)) { + if ('!' == *data) { clusterSpecific = true; } + data++; + } else { + ResponseCmndChar("Wrong delimiter for payload"); + return; + } + + cmd = parseHex(&data, 2); + + + + if ('/' == *data) { data++; } + + size_t size = strlen(data); + SBuffer buf((size+2)/2); + + while (*data) { + uint8_t code = parseHex(&data, 2); + buf.add8(code); + } + + if (0 == endpoint) { + + endpoint = zigbee_devices.findClusterEndpointIn(dstAddr, cluster); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: dstAddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"), + dstAddr, cluster, endpoint, cmd, data); + + if (0 == endpoint) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint")); + return; + } + + + ZigbeeZCLSend(dstAddr, cluster, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len()); + + if (clusterSpecific) { + zigbeeSetCommandTimer(dstAddr, cluster, endpoint); + } + ResponseCmndDone(); +} + +void CmndZbSend(void) { +# 461 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" + if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } + DynamicJsonBuffer jsonBuf; + JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data); + if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; } + + + static char delim[] = ", "; + uint16_t device = 0xFFFF; + uint8_t endpoint = 0x00; + String cmd_str = ""; + + const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); + if (nullptr != &val_device) { + device = zigbee_devices.parseDeviceParam(val_device.as()); + if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; } + } + if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; } + + const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); + if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } + const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send")); + if (nullptr != &val_cmd) { + + + + if (val_cmd.is()) { + + JsonObject &cmd_obj = val_cmd.as(); + int32_t cmd_size = cmd_obj.size(); + if (cmd_size > 1) { + Response_P(PSTR("Only 1 command allowed (%d)"), cmd_size); + return; + } else if (1 == cmd_size) { + + JsonObject::iterator it = cmd_obj.begin(); + String key = it->key; + JsonVariant& value = it->value; + uint32_t x = 0, y = 0, z = 0; + + const __FlashStringHelper* tasmota_cmd = zigbeeFindCommand(key.c_str()); + if (tasmota_cmd) { + cmd_str = tasmota_cmd; + } else { + Response_P(PSTR("Unrecognized zigbee command: %s"), key.c_str()); + return; + } + + + if (value.is()) { + x = value.as() ? 1 : 0; + } else if (value.is()) { + x = value.as(); + } else { + + const char *s_const = value.as(); + if (s_const != nullptr) { + char s[strlen(s_const)+1]; + strcpy(s, s_const); + if ((nullptr != s) && (0x00 != *s)) { + char *sval = strtok(s, delim); + if (sval) { + x = ZigbeeAliasOrNumber(sval); + sval = strtok(nullptr, delim); + if (sval) { + y = ZigbeeAliasOrNumber(sval); + sval = strtok(nullptr, delim); + if (sval) { + z = ZigbeeAliasOrNumber(sval); + } + } + } + } + } + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_template = %s"), cmd_str.c_str()); + cmd_str = zigbeeCmdAddParams(cmd_str.c_str(), x, y, z); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_final = %s"), cmd_str.c_str()); + } else { + + } + } else if (val_cmd.is()) { + + cmd_str = val_cmd.as(); + } else { + + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%s\"}"), + device, endpoint, cmd_str.c_str()); + zigbeeZCLSendStr(device, endpoint, cmd_str.c_str()); + } else { + Response_P(PSTR("Missing zigbee 'Send'")); + return; + } + +} + +ZBM(ZBS_BIND_REQ, Z_SREQ | Z_ZDO, ZDO_BIND_REQ, + 0,0, + 0,0,0,0,0,0,0,0, + 0x00, + 0x00, 0x00, + 0x03, + 0,0,0,0,0,0,0,0, + 0x01 +) + +void CmndZbBind(void) { + + + + if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } + DynamicJsonBuffer jsonBuf; + JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data); + if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; } + + + + uint16_t device = 0xFFFF; + uint8_t endpoint = 0x00; + uint16_t cluster = 0; + uint32_t group = 0xFFFFFFFF; + + const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); + if (nullptr != &val_device) { + device = zigbee_devices.parseDeviceParam(val_device.as()); + if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; } + } + if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; } + + const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); + if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } + const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster")); + if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); } + + + + SBuffer buf(sizeof(ZBS_BIND_REQ)); + buf.add8(Z_SREQ | Z_ZDO); + buf.add8(ZDO_BIND_REQ); + buf.add16(device); + buf.add64(zigbee_devices.getDeviceLongAddr(device)); + buf.add8(endpoint); + buf.add16(cluster); + buf.add8(0x03); + buf.add64(localIEEEAddr); + buf.add8(0x01); + + ZigbeeZNPSend(buf.getBuffer(), buf.len()); + + ResponseCmndDone(); +} + + +void CmndZbProbe(void) { + if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; } + if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; } + + + Z_SendActiveEpReq(shortaddr); + ResponseCmndDone(); +} + + +void CmndZbName(void) { + + + + + + + + if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } + + + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + + + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); + if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; } + if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; } + + if (p == nullptr) { + const String * friendlyName = zigbee_devices.getFriendlyName(shortaddr); + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, friendlyName ? friendlyName->c_str() : ""); + } else { + zigbee_devices.setFriendlyName(shortaddr, p); + Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, p); + } +} + + +void CmndZbForget(void) { + if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; } + if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; } + + + if (zigbee_devices.removeDevice(shortaddr)) { + ResponseCmndDone(); + } else { + ResponseCmndChar("Unknown device"); + } +} + + +void CmndZbSave(void) { + if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } + + saveZigbeeDevices(); + + ResponseCmndDone(); +} + + +void CmndZbRead(void) { + + + + if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } + DynamicJsonBuffer jsonBuf; + JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data); + if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; } + + + uint16_t device = 0xFFFF; + uint16_t cluster = 0x0000; + uint8_t endpoint = 0x00; + size_t attrs_len = 0; + uint8_t* attrs = nullptr; + + const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); + if (nullptr != &val_device) { + device = zigbee_devices.parseDeviceParam(val_device.as()); + if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; } + } + if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; } + + const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster")); + if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); } + const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); + if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } + + const JsonVariant &val_attr = getCaseInsensitive(json, PSTR("Read")); + if (nullptr != &val_attr) { + uint16_t val = strToUInt(val_attr); + if (val_attr.is()) { + JsonArray& attr_arr = val_attr; + attrs_len = attr_arr.size() * 2; + attrs = new uint8_t[attrs_len]; + + uint32_t i = 0; + for (auto value : attr_arr) { + uint16_t val = strToUInt(value); + attrs[i++] = val & 0xFF; + attrs[i++] = val >> 8; + } + } else { + attrs_len = 2; + attrs = new uint8_t[attrs_len]; + attrs[0] = val & 0xFF; + attrs[1] = val >> 8; + } + } + + if ((0 != endpoint) && (attrs_len > 0)) { + ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false ); + ResponseCmndDone(); + } else { + ResponseCmndChar("Missing parameters"); + } + + if (attrs) { delete[] attrs; } +} + + +void CmndZbPermitJoin(void) +{ + if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } + uint32_t payload = XdrvMailbox.payload; + if (payload < 0) { payload = 0; } + if ((99 != payload) && (payload > 1)) { payload = 1; } + + if (1 == payload) { + ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60); + } else if (99 == payload){ + ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX); + } else { + ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_CLOSE); + } + ResponseCmndDone(); +} + +void CmndZbStatus(void) { + if (ZigbeeSerial) { + if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } + uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); + if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; } + if (XdrvMailbox.payload > 0) { + if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; } + } + + String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr); + Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str()); + } +} + + + + + +bool Xdrv23(uint8_t function) +{ + bool result = false; + + if (zigbee.active) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (!zigbee.init_phase) { + zigbee_devices.runTimer(); + } + break; + case FUNC_LOOP: + if (ZigbeeSerial) { ZigbeeInput(); } + if (zigbee.state_machine) { + + ZigbeeStateMachine_Run(); + } + break; + case FUNC_PRE_INIT: + ZigbeeInit(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kZbCommands, ZigbeeCommand); + result = result || DecodeCommand(kZigbeeCommands, ZigbeeCommand); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_24_buzzer.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_24_buzzer.ino" +#ifdef USE_BUZZER + + + + +#define XDRV_24 24 + +struct BUZZER { + uint32_t tune = 0; + uint32_t tune_reload = 0; + bool active = true; + bool enable = false; + uint8_t inverted = 0; + uint8_t count = 0; + uint8_t mode = 0; + uint8_t set[2]; + uint8_t duration; + uint8_t state = 0; +} Buzzer; + + + +void BuzzerOff(void) +{ + DigitalWrite(GPIO_BUZZER, Buzzer.inverted); +} + + +void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32_t mode) +{ + Buzzer.set[0] = off; + Buzzer.set[1] = on; + Buzzer.duration = 1; + Buzzer.tune_reload = 0; + Buzzer.mode = mode; + + if (tune) { + uint32_t tune1 = tune; + uint32_t tune2 = tune; + for (uint32_t i = 0; i < 32; i++) { + if (!(tune2 & 0x80000000)) { + tune2 <<= 1; + } else { + Buzzer.tune_reload <<= 1; + Buzzer.tune_reload |= tune1 & 1; + tune1 >>= 1; + } + } + Buzzer.tune = Buzzer.tune_reload; + } + Buzzer.count = count * 2; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BUZ: %d(%d),%d,%d,0x%08X(0x%08X)"), count, Buzzer.count, on, off, tune, Buzzer.tune); + + Buzzer.enable = (Buzzer.count > 0); + if (!Buzzer.enable) { + BuzzerOff(); + } +} + +void BuzzerSetStateToLed(uint32_t state) +{ + if (Buzzer.enable && (2 == Buzzer.mode)) { + Buzzer.state = (state != 0); + DigitalWrite(GPIO_BUZZER, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); + } +} + +void BuzzerBeep(uint32_t count) +{ + BuzzerBeep(count, 1, 1, 0, 0); +} + +void BuzzerEnabledBeep(uint32_t count, uint32_t duration) +{ + if (Settings.flag3.buzzer_enable) { + BuzzerBeep(count, duration, 1, 0, 0); + } +} + + + +bool BuzzerPinState(void) +{ + if (XdrvMailbox.index == GPIO_BUZZER_INV) { + Buzzer.inverted = 1; + XdrvMailbox.index -= (GPIO_BUZZER_INV - GPIO_BUZZER); + return true; + } + return false; +} + +void BuzzerInit(void) +{ + if (pin[GPIO_BUZZER] < 99) { + pinMode(pin[GPIO_BUZZER], OUTPUT); + BuzzerOff(); + } else { + Buzzer.active = false; + } +} + +void BuzzerEvery100mSec(void) +{ + if (Buzzer.enable && (Buzzer.mode != 2)) { + if (Buzzer.count) { + if (Buzzer.duration) { + Buzzer.duration--; + if (!Buzzer.duration) { + if (Buzzer.tune) { + Buzzer.state = Buzzer.tune & 1; + Buzzer.tune >>= 1; + } else { + Buzzer.tune = Buzzer.tune_reload; + Buzzer.count -= (Buzzer.tune_reload) ? 2 : 1; + Buzzer.state = Buzzer.count & 1; + if (Buzzer.mode) { + Buzzer.count |= 2; + } + } + Buzzer.duration = Buzzer.set[Buzzer.state]; + } + } + DigitalWrite(GPIO_BUZZER, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); + } else { + Buzzer.enable = false; + } + } +} + + + + + +const char kBuzzerCommands[] PROGMEM = "|" + "Buzzer" ; + +void (* const BuzzerCommand[])(void) PROGMEM = { + &CmndBuzzer }; + +void CmndBuzzer(void) +{ +# 174 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_24_buzzer.ino" + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.payload != 0) { + uint32_t parm[4] = { 0 }; + uint32_t mode = 0; + ParseParameters(4, parm); + if (XdrvMailbox.payload <= 0) { + parm[0] = 1; + mode = -XdrvMailbox.payload; + } + for (uint32_t i = 1; i < 3; i++) { + if (parm[i] < 1) { parm[i] = 1; } + } + BuzzerBeep(parm[0], parm[1], parm[2], parm[3], mode); + } else { + BuzzerBeep(0); + } + } else { + BuzzerBeep(1); + } + ResponseCmndDone(); +} + + + + + +bool Xdrv24(uint8_t function) +{ + bool result = false; + + if (Buzzer.active) { + switch (function) { + case FUNC_EVERY_100_MSECOND: + BuzzerEvery100mSec(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kBuzzerCommands, BuzzerCommand); + break; + case FUNC_PRE_INIT: + BuzzerInit(); + break; + case FUNC_PIN_STATE: + result = BuzzerPinState(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_25_A4988_Stepper.ino" +# 21 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_25_A4988_Stepper.ino" +#ifdef USE_A4988_STEPPER + + + + +#define XDRV_25 25 + +#include + +short A4988_dir_pin = pin[GPIO_MAX]; +short A4988_stp_pin = pin[GPIO_MAX]; +short A4988_ms1_pin = pin[GPIO_MAX]; +short A4988_ms2_pin = pin[GPIO_MAX]; +short A4988_ms3_pin = pin[GPIO_MAX]; +short A4988_ena_pin = pin[GPIO_MAX]; +int A4988_spr = 0; +float A4988_rpm = 0; +short A4988_mis = 0; + +A4988_Stepper* myA4988 = nullptr; + +void A4988Init(void) +{ + A4988_dir_pin = pin[GPIO_A4988_DIR]; + A4988_stp_pin = pin[GPIO_A4988_STP]; + A4988_ena_pin = pin[GPIO_A4988_ENA]; + A4988_ms1_pin = pin[GPIO_A4988_MS1]; + A4988_ms2_pin = pin[GPIO_A4988_MS2]; + A4988_ms3_pin = pin[GPIO_A4988_MS3]; + A4988_spr = 200; + A4988_rpm = 30; + A4988_mis = 1; + + myA4988 = new A4988_Stepper( A4988_spr + , A4988_rpm + , A4988_mis + , A4988_dir_pin + , A4988_stp_pin + , A4988_ena_pin + , A4988_ms1_pin + , A4988_ms2_pin + , A4988_ms3_pin ); +} + +const char kA4988Commands[] PROGMEM = "Motor|" + "Move|Rotate|Turn|MIS|SPR|RPM"; + +void (* const A4988Command[])(void) PROGMEM = { + &CmndDoMove,&CmndDoRotate,&CmndDoTurn,&CmndSetMIS,&CmndSetSPR,&CmndSetRPM}; + +void CmndDoMove(void) { + if (XdrvMailbox.data_len > 0) { + long stepsPlease = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->doMove(stepsPlease); + ResponseCmndDone(); + } +} + +void CmndDoRotate(void) { + if (XdrvMailbox.data_len > 0) { + long degrsPlease = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->doRotate(degrsPlease); + ResponseCmndDone(); + } +} + +void CmndDoTurn(void) { + if (XdrvMailbox.data_len > 0) { + float turnsPlease = strtod(XdrvMailbox.data,nullptr); + myA4988->doTurn(turnsPlease); + ResponseCmndDone(); + } +} + +void CmndSetMIS(void) { + if ((pin[GPIO_A4988_MS1] < 99) && (pin[GPIO_A4988_MS2] < 99) && (pin[GPIO_A4988_MS3] < 99) && (XdrvMailbox.data_len > 0)) { + short newMIS = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setMIS(newMIS); + ResponseCmndDone(); + } +} + +void CmndSetSPR(void) { + if (XdrvMailbox.data_len > 0) { + int newSPR = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setSPR(newSPR); + ResponseCmndDone(); + } +} + +void CmndSetRPM(void) { + if (XdrvMailbox.data_len > 0) { + short newRPM = strtoul(XdrvMailbox.data,nullptr,10); + myA4988->setRPM(newRPM); + ResponseCmndDone(); + } +} + + + + +bool Xdrv25(uint8_t function) +{ + bool result = false; + if ((pin[GPIO_A4988_DIR] < 99) && (pin[GPIO_A4988_STP] < 99)) { + switch (function) { + case FUNC_INIT: + A4988Init(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kA4988Commands, A4988Command); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_26_ariluxrf.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_26_ariluxrf.ino" +#ifdef USE_LIGHT +#ifdef USE_ARILUX_RF + + + + +#define XDRV_26 26 + +const uint32_t ARILUX_RF_TIME_AVOID_DUPLICATE = 1000; + +const uint8_t ARILUX_RF_MAX_CHANGES = 51; +const uint32_t ARILUX_RF_SEPARATION_LIMIT = 4300; +const uint32_t ARILUX_RF_RECEIVE_TOLERANCE = 60; + +struct ARILUX { + unsigned int rf_timings[ARILUX_RF_MAX_CHANGES]; + + unsigned long rf_received_value = 0; + unsigned long rf_last_received_value = 0; + unsigned long rf_last_time = 0; + unsigned long rf_lasttime = 0; + + unsigned int rf_change_count = 0; + unsigned int rf_repeat_count = 0; + + uint8_t rf_toggle = 0; +} Arilux; + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 +#ifndef USE_WS2812_DMA +void AriluxRfInterrupt(void) ICACHE_RAM_ATTR; +#endif +#endif + +void AriluxRfInterrupt(void) +{ + unsigned long time = micros(); + unsigned int duration = time - Arilux.rf_lasttime; + + if (duration > ARILUX_RF_SEPARATION_LIMIT) { + if (abs(duration - Arilux.rf_timings[0]) < 200) { + Arilux.rf_repeat_count++; + if (Arilux.rf_repeat_count == 2) { + unsigned long code = 0; + const unsigned int delay = Arilux.rf_timings[0] / 31; + const unsigned int delayTolerance = delay * ARILUX_RF_RECEIVE_TOLERANCE / 100; + for (unsigned int i = 1; i < Arilux.rf_change_count -1; i += 2) { + code <<= 1; + if (abs(Arilux.rf_timings[i] - (delay *3)) < delayTolerance && abs(Arilux.rf_timings[i +1] - delay) < delayTolerance) { + code |= 1; + } + } + if (Arilux.rf_change_count > 49) { + Arilux.rf_received_value = code; + } + Arilux.rf_repeat_count = 0; + } + } + Arilux.rf_change_count = 0; + } + if (Arilux.rf_change_count >= ARILUX_RF_MAX_CHANGES) { + Arilux.rf_change_count = 0; + Arilux.rf_repeat_count = 0; + } + Arilux.rf_timings[Arilux.rf_change_count++] = duration; + Arilux.rf_lasttime = time; +} + +void AriluxRfHandler(void) +{ + unsigned long now = millis(); + if (Arilux.rf_received_value && !((Arilux.rf_received_value == Arilux.rf_last_received_value) && (now - Arilux.rf_last_time < ARILUX_RF_TIME_AVOID_DUPLICATE))) { + Arilux.rf_last_received_value = Arilux.rf_received_value; + Arilux.rf_last_time = now; + + uint16_t hostcode = Arilux.rf_received_value >> 8 & 0xFFFF; + if (Settings.rf_code[1][6] == Settings.rf_code[1][7]) { + Settings.rf_code[1][6] = hostcode >> 8 & 0xFF; + Settings.rf_code[1][7] = hostcode & 0xFF; + } + uint16_t stored_hostcode = Settings.rf_code[1][6] << 8 | Settings.rf_code[1][7]; + + DEBUG_DRIVER_LOG(PSTR(D_LOG_RFR D_HOST D_CODE " 0x%04X, " D_RECEIVED " 0x%06X"), stored_hostcode, Arilux.rf_received_value); + + if (hostcode == stored_hostcode) { + char command[33]; + char value = '-'; + command[0] = '\0'; + uint8_t keycode = Arilux.rf_received_value & 0xFF; + switch (keycode) { + case 1: + case 3: + snprintf_P(command, sizeof(command), PSTR(D_CMND_POWER " %d"), (1 == keycode) ? 1 : 0); + break; + case 2: + Arilux.rf_toggle++; + Arilux.rf_toggle &= 0x3; + snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), 200 + Arilux.rf_toggle); + break; + case 4: + value = '+'; + case 7: + snprintf_P(command, sizeof(command), PSTR(D_CMND_SPEED " %c"), value); + break; + case 5: + value = '+'; + case 8: + snprintf_P(command, sizeof(command), PSTR(D_CMND_SCHEME " %c"), value); + break; + case 6: + value = '+'; + case 9: + snprintf_P(command, sizeof(command), PSTR(D_CMND_DIMMER " %c"), value); + break; + default: { + if ((keycode >= 10) && (keycode <= 21)) { + snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), keycode -9); + } + } + } + if (strlen(command)) { + ExecuteCommand(command, SRC_LIGHT); + } + } + } + Arilux.rf_received_value = 0; +} + +void AriluxRfInit(void) +{ + if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { + if (Settings.last_module != Settings.module) { + Settings.rf_code[1][6] = 0; + Settings.rf_code[1][7] = 0; + Settings.last_module = Settings.module; + } + Arilux.rf_received_value = 0; + + digitalWrite(pin[GPIO_ARIRFSEL], 0); + attachInterrupt(pin[GPIO_ARIRFRCV], AriluxRfInterrupt, CHANGE); + } +} + +void AriluxRfDisable(void) +{ + if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { + detachInterrupt(pin[GPIO_ARIRFRCV]); + digitalWrite(pin[GPIO_ARIRFSEL], 1); + } +} + + + + + +bool Xdrv26(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (pin[GPIO_ARIRFRCV] < 99) { AriluxRfHandler(); } + break; + case FUNC_EVERY_SECOND: + if (10 == uptime) { AriluxRfInit(); } + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_27_shutter.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_27_shutter.ino" +#ifdef USE_SHUTTER + + + + +#define XDRV_27 27 + +#define D_SHUTTER "SHUTTER" + +const uint16_t MOTOR_STOP_TIME = 500; +const uint8_t steps_per_second = 20; + +uint8_t calibrate_pos[6] = {0,30,50,70,90,100}; +uint16_t messwerte[5] = {30,50,70,90,100}; +uint16_t last_execute_step; + +enum ShutterModes { SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE, SHT_OFF_ON__OPEN_CLOSE_STEPPER,}; +enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_HOLD_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,}; + +const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" + D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|" + D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" + D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|" + D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME; + +void (* const ShutterCommand[])(void) PROGMEM = { + &CmndShutterOpen, &CmndShutterClose, &CmndShutterStop, &CmndShutterPosition, + &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, + &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, + &CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime}; + + const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d}"; + const char JSON_SHUTTER_BUTTON[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Button%d\":%d}"; + +#include + +Ticker TickerShutter; + +struct SHUTTER { + power_t mask = 0; + power_t old_power = 0; + power_t switched_relay = 0; + uint32_t time[MAX_SHUTTERS]; + int32_t open_max[MAX_SHUTTERS]; + int32_t target_position[MAX_SHUTTERS]; + int32_t start_position[MAX_SHUTTERS]; + int32_t real_position[MAX_SHUTTERS]; + uint16_t open_time[MAX_SHUTTERS]; + uint16_t close_time[MAX_SHUTTERS]; + uint16_t close_velocity[MAX_SHUTTERS]; + int8_t direction[MAX_SHUTTERS]; + uint8_t mode = 0; + int16_t motordelay[MAX_SHUTTERS]; + int16_t pwm_frequency; + uint16_t max_pwm_frequency = 1000; + uint16_t max_close_pwm_frequency[MAX_SHUTTERS]; + uint8_t skip_relay_change; + int32_t accelerator[MAX_SHUTTERS]; +} Shutter; + +void ShutterLogPos(uint32_t i) +{ + char stemp2[10]; + dtostrfd((float)Shutter.time[i] / steps_per_second, 2, stemp2); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d"), + i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_frequency); +} + +void ShutterRtc50mS(void) +{ + for (uint32_t i = 0; i < shutters_present; i++) { + Shutter.time[i]++; + if (Shutter.accelerator[i]) { + Shutter.pwm_frequency += Shutter.accelerator[i]; + Shutter.pwm_frequency = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i],Shutter.pwm_frequency)); + analogWriteFreq(Shutter.pwm_frequency); + analogWrite(pin[GPIO_PWM1+i], 50); + } + } +} + +#define SHT_DIV_ROUND(__A,__B) (((__A) + (__B)/2) / (__B)) + +int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index) +{ + if (0 == percent) return 0; + if (100 == percent) return Shutter.open_max[index]; + if (Settings.shutter_set50percent[index] != 50) { + return (percent <= 5) ? Settings.shuttercoeff[2][index] * percent : Settings.shuttercoeff[1][index] * percent + Settings.shuttercoeff[0][index]; + } else { + uint32_t realpos; + + for (uint32_t j = 0; j < 5; j++) { + if (0 == Settings.shuttercoeff[j][index]) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: RESET/INIT CALIBRATION MATRIX DIV 0")); + for (uint32_t k = 0; k < 5; k++) { + Settings.shuttercoeff[k][index] = SHT_DIV_ROUND(calibrate_pos[k+1] * 1000, calibrate_pos[5]); + } + } + } + for (uint32_t i = 0; i < 5; i++) { + if ((percent * 10) >= Settings.shuttercoeff[i][index]) { + realpos = SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i+1], 100); + + } else { + if (0 == i) { + realpos = SHT_DIV_ROUND(SHT_DIV_ROUND(percent * Shutter.open_max[index] * calibrate_pos[i+1], Settings.shuttercoeff[i][index]), 10); + } else { + + + realpos += SHT_DIV_ROUND(SHT_DIV_ROUND((percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter.open_max[index] * (calibrate_pos[i+1] - calibrate_pos[i]), Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]), 100); + } + break; + } + } + return realpos; + } +} + +uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index) +{ + if (0 >= realpos) return 0; + if (Shutter.open_max[index] <= realpos) return 100; + if (Settings.shutter_set50percent[index] != 50) { + return (Settings.shuttercoeff[2][index] * 5 > realpos) ? SHT_DIV_ROUND(realpos, Settings.shuttercoeff[2][index]) : SHT_DIV_ROUND(realpos-Settings.shuttercoeff[0][index], Settings.shuttercoeff[1][index]); + } else { + uint16_t realpercent; + + for (uint32_t i = 0; i < 5; i++) { + if (realpos >= Shutter.open_max[index] * calibrate_pos[i+1] / 100) { + realpercent = SHT_DIV_ROUND(Settings.shuttercoeff[i][index], 10); + + } else { + if (0 == i) { + realpercent = SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i], 100)) * 10 * Settings.shuttercoeff[i][index], calibrate_pos[i+1]), Shutter.open_max[index]); + } else { + + + + realpercent += SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i], 100)) * 10 * (Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]), (calibrate_pos[i+1] - calibrate_pos[i])), Shutter.open_max[index]) ; + } + break; + } + } + return realpercent; + } +} + +void ShutterInit(void) +{ + shutters_present = 0; + Shutter.mask = 0; + + Shutter.old_power = power; + bool relay_in_interlock = false; + + + if (Settings.shutter_startrelay[MAX_SHUTTERS] == 0) { + Shutter.max_pwm_frequency = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.max_pwm_frequency; + } + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { + + Settings.shutter_startrelay[i] = (Settings.shutter_startrelay[i] == 0 && i == 0? 1 : Settings.shutter_startrelay[i]); + if (Settings.shutter_startrelay[i] && (Settings.shutter_startrelay[i] < 9)) { + shutters_present++; + + + Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1) ; + + for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings.flag.interlock; j++) { + + if (Settings.interlock[j] && (Settings.interlock[j] & Shutter.mask)) { + + relay_in_interlock = true; + } + } + if (relay_in_interlock) { + if (Settings.pulse_timer[i] > 0) { + Shutter.mode = SHT_PULSE_OPEN__PULSE_CLOSE; + } else { + Shutter.mode = SHT_OFF_OPEN__OFF_CLOSE; + } + } else { + Shutter.mode = SHT_OFF_ON__OPEN_CLOSE; + if ((pin[GPIO_PWM1+i] < 99) && (pin[GPIO_CNTR1+i] < 99)) { + Shutter.mode = SHT_OFF_ON__OPEN_CLOSE_STEPPER; + Shutter.pwm_frequency = 0; + analogWriteFreq(Shutter.pwm_frequency); + analogWrite(pin[GPIO_PWM1+i], 50); + } + } + + TickerShutter.attach_ms(50, ShutterRtc50mS ); + + Settings.shutter_set50percent[i] = (Settings.shutter_set50percent[i] > 0) ? Settings.shutter_set50percent[i] : 50; + + + Shutter.open_time[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100; + Shutter.close_time[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100; + + + Shutter.open_max[i] = 200 * Shutter.open_time[i]; + Shutter.close_velocity[i] = Shutter.open_max[i] / Shutter.close_time[i] / 2 ; + Shutter.max_close_pwm_frequency[i] = Shutter.max_pwm_frequency*Shutter.open_time[i] / Shutter.close_time[i]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d Closefreq: %d"),i, Shutter.max_close_pwm_frequency[i]); + + + if (Settings.shutter_set50percent[i] != 50) { + Settings.shuttercoeff[1][i] = Shutter.open_max[i] * (100 - Settings.shutter_set50percent[i] ) / 5000; + Settings.shuttercoeff[0][i] = Shutter.open_max[i] - (Settings.shuttercoeff[1][i] * 100); + Settings.shuttercoeff[2][i] = (Settings.shuttercoeff[0][i] + 5 * Settings.shuttercoeff[1][i]) / 5; + } + Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1); + + Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i); + + Shutter.start_position[i] = Shutter.target_position[i] = Shutter.real_position[i]; + Shutter.motordelay[i] = Settings.shutter_motordelay[i]; + + char shutter_open_chr[10]; + dtostrfd((float)Shutter.open_time[i] / 10 , 1, shutter_open_chr); + char shutter_close_chr[10]; + dtostrfd((float)Shutter.close_time[i] / 10, 1, shutter_close_chr); + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100, Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoeffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, is locked %d, end stop time enabled %d, shuttermode %d, motordelay %d"), + i+1, Settings.shutter_startrelay[i], Shutter.real_position[i], Settings.shutter_position[i], Shutter.close_velocity[i], Shutter.open_max[i], shutter_open_chr, shutter_close_chr, + Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i], + Shutter.mask, (Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, Shutter.mode, Shutter.motordelay[i]); + + } else { + + break; + } + ShutterLimitRealAndTargetPositions(i); + Settings.shutter_accuracy = 1; + } +} + +void ShutterReportPosition(bool always) +{ + uint32_t shutter_moving = 0; + Response_P(PSTR("{")); + for (uint32_t i = 0; i < shutters_present; i++) { + + uint32_t position = ShutterRealToPercentPosition(Shutter.real_position[i], i); + if (Shutter.direction[i] != 0) { + shutter_moving = 1; + ShutterLogPos(i); + } + if (i) { ResponseAppend_P(PSTR(",")); } + ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter.direction[i]); + } + ResponseJsonEnd(); + if (always || (1 == shutter_moving)) { + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_PRFX_SHUTTER)); + } + if (rules_flag.shutter_moving > shutter_moving) { + rules_flag.shutter_moved = 1; + } else { + rules_flag.shutter_moved = 0; + } + rules_flag.shutter_moving = shutter_moving; + +} + +void ShutterLimitRealAndTargetPositions(uint32_t i) { + if (Shutter.real_position[i]<0) Shutter.real_position[i] = 0; + if (Shutter.real_position[i]>Shutter.open_max[i]) Shutter.real_position[i] = Shutter.open_max[i]; + if (Shutter.target_position[i]<0) Shutter.target_position[i] = 0; + if (Shutter.target_position[i]>Shutter.open_max[i]) Shutter.target_position[i] = Shutter.open_max[i]; +} + +void ShutterUpdatePosition(void) +{ + + char scommand[CMDSZ]; + char stopic[TOPSZ]; + + for (uint32_t i = 0; i < shutters_present; i++) { + if (Shutter.direction[i] != 0) { + int32_t stop_position_delta = 20; + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { + + + Shutter.real_position[i] = ShutterCounterBasedPosition(i); + + int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i]; + int32_t max_freq_change_per_sec = Shutter.max_pwm_frequency*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); + int32_t min_runtime_ms = Shutter.pwm_frequency*1000 / max_freq_change_per_sec; + int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i]; + int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_frequency / max_frequency * Shutter.direction[i] ; + + int32_t next_possible_stop = Shutter.real_position[i] + minstopway ; + stop_position_delta =200 * Shutter.pwm_frequency/max_frequency + Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]); + + + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, velocity %d, minstopway %d,cur_freq %d, max_frequency %d, act_freq_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d"),Shutter.time[i],velocity,minstopway, + Shutter.pwm_frequency,max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i]); + + if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > Shutter.target_position[i] * Shutter.direction[i] ) { + + Shutter.accelerator[i] = - tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*12/200); + + } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_frequency == max_frequency) { + Shutter.accelerator[i] = 0; + } + } else { + Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); + } + if ( Shutter.real_position[i] * Shutter.direction[i] + stop_position_delta >= Shutter.target_position[i] * Shutter.direction[i] ) { + + + uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : 1) ; + int16_t missing_steps; + + switch (Shutter.mode) { + case SHT_PULSE_OPEN__PULSE_CLOSE: + + if (SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source) { + ExecuteCommandPower(cur_relay, 1, SRC_SHUTTER); + } else { + last_source = SRC_SHUTTER; + } + break; + case SHT_OFF_ON__OPEN_CLOSE_STEPPER: + missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) - RtcSettings.pulse_counter[i]; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_frequency); + Shutter.accelerator[i] = 0; + Shutter.pwm_frequency = Shutter.pwm_frequency > 250 ? 250 : Shutter.pwm_frequency; + analogWriteFreq(Shutter.pwm_frequency); + analogWrite(pin[GPIO_PWM1+i], 50); + Shutter.pwm_frequency = 0; + analogWriteFreq(Shutter.pwm_frequency); + while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) { + delay(1); + } + analogWrite(pin[GPIO_PWM1+i], 0); + Shutter.real_position[i] = ShutterCounterBasedPosition(i); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT:Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]); + + if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { + ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); + ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); + } + break; + case SHT_OFF_ON__OPEN_CLOSE: + if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { + ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); + ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); + } + break; + case SHT_OFF_OPEN__OFF_CLOSE: + + if ((1 << (cur_relay-1)) & power) { + + ExecuteCommandPower(cur_relay, 0, SRC_SHUTTER); + } + break; + } + ShutterLimitRealAndTargetPositions(i); + Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i); + + ShutterLogPos(i); + + Shutter.start_position[i] = Shutter.real_position[i]; + + + snprintf_P(scommand, sizeof(scommand),PSTR(D_SHUTTER "%d"), i+1); + GetTopic_P(stopic, STAT, mqtt_topic, scommand); + Response_P("%d", (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]); + MqttPublish(stopic, Settings.flag.mqtt_power_retain); + + Shutter.direction[i] = 0; + ShutterReportPosition(true); + XdrvRulesProcess(); + } + } + } +} + +bool ShutterState(uint32_t device) +{ + device--; + device &= 3; + return (Settings.flag3.shutter_mode && + (Shutter.mask & (1 << (Settings.shutter_startrelay[device]-1))) ); +} + +void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) +{ + + if ( ( (1 == direction) && ((Shutter.open_max[i] - Shutter.real_position[i]) / 100 <= 2) ) + || ( (-1 == direction) && (Shutter.real_position[i] / Shutter.close_velocity[i] <= 2)) ) { + Shutter.skip_relay_change = 1; + } else { + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { + Shutter.pwm_frequency = 0; + analogWriteFreq(Shutter.pwm_frequency); + analogWrite(pin[GPIO_PWM1+i], 0); + + if (pin[GPIO_CNTR1+i] < 99) { + RtcSettings.pulse_counter[i] = 0; + } + Shutter.accelerator[i] = Shutter.max_pwm_frequency / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Ramp up: %d"), Shutter.accelerator[i]); + } + Shutter.target_position[i] = target_pos; + Shutter.start_position[i] = Shutter.real_position[i]; + Shutter.time[i] = 0; + Shutter.skip_relay_change = 0; + Shutter.direction[i] = direction; + + } + +} + +void ShutterWaitForMotorStop(uint32_t i) +{ + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop..")); + if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) { + if (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode) { + + while (Shutter.pwm_frequency > 0) { + Shutter.accelerator[i] = 0; + Shutter.pwm_frequency = tmax(Shutter.pwm_frequency-((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) , 0); + analogWriteFreq(Shutter.pwm_frequency); + analogWrite(pin[GPIO_PWM1+i], 50); + delay(50); + } + analogWrite(pin[GPIO_PWM1+i], 0); + Shutter.real_position[i] = ShutterCounterBasedPosition(i); + } else { + ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); + delay(MOTOR_STOP_TIME); + } + } else { + delay(MOTOR_STOP_TIME); + } +} + +int32_t ShutterCounterBasedPosition(uint32_t i) +{ + return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i]; +} + +void ShutterRelayChanged(void) +{ + + + + + char stemp1[10]; + + for (uint32_t i = 0; i < shutters_present; i++) { + power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3; + + uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; + + if (manual_relays_changed) { + + ShutterLimitRealAndTargetPositions(i); + if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE || Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { + ShutterWaitForMotorStop(i); + switch (powerstate_local) { + case 1: + ShutterStartInit(i, 1, Shutter.open_max[i]); + break; + case 3: + ShutterStartInit(i, -1, 0); + break; + default: + + Shutter.target_position[i] = Shutter.real_position[i]; + } + } else { + if (Shutter.direction[i] != 0 && (!powerstate_local || (powerstate_local && Shutter.mode == SHT_PULSE_OPEN__PULSE_CLOSE))) { + Shutter.target_position[i] = Shutter.real_position[i]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); + } else { + last_source = SRC_SHUTTER; + if (powerstate_local == 2) { + + ShutterWaitForMotorStop(i); + ShutterStartInit(i, -1, 0); + } else { + + ShutterWaitForMotorStop(i); + ShutterStartInit(i, 1, Shutter.open_max[i]); + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter.target_position[i], powerstate_local); + } + } + } +} + +bool ShutterButtonIsSimultaneousHold(uint32_t button_index, uint32_t shutter_index) { + + uint32 min_shutterbutton_hold_timer = -1; + for (uint32_t i = 0; i < MAX_KEYS; i++) { + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (Button.hold_timer[i] < min_shutterbutton_hold_timer)) + min_shutterbutton_hold_timer = Button.hold_timer[i]; + } + return (min_shutterbutton_hold_timer > (Button.hold_timer[button_index]>>1)); +} + +void ShutterButtonHandler(void) +{ + uint8_t buttonState = SHT_NOT_PRESSED; + uint8_t button = XdrvMailbox.payload; + uint8_t press_index; + uint32_t button_index = XdrvMailbox.index; + uint8_t shutter_index = Settings.shutter_button[button_index] & 0x03; + + uint16_t loops_per_second = 1000 / Settings.button_debounce; + + if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { + if (Settings.flag.button_single) { + buttonState = SHT_PRESSED_MULTI; + press_index = 1; + } else { + if ((Shutter.direction[shutter_index]) && (Button.press_counter[button_index]==0)) { + buttonState = SHT_PRESSED_IMMEDIATE; + press_index = 1; + Button.press_counter[button_index] = 99; + } else + Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; + Button.window_timer[button_index] = loops_per_second / 2; + } + blinks = 201; + } + + if (NOT_PRESSED == button) { + Button.hold_timer[button_index] = 0; + } else { + Button.hold_timer[button_index]++; + if (!Settings.flag.button_single) { + if (Settings.param[P_HOLD_IGNORE] > 0) { + if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) { + Button.hold_timer[button_index] = 0; + Button.press_counter[button_index] = 0; + } + } + if ((Button.press_counter[button_index]<99) && (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10)) { + + if (ShutterButtonIsSimultaneousHold(button_index, shutter_index)) { + + for (uint32_t i = 0; i < MAX_KEYS; i++) + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index)) + Button.press_counter[i] = 99; + press_index = 0; + buttonState = SHT_PRESSED_HOLD_SIMULTANEOUS; + } + if (Button.press_counter[button_index]<99) { + press_index = 0; + buttonState = SHT_PRESSED_HOLD; + } + Button.press_counter[button_index] = 0; + } + if ((Button.press_counter[button_index]==0) && (Button.hold_timer[button_index] == loops_per_second * IMMINENT_RESET_FACTOR * Settings.param[P_HOLD_TIME] / 10)) { + + if (ShutterButtonIsSimultaneousHold(button_index, shutter_index)) { + + press_index = 0; + buttonState = SHT_PRESSED_EXT_HOLD_SIMULTANEOUS; + } + } + } + } + + if (!Settings.flag.button_single) { + if (Button.window_timer[button_index]) { + Button.window_timer[button_index]--; + } else { + if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0)) { + if (Button.press_counter[button_index]<99) { + + uint32 min_shutterbutton_press_counter = -1; + for (uint32_t i = 0; i < MAX_KEYS; i++) { + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) + min_shutterbutton_press_counter = Button.press_counter[i]; + } + if (min_shutterbutton_press_counter == Button.press_counter[button_index]) { + + press_index = Button.press_counter[button_index]; + for (uint32_t i = 0; i < MAX_KEYS; i++) + if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index)) + Button.press_counter[i] = 99; + buttonState = SHT_PRESSED_MULTI_SIMULTANEOUS; + } + if ((buttonState != SHT_PRESSED_MULTI_SIMULTANEOUS) && (Button.press_counter[button_index]<99)) { + + press_index = Button.press_counter[button_index]; + buttonState = SHT_PRESSED_MULTI; + } + } + Button.press_counter[button_index] = 0; + } + } + } + + if (buttonState != SHT_NOT_PRESSED) { + if (buttonState == SHT_PRESSED_MULTI_SIMULTANEOUS) { + if ((press_index>=5) && (press_index<=7) && (!Settings.flag.button_restrict)) { + + char scmnd[20]; + GetTextIndexed(scmnd, sizeof(scmnd), press_index -3, kCommands); + ExecuteCommand(scmnd, SRC_BUTTON); + return; + } + } else if (buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS) { + + if (!Settings.flag.button_restrict) { + char scmnd[20]; + snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); + ExecuteCommand(scmnd, SRC_BUTTON); + return; + } + } else if (buttonState <= SHT_PRESSED_IMMEDIATE) { + if (Settings.shutter_startrelay[shutter_index] && Settings.shutter_startrelay[shutter_index] <9) { + uint8_t pos_press_index = (buttonState == SHT_PRESSED_HOLD) ? 3 : (press_index-1); + if (pos_press_index>3) pos_press_index=3; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: shutter %d, button %d = %d (single=1, double=2, tripple=3, hold=4)"), shutter_index+1, button_index+1, pos_press_index+1); + XdrvMailbox.index = shutter_index +1; + last_source = SRC_BUTTON; + XdrvMailbox.data_len = 0; + char databuf[1] = ""; + XdrvMailbox.data = databuf; + XdrvMailbox.command = NULL; + if (buttonState == SHT_PRESSED_IMMEDIATE) { + XdrvMailbox.payload = XdrvMailbox.index; + CmndShutterStop(); + } + else { + uint8_t position = (Settings.shutter_button[button_index]>>(6*pos_press_index + 2)) & 0x03f; + if (position) { + if (Shutter.direction[shutter_index]) { + XdrvMailbox.payload = XdrvMailbox.index; + CmndShutterStop(); + } else { + XdrvMailbox.payload = position = (position-1)<<1; + CmndShutterPosition(); + if (Settings.shutter_button[button_index] & ((0x01<<26)< 0) && (XdrvMailbox.index <= shutters_present)) { + if (!(Settings.shutter_options[XdrvMailbox.index-1] & 2)) { + if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) { + XdrvMailbox.index = XdrvMailbox.payload; + } + uint32_t i = XdrvMailbox.index -1; + if (Shutter.direction[i] != 0) { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter.direction[i]); + + int32_t temp_realpos = Shutter.start_position[i] + ( (Shutter.time[i]+10) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); + XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, i); + + last_source = SRC_WEBGUI; + CmndShutterPosition(); + } else { + if (XdrvMailbox.command) + ResponseCmndDone(); + } + } else { + if (XdrvMailbox.command) + ResponseCmndIdxChar("Locked"); + } + } +} + +void CmndShutterPosition(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (!(Settings.shutter_options[XdrvMailbox.index-1] & 2)) { + uint32_t index = XdrvMailbox.index-1; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Pos. in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, last_source ); + + + if ((XdrvMailbox.data_len > 1) && (XdrvMailbox.payload <= 0)) { + + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_UP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_OPEN) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLEUP))) { + CmndShutterOpen(); + return; + } + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_DOWN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_CLOSE) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLEDOWN))) { + CmndShutterClose(); + return; + } + if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOP) || ((Shutter.direction[index]) && (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLEUP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLEDOWN)))) { + XdrvMailbox.payload = -99; + CmndShutterStop(); + return; + } + } + + int8_t target_pos_percent = (XdrvMailbox.payload < 0) ? 0 : ((XdrvMailbox.payload > 100) ? 100 : XdrvMailbox.payload); + + target_pos_percent = ((Settings.shutter_options[index] & 1) && (SRC_WEBGUI != last_source)) ? 100 - target_pos_percent : target_pos_percent; + if (XdrvMailbox.payload != -99) { + + Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index); + Shutter.accelerator[index] = Shutter.max_pwm_frequency / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent); + } + if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter.target_position[index] - Shutter.real_position[index] ) / Shutter.close_velocity[index] > 2) { + if (Settings.shutter_options[index] & 4) { + if (0 == target_pos_percent) Shutter.target_position[index] -= 1 * 2000; + if (100 == target_pos_percent) Shutter.target_position[index] += 1 * 2000; + } + int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1; + if (Shutter.direction[index] == -new_shutterdirection) { + + if (SHT_PULSE_OPEN__PULSE_CLOSE == Shutter.mode) { + + ExecuteCommandPower(Settings.shutter_startrelay[index] + ((new_shutterdirection == 1) ? 0 : 1), 1, SRC_SHUTTER); + delay(100); + } else { + if (SHT_OFF_OPEN__OFF_CLOSE == Shutter.mode) { + ExecuteCommandPower(Settings.shutter_startrelay[index] + ((new_shutterdirection == 1) ? 1 : 0), 0, SRC_SHUTTER); + ShutterWaitForMotorStop(index); + } + } + } + if (Shutter.direction[index] != new_shutterdirection) { + if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) { + + ShutterWaitForMotorStop(index); + ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); + ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); + if (Shutter.skip_relay_change == 0) { + + ExecuteCommandPower(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER); + + ExecuteCommandPower(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); + } + } else { + + AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start in dir %d"), Shutter.direction[index]); + ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); + if (Shutter.skip_relay_change == 0) { + ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); + } + + } + Shutter.switched_relay = 0; + } + } else { + target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index); + ShutterReportPosition(true); + } + XdrvMailbox.index = index +1; + if (XdrvMailbox.command) + ResponseCmndIdxNumber((Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent); + } else { + ShutterReportPosition(true); + if (XdrvMailbox.command) + ResponseCmndIdxChar("Locked"); + } + } +} + +void CmndShutterOpenTime(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + Settings.shutter_opentime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data)); + ShutterInit(); + } + char time_chr[10]; + dtostrfd((float)(Settings.shutter_opentime[XdrvMailbox.index -1]) / 10, 1, time_chr); + ResponseCmndIdxChar(time_chr); + } +} + +void CmndShutterCloseTime(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + Settings.shutter_closetime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data)); + ShutterInit(); + } + char time_chr[10]; + dtostrfd((float)(Settings.shutter_closetime[XdrvMailbox.index -1]) / 10, 1, time_chr); + ResponseCmndIdxChar(time_chr); + } +} + +void CmndShutterMotorDelay(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + Settings.shutter_motordelay[XdrvMailbox.index -1] = (uint16_t)(steps_per_second * CharToFloat(XdrvMailbox.data)); + ShutterInit(); + } + char time_chr[10]; + dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / steps_per_second, 2, time_chr); + ResponseCmndIdxChar(time_chr); + } +} + +void CmndShutterRelay(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 64)) { + Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; + if (XdrvMailbox.payload > 0) { + Shutter.mask |= 3 << (XdrvMailbox.payload - 1); + } else { + Shutter.mask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1); + } + Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; + ShutterInit(); + + } + ResponseCmndIdxNumber(Settings.shutter_startrelay[XdrvMailbox.index -1]); + } +} + +void CmndShutterButton(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { + uint32_t setting = 0; +# 915 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_27_shutter.ino" + if (XdrvMailbox.data_len > 0) { + uint32_t i = 0; + uint32_t button_index = 0; + bool done = false; + bool isShortCommand = false; + char *str_ptr; + + char data_copy[strlen(XdrvMailbox.data) +1]; + strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); + + for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < (1+4+4+1); str = strtok_r(nullptr, " ", &str_ptr), i++) { + int field; + if (str[0] == '-') { + field = -1; + } else { + field = atoi(str); + } + switch (i) { + case 0: + if ((field >= -1) && (field<=4)) { + button_index = (field<=0)?(-1):field; + done = (button_index==-1); + } else + done = true; + break; + case 1: + if (!strcmp_P(str, PSTR("up"))) { + setting |= (((100>>1)+1)<<2) | (((50>>1)+1)<<8) | (((75>>1)+1)<<14) | (((100>>1)+1)<<20); + isShortCommand = true; + break; + } else if (!strcmp_P(str, PSTR("down"))) { + setting |= (((0>>1)+1)<<2) | (((50>>1)+1)<<8) | (((25>>1)+1)<<14) | (((0>>1)+1)<<20); + isShortCommand = true; + break; + } else if (!strcmp_P(str, PSTR("updown"))) { + setting |= (((100>>1)+1)<<2) | (((0>>1)+1)<<8) | (((50>>1)+1)<<14); + isShortCommand = true; + break; + } + case 2: + if (isShortCommand) { + if ((field==1) && (setting & (0x3F<<(2+6*3)))) + + setting |= (0x3<<29); + done = true; + break; + } + case 3: + case 4: + if ((field >= -1) && (field<=100)) + setting |= (((field>>1)+1)<<(i*6 + (2-6))); + break; + case 5: + case 6: + case 7: + case 8: + case 9: + if (field==1) + setting |= (1<<(i + (26-5))); + break; + } + if (done) break; + } + + if (button_index) { + if (button_index==-1) { + + for (uint32_t i=0 ; i < MAX_KEYS ; i++) + if ((Settings.shutter_button[i]&0x3) == (XdrvMailbox.index-1)) + Settings.shutter_button[i] = 0; + } else { + if (setting) { + + setting |= (1<<31); + setting |= (XdrvMailbox.index-1) & 0x3; + } + Settings.shutter_button[button_index-1] = setting; + } + } + } + char setting_chr[30*MAX_KEYS] = "-", *setting_chr_ptr = setting_chr; + for (uint32_t i=0 ; i < MAX_KEYS ; i++) { + setting = Settings.shutter_button[i]; + if ((setting&(1<<31)) && ((setting&0x3) == (XdrvMailbox.index-1))) { + if (*setting_chr_ptr == 0) + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR("|")); + setting_chr_ptr += snprintf_P(setting_chr_ptr, 2, PSTR("%d"), i+1); + + for (uint32_t j=0 ; j < 4 ; j++) { + int8_t pos = (((setting>> (2+6*j))&(0x3f))-1)<<1; + if (pos>=0) + setting_chr_ptr += snprintf_P(setting_chr_ptr, 5, PSTR(" %d"), pos); + else + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" -")); + } + for (uint32_t j=0 ; j < 5 ; j++) { + bool mqtt = ((setting>>(26+j))&(0x01)!=0); + if (mqtt) + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" 1")); + else + setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" -")); + } + } + } + ResponseCmndIdxChar(setting_chr); + } +} + +void CmndShutterSetHalfway(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + Settings.shutter_set50percent[XdrvMailbox.index -1] = (Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 100 - XdrvMailbox.payload : XdrvMailbox.payload; + ShutterInit(); + } + ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 100 - Settings.shutter_set50percent[XdrvMailbox.index -1] : Settings.shutter_set50percent[XdrvMailbox.index -1]); + } +} + +void CmndShutterFrequency(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) { + Shutter.max_pwm_frequency = XdrvMailbox.payload; + if (shutters_present < 4) { + Settings.shuttercoeff[4][3] = Shutter.max_pwm_frequency; + } + ShutterInit(); + ResponseCmndNumber(XdrvMailbox.payload); + } else { + ResponseCmndNumber(Shutter.max_pwm_frequency); + } +} + +void CmndShutterSetClose(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + Shutter.real_position[XdrvMailbox.index -1] = 0; + ShutterStartInit(XdrvMailbox.index -1, 0, 0); + Settings.shutter_position[XdrvMailbox.index -1] = 0; + ResponseCmndIdxChar(D_CONFIGURATION_RESET); + } +} + +void CmndShutterInvert(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.payload == 0) { + Settings.shutter_options[XdrvMailbox.index -1] &= ~(1); + } else if (XdrvMailbox.payload == 1) { + Settings.shutter_options[XdrvMailbox.index -1] |= (1); + } + ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 1 : 0); + } +} + +void CmndShutterCalibration(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.data_len > 0) { + uint32_t i = 0; + char *str_ptr; + + char data_copy[strlen(XdrvMailbox.data) +1]; + strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); + + for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 5; str = strtok_r(nullptr, " ", &str_ptr), i++) { + int field = atoi(str); + + + if ((field <= 0) || (field > 30000) || ( (i>0) && (field <= messwerte[i-1]) ) ) { + break; + } + messwerte[i] = field; + } + for (i = 0; i < 5; i++) { + Settings.shuttercoeff[i][XdrvMailbox.index -1] = SHT_DIV_ROUND((uint32_t)messwerte[i] * 1000, messwerte[4]); + AddLog_P2(LOG_LEVEL_INFO, PSTR("Settings.shuttercoeff: %d, i: %d, value: %d, messwert %d"), i,XdrvMailbox.index -1,Settings.shuttercoeff[i][XdrvMailbox.index -1], messwerte[i]); + } + ShutterInit(); + ResponseCmndIdxChar(XdrvMailbox.data); + } else { + char setting_chr[30] = "0"; + snprintf_P(setting_chr, sizeof(setting_chr), PSTR("%d %d %d %d %d"), Settings.shuttercoeff[0][XdrvMailbox.index -1], Settings.shuttercoeff[1][XdrvMailbox.index -1], Settings.shuttercoeff[2][XdrvMailbox.index -1], Settings.shuttercoeff[3][XdrvMailbox.index -1], Settings.shuttercoeff[4][XdrvMailbox.index -1]); + ResponseCmndIdxChar(setting_chr); + } + } +} + +void CmndShutterLock(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.payload == 0) { + Settings.shutter_options[XdrvMailbox.index -1] &= ~(2); + } else if (XdrvMailbox.payload == 1) { + Settings.shutter_options[XdrvMailbox.index -1] |= (2); + } + ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 2) ? 1 : 0); + } +} + +void CmndShutterEnableEndStopTime(void) { + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { + if (XdrvMailbox.payload == 0) { + Settings.shutter_options[XdrvMailbox.index -1] &= ~(4); + } else if (XdrvMailbox.payload == 1) { + Settings.shutter_options[XdrvMailbox.index -1] |= (4); + } + ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 4) ? 1 : 0); + } +} + + + + + +bool Xdrv27(uint8_t function) +{ + bool result = false; + + if (Settings.flag3.shutter_mode) { + switch (function) { + case FUNC_PRE_INIT: + ShutterInit(); + break; + case FUNC_EVERY_50_MSECOND: + ShutterUpdatePosition(); + break; + case FUNC_EVERY_SECOND: + + ShutterReportPosition(false); + break; + + case FUNC_COMMAND: + result = DecodeCommand(kShutterCommands, ShutterCommand); + break; + case FUNC_JSON_APPEND: + for (uint8_t i = 0; i < shutters_present; i++) { + uint8_t position = (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]; + ResponseAppend_P(","); + ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter.direction[i]); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzSensor(DZ_SHUTTER, position); + } +#endif + } + break; + case FUNC_SET_POWER: + char stemp1[10]; + + Shutter.switched_relay = XdrvMailbox.index ^ Shutter.old_power; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.switched_relay,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource)); + ShutterRelayChanged(); + Shutter.old_power = XdrvMailbox.index; + break; + case FUNC_SET_DEVICE_POWER: + if (Shutter.skip_relay_change ) { + uint8_t i; + for (i = 0; i < devices_present; i++) { + if (Shutter.switched_relay &1) { + break; + } + Shutter.switched_relay >>= 1; + } + + result = true; + Shutter.skip_relay_change = 0; + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"),i); + ExecuteCommandPower(i+1, 0, SRC_SHUTTER); + } + break; + case FUNC_BUTTON_PRESSED: + if (Settings.shutter_button[XdrvMailbox.index] & (1<<31)) { + ShutterButtonHandler(); + result = true; + } + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_28_pcf8574.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_28_pcf8574.ino" +#ifdef USE_I2C +#ifdef USE_PCF8574 + + + + + + + +#define XDRV_28 28 +#define XI2C_02 2 + +#define PCF8574_ADDR1 0x20 +#define PCF8574_ADDR2 0x38 + +struct PCF8574 { + int error; + uint8_t pin[64]; + uint8_t address[MAX_PCF8574]; + uint8_t pin_mask[MAX_PCF8574] = { 0 }; + uint8_t max_connected_ports = 0; + uint8_t max_devices = 0; + char stype[9]; + bool type = false; +} Pcf8574; + +void Pcf8574SwitchRelay(void) +{ + for (uint32_t i = 0; i < devices_present; i++) { + uint8_t relay_state = bitRead(XdrvMailbox.index, i); + + + + if (Pcf8574.max_devices > 0 && Pcf8574.pin[i] < 99) { + uint8_t board = Pcf8574.pin[i]>>3; + uint8_t oldpinmask = Pcf8574.pin_mask[board]; + uint8_t _val = bitRead(rel_inverted, i) ? !relay_state : relay_state; + + + + if (_val) { + Pcf8574.pin_mask[board] |= _val << (Pcf8574.pin[i]&0x7); + } else { + Pcf8574.pin_mask[board] &= ~(1 << (Pcf8574.pin[i]&0x7)); + } + if (oldpinmask != Pcf8574.pin_mask[board]) { + Wire.beginTransmission(Pcf8574.address[board]); + Wire.write(Pcf8574.pin_mask[board]); + Pcf8574.error = Wire.endTransmission(); + } + + } + } +} + +void Pcf8574Init(void) +{ + uint8_t pcf8574_address = PCF8574_ADDR1; + while ((Pcf8574.max_devices < MAX_PCF8574) && (pcf8574_address < PCF8574_ADDR2 +8)) { + + + + if (I2cSetDevice(pcf8574_address)) { + Pcf8574.type = true; + + Pcf8574.address[Pcf8574.max_devices] = pcf8574_address; + Pcf8574.max_devices++; + + strcpy(Pcf8574.stype, "PCF8574"); + if (pcf8574_address >= PCF8574_ADDR2) { + strcpy(Pcf8574.stype, "PCF8574A"); + } + I2cSetActiveFound(pcf8574_address, Pcf8574.stype); + } + + pcf8574_address++; +#ifdef USE_MCP230xx_ADDR + if (USE_MCP230xx_ADDR == pcf8574_address) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Addr: 0x%x reserved for MCP320xx, skipping PCF8574 probe"), pcf8574_address); + pcf8574_address++; + } +#endif + if ((PCF8574_ADDR1 +7) == pcf8574_address) { + pcf8574_address = PCF8574_ADDR2 +1; + } + } + if (Pcf8574.type) { + for (uint32_t i = 0; i < sizeof(Pcf8574.pin); i++) { + Pcf8574.pin[i] = 99; + } + devices_present = devices_present - Pcf8574.max_connected_ports; + Pcf8574.max_connected_ports = 0; + for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PCF: Device %d config 0x%02x"), idx +1, Settings.pcf8574_config[idx]); + + for (uint32_t i = 0; i < 8; i++) { + uint8_t _result = Settings.pcf8574_config[idx] >> i &1; + + if (_result > 0) { + Pcf8574.pin[devices_present] = i + 8 * idx; + bitWrite(rel_inverted, devices_present, Settings.flag3.pcf8574_ports_inverted); + devices_present++; + Pcf8574.max_connected_ports++; + } + } + } + AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Total devices %d, PCF8574 output ports %d"), Pcf8574.max_devices, Pcf8574.max_connected_ports); + } +} + + + + + +#ifdef USE_WEBSERVER + +#define WEB_HANDLE_PCF8574 "pcf" + +const char HTTP_BTN_MENU_PCF8574[] PROGMEM = + "

"; + +const char HTTP_FORM_I2C_PCF8574_1[] PROGMEM = + "
 " D_PCF8574_PARAMETERS " " + "
" + "

" D_INVERT_PORTS "


"; + +const char HTTP_FORM_I2C_PCF8574_2[] PROGMEM = + "" D_DEVICE " %d " D_PORT " %d"; + +void HandlePcf8574(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_PCF8574)); + + if (WebServer->hasArg("save")) { + Pcf8574SaveSettings(); + WebRestart(1); + return; + } + + WSContentStart_P(D_CONFIGURE_PCF8574); + WSContentSendStyle(); + WSContentSend_P(HTTP_FORM_I2C_PCF8574_1, (Settings.flag3.pcf8574_ports_inverted) ? " checked" : ""); + WSContentSend_P(HTTP_TABLE100); + for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { + for (uint32_t idx2 = 0; idx2 < 8; idx2++) { + uint8_t helper = 1 << idx2; + WSContentSend_P(HTTP_FORM_I2C_PCF8574_2, + idx +1, idx2, + idx2 + 8*idx, + idx2 + 8*idx, + ((helper & Settings.pcf8574_config[idx]) >> idx2 == 0) ? " selected " : " ", + ((helper & Settings.pcf8574_config[idx]) >> idx2 == 1) ? " selected " : " " + ); + } + } + WSContentSend_P(PSTR("")); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void Pcf8574SaveSettings(void) +{ + char stemp[7]; + char tmp[100]; + + + + Settings.flag3.pcf8574_ports_inverted = WebServer->hasArg("b1"); + for (byte idx = 0; idx < Pcf8574.max_devices; idx++) { + byte count=0; + byte n = Settings.pcf8574_config[idx]; + while(n!=0) { + n = n&(n-1); + count++; + } + if (count <= devices_present) { + devices_present = devices_present - count; + } + for (byte i = 0; i < 8; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("i2cs%d"), i+8*idx); + WebGetArg(stemp, tmp, sizeof(tmp)); + byte _value = (!strlen(tmp)) ? 0 : atoi(tmp); + if (_value) { + Settings.pcf8574_config[idx] = Settings.pcf8574_config[idx] | 1 << i; + devices_present++; + Pcf8574.max_connected_ports++; + } else { + Settings.pcf8574_config[idx] = Settings.pcf8574_config[idx] & ~(1 << i ); + } + } + + + + } +} +#endif + + + + + +bool Xdrv28(uint8_t function) +{ + if (!I2cEnabled(XI2C_02)) { return false; } + + bool result = false; + + if (FUNC_PRE_INIT == function) { + Pcf8574Init(); + } + else if (Pcf8574.type) { + switch (function) { + case FUNC_SET_POWER: + Pcf8574SwitchRelay(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_PCF8574); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer->on("/" WEB_HANDLE_PCF8574, HandlePcf8574); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_29_deepsleep.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_29_deepsleep.ino" +#ifdef USE_DEEPSLEEP +# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_29_deepsleep.ino" +#define XDRV_29 29 + +#define D_PRFX_DEEPSLEEP "DeepSleep" +#define D_CMND_DEEPSLEEP_TIME "Time" + +const uint32_t DEEPSLEEP_MAX = 10 * 366 * 24 * 60 * 60; +const uint32_t DEEPSLEEP_MAX_CYCLE = 60 * 60; +const uint32_t DEEPSLEEP_MIN_TIME = 5; +const uint32_t DEEPSLEEP_START_COUNTDOWN = 4; + +const char kDeepsleepCommands[] PROGMEM = D_PRFX_DEEPSLEEP "|" + D_CMND_DEEPSLEEP_TIME ; + +void (* const DeepsleepCommand[])(void) PROGMEM = { + &CmndDeepsleepTime }; + +uint32_t deepsleep_sleeptime = 0; +uint8_t deepsleep_flag = 0; + +bool DeepSleepEnabled(void) +{ + if ((Settings.deepsleep < 10) || (Settings.deepsleep > DEEPSLEEP_MAX)) { + Settings.deepsleep = 0; + return false; + } + + if (pin[GPIO_DEEPSLEEP] < 99) { + pinMode(pin[GPIO_DEEPSLEEP], INPUT_PULLUP); + return (digitalRead(pin[GPIO_DEEPSLEEP])); + } + + return true; +} + +void DeepSleepReInit(void) +{ + if ((ResetReason() == REASON_DEEP_SLEEP_AWAKE) && DeepSleepEnabled()) { + if ((RtcSettings.ultradeepsleep > DEEPSLEEP_MAX_CYCLE) && (RtcSettings.ultradeepsleep < 1700000000)) { + + RtcSettings.ultradeepsleep = RtcSettings.ultradeepsleep - DEEPSLEEP_MAX_CYCLE; + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Remain DeepSleep %d"), RtcSettings.ultradeepsleep); + RtcSettingsSave(); + RtcRebootReset(); + ESP.deepSleep(100 * RtcSettings.deepsleep_slip * (DEEPSLEEP_MAX_CYCLE < RtcSettings.ultradeepsleep ? DEEPSLEEP_MAX_CYCLE : RtcSettings.ultradeepsleep), WAKE_RF_DEFAULT); + yield(); + + } + } + + RtcSettings.ultradeepsleep = 0; +} + +void DeepSleepPrepare(void) +{ + + + + + if ((RtcSettings.nextwakeup == 0) || + (RtcSettings.deepsleep_slip < 9000) || + (RtcSettings.deepsleep_slip > 11000) || + (RtcSettings.nextwakeup > (UtcTime() + Settings.deepsleep))) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Reset wrong settings wakeup: %ld, slip %ld"), RtcSettings.nextwakeup, RtcSettings.deepsleep_slip ); + RtcSettings.nextwakeup = 0; + RtcSettings.deepsleep_slip = 10000; + } + + + + int16_t timeslip = (int16_t)(RtcSettings.nextwakeup + millis() / 1000 - UtcTime()) * 10; + + + + timeslip = (timeslip < -(int32_t)Settings.deepsleep) ? 0 : (timeslip > (int32_t)Settings.deepsleep) ? 0 : 1; + if (timeslip) { + RtcSettings.deepsleep_slip = (Settings.deepsleep + RtcSettings.nextwakeup - UtcTime()) * RtcSettings.deepsleep_slip / tmax((Settings.deepsleep - (millis() / 1000)),5); + + RtcSettings.deepsleep_slip = tmin(tmax(RtcSettings.deepsleep_slip, 9000), 11000); + RtcSettings.nextwakeup += Settings.deepsleep; + } + + + + if (RtcSettings.nextwakeup <= (UtcTime() - DEEPSLEEP_MIN_TIME)) { + + RtcSettings.nextwakeup += (((UtcTime() + DEEPSLEEP_MIN_TIME - RtcSettings.nextwakeup) / Settings.deepsleep) + 1) * Settings.deepsleep; + } + + String dt = GetDT(RtcSettings.nextwakeup + LocalTime() - UtcTime()); + + + deepsleep_sleeptime = tmin((uint32_t)DEEPSLEEP_MAX_CYCLE ,RtcSettings.nextwakeup - UtcTime()); + + + Response_P(PSTR("{\"" D_PRFX_DEEPSLEEP "\":{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%d}}"), (char*)dt.c_str(), RtcSettings.nextwakeup); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_STATUS)); + + + +} + +void DeepSleepStart(void) +{ + AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Sleeping")); + + WifiShutdown(); + RtcSettings.ultradeepsleep = RtcSettings.nextwakeup - UtcTime(); + RtcSettingsSave(); + + ESP.deepSleep(100 * RtcSettings.deepsleep_slip * deepsleep_sleeptime); + yield(); +} + +void DeepSleepEverySecond(void) +{ + if (!deepsleep_flag) { return; } + + if (DeepSleepEnabled()) { + if (DEEPSLEEP_START_COUNTDOWN == deepsleep_flag) { + SettingsSaveAll(); + DeepSleepPrepare(); + } + deepsleep_flag--; + if (deepsleep_flag <= 0) { + DeepSleepStart(); + } + } else { + deepsleep_flag = 0; + } +} + + + + + +void CmndDeepsleepTime(void) +{ + if ((0 == XdrvMailbox.payload) || + ((XdrvMailbox.payload > 10) && (XdrvMailbox.payload < DEEPSLEEP_MAX))) { + Settings.deepsleep = XdrvMailbox.payload; + RtcSettings.nextwakeup = 0; + deepsleep_flag = (0 == XdrvMailbox.payload) ? 0 : DEEPSLEEP_START_COUNTDOWN; + if (deepsleep_flag) { + if (!Settings.tele_period) { + Settings.tele_period = TELE_PERIOD; + } + } + } + Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, Settings.deepsleep); +} + + + + + +bool Xdrv29(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_SECOND: + DeepSleepEverySecond(); + break; + case FUNC_AFTER_TELEPERIOD: + if (DeepSleepEnabled() && !deepsleep_flag && (Settings.tele_period == 10 || Settings.tele_period == 300 || UpTime() > Settings.tele_period)) { + deepsleep_flag = DEEPSLEEP_START_COUNTDOWN; + } + break; + case FUNC_COMMAND: + result = DecodeCommand(kDeepsleepCommands, DeepsleepCommand); + break; + case FUNC_PRE_INIT: + DeepSleepReInit(); + break; + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" +#ifdef USE_LIGHT +#ifdef USE_EXS_DIMMER + + + + + + + +#define XDRV_30 30 + +#define EXS_GATE_1_ON 0x20 +#define EXS_GATE_1_OFF 0x21 +#define EXS_DIMM_1_ON 0x22 +#define EXS_DIMM_1_OFF 0x23 +#define EXS_DIMM_1_TBL 0x24 +#define EXS_DIMM_1_VAL 0x25 +#define EXS_GATE_2_ON 0x30 +#define EXS_GATE_2_OFF 0x31 +#define EXS_DIMM_2_ON 0x32 +#define EXS_DIMM_2_OFF 0x33 +#define EXS_DIMM_2_TBL 0x34 +#define EXS_DIMM_2_VAL 0x35 +#define EXS_GATES_ON 0x40 +#define EXS_GATES_OFF 0x41 +#define EXS_DIMMS_ON 0x50 +#define EXS_DIMMS_OFF 0x51 +#define EXS_CH_LOCK 0x60 +#define EXS_GET_VALUES 0xFA +#define EXS_WRITE_EE 0xFC +#define EXS_READ_EE 0xFD +#define EXS_GET_VERSION 0xFE +#define EXS_RESET 0xFF + +#define EXS_BUFFER_SIZE 256 +#define EXS_ACK_TIMEOUT 200 + +#include + +TasmotaSerial *ExsSerial = nullptr; + +typedef struct +{ + uint8_t on = 0; + uint8_t bright_tbl = 0; + uint8_t dimm = 0; + uint8_t impuls_start = 0; + uint32_t impuls_len = 0; +} CHANNEL; + +typedef struct +{ + uint8_t version_major = 0; + uint8_t version_minor = 0; + CHANNEL channel[2]; + uint8_t gate_lock = 0; +} DIMMER; + +struct EXS +{ + uint8_t *buffer = nullptr; + int byte_counter = 0; + int cmd_status = 0; + uint8_t power = 0; + uint8_t dimm[2] = {0, 0}; + DIMMER dimmer; +} Exs; + + + + + +uint8_t crc8(const uint8_t *p, uint8_t len) +{ + const uint8_t table[] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D}; + + const uint8_t table_rev[] = { + 0x00, 0x70, 0xE0, 0x90, 0xC1, 0xB1, 0x21, 0x51, + 0x83, 0xF3, 0x63, 0x13, 0x42, 0x32, 0xA2, 0xD2}; + + uint8_t offset; + uint8_t temp, crc8_temp; + uint8_t crc8 = 0; + + for (int i = 0; i < len; i++) + { + temp = *(p + i); + offset = temp ^ crc8; + offset >>= 4; + crc8_temp = crc8 & 0x0f; + crc8 = crc8_temp ^ table_rev[offset]; + offset = crc8 ^ temp; + offset &= 0x0f; + crc8_temp = crc8 & 0xf0; + crc8 = crc8_temp ^ table[offset]; + } + return crc8 ^ 0x55; +} + +void ExsSerialSend(const uint8_t data[] = nullptr, uint16_t len = 0) +{ + int retries = 3; + char rc; + +#ifdef EXS_DEBUG + snprintf_P(log_data, sizeof(log_data), PSTR("EXS: Tx Packet: \"")); + for (uint32_t i = 0; i < len; i++) + { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, data[i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + while (retries) + { + retries--; + + ExsSerial->write(data, len); + ExsSerial->flush(); + + + uint32_t snd_time = millis(); + while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) && + (!ExsSerial->available())) + ; + + if (!ExsSerial->available()) + { + +#ifdef EXS_DEBUG + AddLog_P(LOG_LEVEL_DEBUG, PSTR("ESX: serial send timeout")); +#endif + continue; + } + + rc = ExsSerial->read(); + if (rc == 0xFF) + break; + } +} + +void ExsSendCmd(uint8_t cmd, uint8_t value) +{ + uint8_t buffer[8]; + uint16_t len; + + buffer[0] = 0x7b; + buffer[3] = cmd; + + switch (cmd) + { + case EXS_GATE_1_ON: + case EXS_GATE_1_OFF: + case EXS_DIMM_1_ON: + case EXS_DIMM_1_OFF: + case EXS_GATE_2_ON: + case EXS_GATE_2_OFF: + case EXS_DIMM_2_ON: + case EXS_DIMM_2_OFF: + case EXS_GATES_ON: + case EXS_GATES_OFF: + case EXS_DIMMS_ON: + case EXS_DIMMS_OFF: + case EXS_GET_VALUES: + case EXS_GET_VERSION: + case EXS_RESET: + buffer[2] = 1; + len = 4; + break; + + case EXS_CH_LOCK: + case EXS_DIMM_1_TBL: + case EXS_DIMM_1_VAL: + case EXS_DIMM_2_TBL: + case EXS_DIMM_2_VAL: + buffer[2] = 2; + buffer[4] = value; + len = 5; + break; + } + buffer[1] = crc8(&buffer[3], buffer[2]); + + ExsSerialSend(buffer, len); +} + +uint8_t ExsSetPower(uint8_t device, uint8_t power) +{ + Exs.dimmer.channel[device].dimm = power; + ExsSendCmd(EXS_DIMM_1_ON + 0x10 * device + power ^ 1, 0); +} + +uint8_t ExsSetBri(uint8_t device, uint8_t bri) +{ + Exs.dimmer.channel[device].bright_tbl = bri; + ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * device, bri); +} + +uint8_t ExsSyncState(uint8_t device) +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Channel %d Power Want %d, Is %d"), + device, bitRead(Exs.power, device), Exs.dimmer.channel[device].dimm); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Set Channel %d Brightness Want %d, Is %d"), + device, Exs.dimm[device], Exs.dimmer.channel[device].bright_tbl); +#endif + + if (bitRead(Exs.power, device) && + Exs.dimm[device] != Exs.dimmer.channel[device].bright_tbl) { + ExsSetBri(device, Exs.dimm[device]); + } + + if (!Exs.dimm[device]) { + Exs.dimmer.channel[device].dimm = 0; + } else if (Exs.dimmer.channel[device].dimm != bitRead(Exs.power, device)) { + ExsSetPower(device, bitRead(Exs.power, device)); + } +} + +bool ExsSyncState() +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Serial %p, Cmd %d"), ExsSerial, Exs.cmd_status); +#endif + + if (!ExsSerial || Exs.cmd_status != 0) + return false; + + ExsSyncState(0); + ExsSyncState(1); +} + +void ExsDebugState() +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: MCU v%d.%d, c0: On:%d,Dim:%d,Tbl:%d(%d%%), c1: On:%d,Dim:%d,Tbl:%d(%d%%), ChLock: %d"), + Exs.dimmer.version_major, Exs.dimmer.version_minor, + Exs.dimmer.channel[0].on, Exs.dimmer.channel[0].dimm, + Exs.dimmer.channel[0].bright_tbl, + changeUIntScale(Exs.dimmer.channel[0].bright_tbl, 0, 255, 0, 100), + Exs.dimmer.channel[1].on, Exs.dimmer.channel[1].dimm, + Exs.dimmer.channel[1].bright_tbl, + changeUIntScale(Exs.dimmer.channel[1].bright_tbl, 0, 255, 0, 100), + Exs.dimmer.gate_lock); +#endif +} + +void ExsPacketProcess(void) +{ + uint8_t len = Exs.buffer[1]; + uint8_t cmd = Exs.buffer[2]; + + switch (cmd) + { + case EXS_GET_VALUES: +# 294 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" + if (len > 9) + { + Exs.dimmer.version_major = Exs.buffer[3]; + Exs.dimmer.version_minor = Exs.buffer[4]; + + + Exs.dimmer.channel[0].on = Exs.buffer[6]; + Exs.dimmer.channel[0].dimm = Exs.buffer[6]; + Exs.dimmer.channel[0].bright_tbl = Exs.buffer[7]; + + + Exs.dimmer.channel[1].on = Exs.buffer[9]; + Exs.dimmer.channel[1].dimm = Exs.buffer[9]; + Exs.dimmer.channel[1].bright_tbl = Exs.buffer[10]; + + Exs.dimmer.gate_lock = Exs.buffer[11]; + } + else +# 327 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" + { + Exs.dimmer.version_major = 1; + Exs.dimmer.version_minor = 0; + + + Exs.dimmer.channel[0].on = Exs.buffer[4] - 48; + Exs.dimmer.channel[0].dimm = Exs.buffer[4] - 48; + Exs.dimmer.channel[0].bright_tbl = Exs.buffer[5] - 48; + + + Exs.dimmer.channel[1].on = Exs.buffer[7] - 48; + Exs.dimmer.channel[1].dimm = Exs.buffer[7] - 48; + Exs.dimmer.channel[1].bright_tbl = Exs.buffer[8] - 48; + + Exs.dimmer.gate_lock = Exs.buffer[9] - 48; + } + + ExsDebugState(); + ExsSyncState(); + ExsDebugState(); + break; + default: + break; + } +} + + + +bool ExsModuleSelected(void) +{ + Settings.light_correction = 0; + Settings.flag.mqtt_serial = 0; + Settings.flag3.pwm_multi_channels = 1; + SetSeriallog(LOG_LEVEL_NONE); + + devices_present = +2; + light_type = LT_SERIAL2; + return true; +} + +bool ExsSetChannels(void) +{ +#ifdef EXS_DEBUG + snprintf_P(log_data, sizeof(log_data), PSTR("EXS: SetChannels: \"")); + for (int i = 0; i < XdrvMailbox.data_len; i++) + { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, ((uint8_t *)XdrvMailbox.data)[i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + Exs.dimm[0] = ((uint8_t *)XdrvMailbox.data)[0]; + Exs.dimm[1] = ((uint8_t *)XdrvMailbox.data)[1]; + return ExsSyncState(); +} + +bool ExsSetPower(void) +{ + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Set Power, Device %d, Power 0x%02x"), + active_device, XdrvMailbox.index); + + Exs.power = XdrvMailbox.index; + return ExsSyncState(); +} + +void EsxMcuStart(void) +{ + int retries = 3; + +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Request MCU configuration, PIN %d to Low"), pin[GPIO_EXS_ENABLE]); +#endif + + pinMode(pin[GPIO_EXS_ENABLE], OUTPUT); + digitalWrite(pin[GPIO_EXS_ENABLE], LOW); + + delay(1); + + while (ExsSerial->available()) + { + + ExsSerial->read(); + } +} + +void ExsInit(void) +{ +#ifdef EXS_DEBUG + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Starting Tx %d Rx %d"), pin[GPIO_TXD], pin[GPIO_RXD]); +#endif + + Exs.buffer = (uint8_t *)malloc(EXS_BUFFER_SIZE); + if (Exs.buffer != nullptr) + { + ExsSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); + if (ExsSerial->begin(9600)) + { + if (ExsSerial->hardwareSerial()) + { + ClaimSerial(); + } + ExsSerial->flush(); + EsxMcuStart(); + ExsSendCmd(EXS_CH_LOCK, 0); + ExsSendCmd(EXS_GET_VALUES, 0); + } + } +} + +void ExsSerialInput(void) +{ + while (ExsSerial->available()) + { + yield(); + uint8_t serial_in_byte = ExsSerial->read(); + + AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Serial In Byte 0x%02x"), serial_in_byte); + + if (Exs.cmd_status == 0 && + serial_in_byte == 0x7B) + { + Exs.cmd_status = 1; + Exs.byte_counter = 0; + } + else if (Exs.byte_counter >= EXS_BUFFER_SIZE) + { + Exs.cmd_status = 0; + } + else if (Exs.cmd_status == 1) + { + Exs.buffer[Exs.byte_counter++] = serial_in_byte; + + if (Exs.byte_counter > 2 && Exs.byte_counter == Exs.buffer[1] + 2) + { + uint8_t crc = crc8(&Exs.buffer[2], Exs.buffer[1]); + + + Exs.cmd_status = 0; + +#ifdef EXS_DEBUG + snprintf_P(log_data, sizeof(log_data), PSTR("EXS: RX Packet: \"")); + for (uint32_t i = 0; i < Exs.byte_counter; i++) + { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, Exs.buffer[i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s\", CRC: 0x%02x"), log_data, crc); + AddLog(LOG_LEVEL_DEBUG_MORE); +#endif + + if (Exs.buffer[0] == crc) + { + ExsSerial->write(0xFF); + ExsPacketProcess(); + } + else + { + ExsSerial->write(0x00); + } + + } + } + } +} + + + + + +#ifdef EXS_MCU_CMNDS + +#define D_PRFX_EXS "Exs" +#define D_CMND_EXS_DIMM "Dimm" +#define D_CMND_EXS_DIMM_TBL "DimmTbl" +#define D_CMND_EXS_DIMM_VAL "DimmVal" +#define D_CMND_EXS_DIMMS "Dimms" +#define D_CMND_EXS_CH_LOCK "ChLock" +#define D_CMND_EXS_STATE "State" + +const char kExsCommands[] PROGMEM = D_PRFX_EXS "|" + D_CMND_EXS_DIMM "|" D_CMND_EXS_DIMM_TBL "|" D_CMND_EXS_DIMM_VAL "|" + D_CMND_EXS_DIMMS "|" D_CMND_EXS_CH_LOCK "|" + D_CMND_EXS_STATE; + +void (* const ExsCommand[])(void) PROGMEM = { + &CmndExsDimm, &CmndExsDimmTbl, &CmndExsDimmVal, + &CmndExsDimms, &CmndExsChLock, + &CmndExsState }; + +void CmndExsDimm(void) +{ + if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && + (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1)) { + ExsSendCmd(EXS_DIMM_1_ON + 0x10 * (XdrvMailbox.index - 1) + + XdrvMailbox.payload ^ 1, 0); + } + CmndExsState(); +} + +void CmndExsDimmTbl(void) +{ + if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && + (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) { + ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * (XdrvMailbox.index - 1), + XdrvMailbox.payload); + } + CmndExsState(); +} + +void CmndExsDimmVal(void) +{ + if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && + (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) { + ExsSendCmd(EXS_DIMM_1_VAL + 0x10 * (XdrvMailbox.index - 1), + XdrvMailbox.payload); + } + CmndExsState(); +} + +void CmndExsDimms(void) +{ + if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) { + ExsSendCmd(EXS_DIMMS_ON + XdrvMailbox.payload ^ 1, 0); + } + CmndExsState(); +} + +void CmndExsChLock(void) +{ + if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) { + ExsSendCmd(EXS_CH_LOCK, XdrvMailbox.payload); + } + CmndExsState(); +} + +void CmndExsState(void) +{ + ExsSendCmd(EXS_GET_VALUES, 0); + + + uint32_t snd_time = millis(); + while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) && + (!ExsSerial->available())) + ; + ExsSerialInput(); + + Response_P(PSTR("{\"" D_CMND_EXS_STATE "\":{")); + ResponseAppend_P(PSTR("\"McuVersion\":\"%d.%d\"," + "\"Channels\":["), + Exs.dimmer.version_major, Exs.dimmer.version_minor); + + for (uint32_t i = 0; i < 2; i++) { + if (i != 0) { + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("{\"On\":\"%d\"," + "\"BrightProz\":\"%d\"," + "\"BrightTab\":\"%d\"," + "\"Dimm\":\"%d\"}"), + Exs.dimmer.channel[i].on, + changeUIntScale(Exs.dimmer.channel[i].bright_tbl, 0, 255, 0, 100), + Exs.dimmer.channel[i].bright_tbl, + Exs.dimmer.channel[i].dimm); + } + ResponseAppend_P(PSTR("],")); + ResponseAppend_P(PSTR("\"GateLock\":\"%d\""), Exs.dimmer.gate_lock); + ResponseJsonEndEnd(); +} + +#endif + + + + + +bool Xdrv30(uint8_t function) +{ + bool result = false; + + if (EXS_DIMMER == my_module_type) + { + switch (function) + { + case FUNC_LOOP: + if (ExsSerial) + ExsSerialInput(); + break; + case FUNC_MODULE_INIT: + result = ExsModuleSelected(); + break; + case FUNC_INIT: + ExsInit(); + break; + case FUNC_SET_DEVICE_POWER: + result = ExsSetPower(); + break; + case FUNC_SET_CHANNELS: + result = ExsSetChannels(); + break; +#ifdef EXS_MCU_CMNDS + case FUNC_COMMAND: + result = DecodeCommand(kExsCommands, ExsCommand); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_31_tasmota_slave.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_31_tasmota_slave.ino" +#ifdef USE_TASMOTA_SLAVE + + + + +#define XDRV_31 31 + +#define CONST_STK_CRC_EOP 0x20 + +#define CMND_STK_GET_SYNC 0x30 +#define CMND_STK_SET_DEVICE 0x42 +#define CMND_STK_SET_DEVICE_EXT 0x45 +#define CMND_STK_ENTER_PROGMODE 0x50 +#define CMND_STK_LEAVE_PROGMODE 0x51 +#define CMND_STK_LOAD_ADDRESS 0x55 +#define CMND_STK_PROG_PAGE 0x64 + + + + + +#define CMND_START 0xFC +#define CMND_END 0xFD + +#define CMND_FEATURES 0x01 +#define CMND_JSON 0x02 +#define CMND_FUNC_EVERY_SECOND 0x03 +#define CMND_FUNC_EVERY_100_MSECOND 0x04 +#define CMND_SLAVE_SEND 0x05 +#define CMND_PUBLISH_TELE 0x06 +#define CMND_EXECUTE_CMND 0x07 + +#define PARAM_DATA_START 0xFE +#define PARAM_DATA_END 0xFF + +#include + + + + + +class SimpleHexParse { + public: + SimpleHexParse(void); + uint8_t parseLine(char *hexline); + uint8_t ptr_l = 0; + uint8_t ptr_h = 0; + bool PageIsReady = false; + bool firstrun = true; + bool EndOfFile = false; + uint8_t FlashPage[128]; + uint8_t FlashPageIdx = 0; + uint8_t layoverBuffer[16]; + uint8_t layoverIdx = 0; + uint8_t getByte(char *hexline, uint8_t idx); +}; + +SimpleHexParse::SimpleHexParse(void) +{ + +} + +uint8_t SimpleHexParse::parseLine(char *hexline) +{ + if (layoverIdx) { + memcpy(&FlashPage[0], &layoverBuffer[0], layoverIdx); + FlashPageIdx = layoverIdx; + layoverIdx = 0; + } + uint8_t len = getByte(hexline, 1); + uint8_t addr_h = getByte(hexline, 2); + uint8_t addr_l = getByte(hexline, 3); + uint8_t rectype = getByte(hexline, 4); + for (uint8_t idx = 0; idx < len; idx++) { + if (FlashPageIdx < 128) { + FlashPage[FlashPageIdx] = getByte(hexline, idx+5); + FlashPageIdx++; + } else { + layoverBuffer[layoverIdx] = getByte(hexline, idx+5); + layoverIdx++; + } + } + if (1 == rectype) { + EndOfFile = true; + while (FlashPageIdx < 128) { + FlashPage[FlashPageIdx] = 0xFF; + FlashPageIdx++; + } + } + if (FlashPageIdx == 128) { + if (firstrun) { + firstrun = false; + } else { + ptr_l += 0x40; + if (ptr_l == 0) { + ptr_l = 0; + ptr_h++; + } + } + firstrun = false; + PageIsReady = true; + } + return 0; +} + +uint8_t SimpleHexParse::getByte(char* hexline, uint8_t idx) +{ + char buff[3]; + buff[3] = '\0'; + memcpy(&buff, &hexline[(idx*2)-1], 2); + return strtol(buff, 0, 16); +} + + + + + +struct TSLAVE { + uint32_t spi_hex_size = 0; + uint32_t spi_sector_counter = 0; + uint8_t spi_sector_cursor = 0; + uint8_t inverted = LOW; + bool type = false; + bool flashing = false; + bool SerialEnabled = false; + uint8_t waitstate = 0; + bool unsupported = false; +} TSlave; + +typedef union { + uint32_t data; + struct { + uint32_t func_json_append : 1; + uint32_t func_every_second : 1; + uint32_t func_every_100_msecond : 1; + uint32_t func_slave_send : 1; + uint32_t spare4 : 1; + uint32_t spare5 : 1; + uint32_t spare6 : 1; + uint32_t spare7 : 1; + uint32_t spare8 : 1; + uint32_t spare9 : 1; + uint32_t spare10 : 1; + uint32_t spare11 : 1; + uint32_t spare12 : 1; + uint32_t spare13 : 1; + uint32_t spare14 : 1; + uint32_t spare15 : 1; + uint32_t spare16 : 1; + uint32_t spare17 : 1; + uint32_t spare18 : 1; + uint32_t spare19 : 1; + uint32_t spare20 : 1; + uint32_t spare21 : 1; + uint32_t spare22 : 1; + uint32_t spare23 : 1; + uint32_t spare24 : 1; + uint32_t spare25 : 1; + uint32_t spare26 : 1; + uint32_t spare27 : 1; + uint32_t spare28 : 1; + uint32_t spare29 : 1; + uint32_t spare30 : 1; + uint32_t spare31 : 1; + }; +} TSlaveFeatureCfg; + + + + + + +struct TSLAVE_FEATURES { + uint32_t features_version; + TSlaveFeatureCfg features; +} TSlaveSettings; + +struct TSLAVE_COMMAND { + uint8_t command; + uint8_t parameter; + uint8_t unused2; + uint8_t unused3; +} TSlaveCommand; + +TasmotaSerial *TasmotaSlave_Serial; + +uint32_t TasmotaSlave_FlashStart(void) +{ + return (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 2; +} + +uint8_t TasmotaSlave_UpdateInit(void) +{ + TSlave.spi_hex_size = 0; + TSlave.spi_sector_counter = TasmotaSlave_FlashStart(); + TSlave.spi_sector_cursor = 0; + return 0; +} + +void TasmotaSlave_Reset(void) +{ + if (TSlave.SerialEnabled) { + digitalWrite(pin[GPIO_TASMOTASLAVE_RST], !TSlave.inverted); + delay(1); + digitalWrite(pin[GPIO_TASMOTASLAVE_RST], TSlave.inverted); + delay(1); + digitalWrite(pin[GPIO_TASMOTASLAVE_RST], !TSlave.inverted); + delay(5); + } +} + +uint8_t TasmotaSlave_waitForSerialData(int dataCount, int timeout) +{ + int timer = 0; + while (timer < timeout) { + if (TasmotaSlave_Serial->available() >= dataCount) { + return 1; + } + delay(1); + timer++; + } + return 0; +} + +uint8_t TasmotaSlave_sendBytes(uint8_t* bytes, int count) +{ + TasmotaSlave_Serial->write(bytes, count); + TasmotaSlave_waitForSerialData(2, 250); + uint8_t sync = TasmotaSlave_Serial->read(); + uint8_t ok = TasmotaSlave_Serial->read(); + if ((sync == 0x14) && (ok == 0x10)) { + return 1; + } + return 0; +} + +uint8_t TasmotaSlave_execCmd(uint8_t cmd) +{ + uint8_t bytes[] = { cmd, CONST_STK_CRC_EOP }; + return TasmotaSlave_sendBytes(bytes, 2); +} + +uint8_t TasmotaSlave_execParam(uint8_t cmd, uint8_t* params, int count) +{ + uint8_t bytes[32]; + bytes[0] = cmd; + int i = 0; + while (i < count) { + bytes[i + 1] = params[i]; + i++; + } + bytes[i + 1] = CONST_STK_CRC_EOP; + return TasmotaSlave_sendBytes(bytes, i + 2); +} + +uint8_t TasmotaSlave_exitProgMode(void) +{ + return TasmotaSlave_execCmd(CMND_STK_LEAVE_PROGMODE); +} + +uint8_t TasmotaSlave_SetupFlash(void) +{ + uint8_t ProgParams[] = {0x86, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00}; + uint8_t ExtProgParams[] = {0x05, 0x04, 0xd7, 0xc2, 0x00}; + TasmotaSlave_Serial->begin(USE_TASMOTA_SLAVE_FLASH_SPEED); + if (TasmotaSlave_Serial->hardwareSerial()) { + ClaimSerial(); + } + + TasmotaSlave_Reset(); + + uint8_t timeout = 0; + uint8_t no_error = 0; + while (50 > timeout) { + if (TasmotaSlave_execCmd(CMND_STK_GET_SYNC)) { + timeout = 200; + no_error = 1; + } + timeout++; + delay(1); + } + if (no_error) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Found bootloader")); + } else { + no_error = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Bootloader could not be found")); + } + if (no_error) { + if (TasmotaSlave_execParam(CMND_STK_SET_DEVICE, ProgParams, sizeof(ProgParams))) { + } else { + no_error = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Could not configure device for programming (1)")); + } + } + if (no_error) { + if (TasmotaSlave_execParam(CMND_STK_SET_DEVICE_EXT, ExtProgParams, sizeof(ExtProgParams))) { + } else { + no_error = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Could not configure device for programming (2)")); + } + } + if (no_error) { + if (TasmotaSlave_execCmd(CMND_STK_ENTER_PROGMODE)) { + } else { + no_error = 0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Failed to put bootloader into programming mode")); + } + } + return no_error; +} + +uint8_t TasmotaSlave_loadAddress(uint8_t adrHi, uint8_t adrLo) +{ + uint8_t params[] = { adrLo, adrHi }; + return TasmotaSlave_execParam(CMND_STK_LOAD_ADDRESS, params, sizeof(params)); +} + +void TasmotaSlave_FlashPage(uint8_t addr_h, uint8_t addr_l, uint8_t* data) +{ + uint8_t Header[] = {CMND_STK_PROG_PAGE, 0x00, 0x80, 0x46}; + TasmotaSlave_loadAddress(addr_h, addr_l); + TasmotaSlave_Serial->write(Header, 4); + for (int i = 0; i < 128; i++) { + TasmotaSlave_Serial->write(data[i]); + } + TasmotaSlave_Serial->write(CONST_STK_CRC_EOP); + TasmotaSlave_waitForSerialData(2, 250); + TasmotaSlave_Serial->read(); + TasmotaSlave_Serial->read(); +} + +void TasmotaSlave_Flash(void) +{ + bool reading = true; + uint32_t read = 0; + uint32_t processed = 0; + char thishexline[50]; + uint8_t position = 0; + char* flash_buffer; + + SimpleHexParse hexParse = SimpleHexParse(); + + if (!TasmotaSlave_SetupFlash()) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Flashing aborted!")); + TSlave.flashing = false; + restart_flag = 2; + return; + } + + flash_buffer = new char[SPI_FLASH_SEC_SIZE]; + uint32_t flash_start = TasmotaSlave_FlashStart() * SPI_FLASH_SEC_SIZE; + while (reading) { + ESP.flashRead(flash_start + read, (uint32_t*)flash_buffer, SPI_FLASH_SEC_SIZE); + read = read + SPI_FLASH_SEC_SIZE; + if (read >= TSlave.spi_hex_size) { + reading = false; + } + for (uint32_t ca = 0; ca < SPI_FLASH_SEC_SIZE; ca++) { + processed++; + if ((processed <= TSlave.spi_hex_size) && (!hexParse.EndOfFile)) { + if (':' == flash_buffer[ca]) { + position = 0; + } + if (0x0D == flash_buffer[ca]) { + thishexline[position] = 0; + hexParse.parseLine(thishexline); + if (hexParse.PageIsReady) { + TasmotaSlave_FlashPage(hexParse.ptr_h, hexParse.ptr_l, hexParse.FlashPage); + hexParse.PageIsReady = false; + hexParse.FlashPageIdx = 0; + } + } else { + if (0x0A != flash_buffer[ca]) { + thishexline[position] = flash_buffer[ca]; + position++; + } + } + } + } + } + TasmotaSlave_exitProgMode(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Flash done!")); + TSlave.flashing = false; + restart_flag = 2; +} + +void TasmotaSlave_SetFlagFlashing(bool value) +{ + TSlave.flashing = value; +} + +bool TasmotaSlave_GetFlagFlashing(void) +{ + return TSlave.flashing; +} + +void TasmotaSlave_WriteBuffer(uint8_t *buf, size_t size) +{ + if (0 == TSlave.spi_sector_cursor) { + ESP.flashEraseSector(TSlave.spi_sector_counter); + } + TSlave.spi_sector_cursor++; + ESP.flashWrite((TSlave.spi_sector_counter * SPI_FLASH_SEC_SIZE) + ((TSlave.spi_sector_cursor-1)*2048), (uint32_t*)buf, size); + TSlave.spi_hex_size = TSlave.spi_hex_size + size; + if (2 == TSlave.spi_sector_cursor) { + TSlave.spi_sector_cursor = 0; + TSlave.spi_sector_counter++; + } +} + +void TasmotaSlave_Init(void) +{ + if (TSlave.type) { + return; + } + if (10 > TSlave.waitstate) { + TSlave.waitstate++; + return; + } + if (!TSlave.SerialEnabled) { + if ((pin[GPIO_TASMOTASLAVE_RXD] < 99) && (pin[GPIO_TASMOTASLAVE_TXD] < 99) && + ((pin[GPIO_TASMOTASLAVE_RST] < 99) || (pin[GPIO_TASMOTASLAVE_RST_INV] < 99))) { + TasmotaSlave_Serial = new TasmotaSerial(pin[GPIO_TASMOTASLAVE_RXD], pin[GPIO_TASMOTASLAVE_TXD], 1, 0, 200); + if (TasmotaSlave_Serial->begin(USE_TASMOTA_SLAVE_SERIAL_SPEED)) { + if (TasmotaSlave_Serial->hardwareSerial()) { + ClaimSerial(); + } + TasmotaSlave_Serial->setTimeout(50); + if (pin[GPIO_TASMOTASLAVE_RST_INV] < 99) { + pin[GPIO_TASMOTASLAVE_RST] = pin[GPIO_TASMOTASLAVE_RST_INV]; + pin[GPIO_TASMOTASLAVE_RST_INV] = 99; + TSlave.inverted = HIGH; + } + pinMode(pin[GPIO_TASMOTASLAVE_RST], OUTPUT); + TSlave.SerialEnabled = true; + TasmotaSlave_Reset(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Enabled")); + } + } + } + if (TSlave.SerialEnabled) { + TasmotaSlave_sendCmnd(CMND_FEATURES, 0); + char buffer[32]; + TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_START), buffer, sizeof(buffer)); + uint8_t len = TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_END), buffer, sizeof(buffer)); + memcpy(&TSlaveSettings, &buffer, sizeof(TSlaveSettings)); + if (20191129 == TSlaveSettings.features_version) { + TSlave.type = true; + AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Version %u"), TSlaveSettings.features_version); + } else { + if ((!TSlave.unsupported) && (TSlaveSettings.features_version > 0)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Version %u not supported!"), TSlaveSettings.features_version); + TSlave.unsupported = true; + } + } + } +} + +void TasmotaSlave_Show(void) +{ + if ((TSlave.type) && (TSlaveSettings.features.func_json_append)) { + char buffer[100]; + TasmotaSlave_sendCmnd(CMND_JSON, 0); + TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_START), buffer, sizeof(buffer)-1); + uint8_t len = TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_END), buffer, sizeof(buffer)-1); + buffer[len] = '\0'; + ResponseAppend_P(PSTR(",\"TasmotaSlave\":%s"), buffer); + } +} + +void TasmotaSlave_sendCmnd(uint8_t cmnd, uint8_t param) +{ + TSlaveCommand.command = cmnd; + TSlaveCommand.parameter = param; + char buffer[sizeof(TSlaveCommand)+2]; + buffer[0] = CMND_START; + memcpy(&buffer[1], &TSlaveCommand, sizeof(TSlaveCommand)); + buffer[sizeof(TSlaveCommand)+1] = CMND_END; + for (uint8_t ca = 0; ca < sizeof(buffer); ca++) { + TasmotaSlave_Serial->write(buffer[ca]); + } +} + +#define D_PRFX_SLAVE "Slave" +#define D_CMND_SLAVE_RESET "Reset" +#define D_CMND_SLAVE_SEND "Send" + +const char kTasmotaSlaveCommands[] PROGMEM = D_PRFX_SLAVE "|" + D_CMND_SLAVE_RESET "|" D_CMND_SLAVE_SEND; + +void (* const TasmotaSlaveCommand[])(void) PROGMEM = { + &CmndTasmotaSlaveReset, &CmndTasmotaSlaveSend }; + +void CmndTasmotaSlaveReset(void) +{ + TasmotaSlave_Reset(); + TSlave.type = false; + TSlave.waitstate = 7; + TSlave.unsupported = false; + ResponseCmndDone(); +} + +void CmndTasmotaSlaveSend(void) +{ + if (0 < XdrvMailbox.data_len) { + TasmotaSlave_sendCmnd(CMND_SLAVE_SEND, XdrvMailbox.data_len); + TasmotaSlave_Serial->write(char(PARAM_DATA_START)); + for (uint8_t idx = 0; idx < XdrvMailbox.data_len; idx++) { + TasmotaSlave_Serial->write(XdrvMailbox.data[idx]); + } + TasmotaSlave_Serial->write(char(PARAM_DATA_END)); + } + ResponseCmndDone(); +} + +void TasmotaSlave_ProcessIn(void) +{ + uint8_t cmnd = TasmotaSlave_Serial->read(); + switch (cmnd) { + case CMND_START: + TasmotaSlave_waitForSerialData(sizeof(TSlaveCommand),50); + uint8_t buffer[sizeof(TSlaveCommand)]; + for (uint8_t idx = 0; idx < sizeof(TSlaveCommand); idx++) { + buffer[idx] = TasmotaSlave_Serial->read(); + } + TasmotaSlave_Serial->read(); + memcpy(&TSlaveCommand, &buffer, sizeof(TSlaveCommand)); + char inbuf[TSlaveCommand.parameter+1]; + TasmotaSlave_waitForSerialData(TSlaveCommand.parameter, 50); + TasmotaSlave_Serial->read(); + for (uint8_t idx = 0; idx < TSlaveCommand.parameter; idx++) { + inbuf[idx] = TasmotaSlave_Serial->read(); + } + TasmotaSlave_Serial->read(); + inbuf[TSlaveCommand.parameter] = '\0'; + + if (CMND_PUBLISH_TELE == TSlaveCommand.command) { + Response_P(PSTR("{\"TasmotaSlave\":")); + ResponseAppend_P("%s", inbuf); + ResponseJsonEnd(); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); + XdrvRulesProcess(); + } + if (CMND_EXECUTE_CMND == TSlaveCommand.command) { + ExecuteCommand(inbuf, SRC_IGNORE); + } + break; + default: + break; + } +} + + + + + + +bool Xdrv31(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_100_MSECOND: + if (TSlave.type) { + if (TasmotaSlave_Serial->available()) { + TasmotaSlave_ProcessIn(); + } + if (TSlaveSettings.features.func_every_100_msecond) { + TasmotaSlave_sendCmnd(CMND_FUNC_EVERY_100_MSECOND, 0); + } + } + break; + case FUNC_EVERY_SECOND: + if ((TSlave.type) && (TSlaveSettings.features.func_every_second)) { + TasmotaSlave_sendCmnd(CMND_FUNC_EVERY_SECOND, 0); + } + TasmotaSlave_Init(); + break; + case FUNC_JSON_APPEND: + if ((TSlave.type) && (TSlaveSettings.features.func_json_append)) { + TasmotaSlave_Show(); + } + break; + case FUNC_COMMAND: + result = DecodeCommand(kTasmotaSlaveCommands, TasmotaSlaveCommand); + break; + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_32_hotplug.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_32_hotplug.ino" +#ifdef USE_HOTPLUG + + + + + + + +#define XDRV_32 32 + +const uint32_t HOTPLUG_MAX = 254; + +const char kHotPlugCommands[] PROGMEM = "|" + D_CMND_HOTPLUG; + +void (* const HotPlugCommand[])(void) PROGMEM = { + &CmndHotPlugTime }; + +struct { + + bool enabled = false; + uint8_t timeout = 0; +} Hotplug; + +void HotPlugInit(void) +{ + + if (Settings.hotplug_scan == 0xFF) { Settings.hotplug_scan = 0; } + if (Settings.hotplug_scan != 0) { + Hotplug.enabled = true; + Hotplug.timeout = 1; + } else + Hotplug.enabled = false; +} + +void HotPlugEverySecond(void) +{ + if (Hotplug.enabled) { + if (Hotplug.timeout == 0) { + XsnsCall(FUNC_HOTPLUG_SCAN); + Hotplug.timeout = Settings.hotplug_scan; + } + Hotplug.timeout--; + } +} + + + + + +void CmndHotPlugTime(void) +{ + if (XdrvMailbox.payload <= HOTPLUG_MAX) { + Settings.hotplug_scan = XdrvMailbox.payload; + HotPlugInit(); + } + ResponseCmndNumber(Settings.hotplug_scan); +} + + + + + +bool Xdrv32(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_SECOND: + HotPlugEverySecond(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kHotPlugCommands, HotPlugCommand); + break; + case FUNC_PRE_INIT: + HotPlugInit(); + break; + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_33_nrf24l01.ino" +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_33_nrf24l01.ino" +#ifdef USE_SPI +#ifdef USE_NRF24 + + + + + + + +#define XDRV_33 33 + +#define MOSI 13 +#define MISO 12 +#define SCK 14 + +#include +#include + +const char NRF24type[] PROGMEM = "NRF24"; + +const char HTTP_NRF24[] PROGMEM = + "{s}%sL01%c: " "{m}started{e}"; + +struct { + uint8_t chipType = 0; +} NRF24; + + + +RF24 NRF24radio; + +bool NRF24initRadio() +{ + NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]); + NRF24radio.powerUp(); + + if(NRF24radio.isChipConnected()){ + DEBUG_DRIVER_LOG(PSTR("NRF24 chip connected")); + return true; + } + DEBUG_DRIVER_LOG(PSTR("NRF24 chip NOT !!!! connected")); + return false; +} + +bool NRF24Detect(void) +{ + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_DC]<99)){ + SPI.pins(SCK,MOSI,MISO,-1); + if(NRF24initRadio()){ + NRF24.chipType = 32; + AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF24L01 initialized")); + if(NRF24radio.isPVariant()){ + NRF24.chipType = 43; + AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF24L01+ detected")); + } + return true; + } + } + return false; +} + + + + + +bool Xdrv33(uint8_t function) +{ + bool result = false; + + if (FUNC_INIT == function) { + result = NRF24Detect(); + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_99_debug.ino" +# 22 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_99_debug.ino" +#ifdef DEBUG_THEO +#ifndef USE_DEBUG_DRIVER +#define USE_DEBUG_DRIVER +#endif +#endif + +#ifdef USE_DEBUG_DRIVER + + + + + + +#define XDRV_99 99 + +#ifndef CPU_LOAD_CHECK +#define CPU_LOAD_CHECK 1 +#endif + + + + + +#define D_CMND_CFGDUMP "CfgDump" +#define D_CMND_CFGPEEK "CfgPeek" +#define D_CMND_CFGPOKE "CfgPoke" +#define D_CMND_CFGSHOW "CfgShow" +#define D_CMND_CFGXOR "CfgXor" +#define D_CMND_CPUCHECK "CpuChk" +#define D_CMND_EXCEPTION "Exception" +#define D_CMND_FLASHDUMP "FlashDump" +#define D_CMND_FLASHMODE "FlashMode" +#define D_CMND_FREEMEM "FreeMem" +#define D_CMND_HELP "Help" +#define D_CMND_RTCDUMP "RtcDump" +#define D_CMND_SETSENSOR "SetSensor" +#define D_CMND_I2CWRITE "I2CWrite" +#define D_CMND_I2CREAD "I2CRead" +#define D_CMND_I2CSTRETCH "I2CStretch" +#define D_CMND_I2CCLOCK "I2CClock" + +const char kDebugCommands[] PROGMEM = "|" + D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" +#ifdef USE_WEBSERVER + D_CMND_CFGXOR "|" +#endif + D_CMND_CPUCHECK "|" +#ifdef DEBUG_THEO + D_CMND_EXCEPTION "|" +#endif + D_CMND_FLASHDUMP "|" D_CMND_FLASHMODE "|" D_CMND_FREEMEM"|" D_CMND_HELP "|" D_CMND_RTCDUMP "|" D_CMND_SETSENSOR "|" +#ifdef USE_I2C + D_CMND_I2CWRITE "|" D_CMND_I2CREAD "|" D_CMND_I2CSTRETCH "|" D_CMND_I2CCLOCK +#endif + ; + +void (* const DebugCommand[])(void) PROGMEM = { + &CmndCfgDump, &CmndCfgPeek, &CmndCfgPoke, +#ifdef USE_WEBSERVER + &CmndCfgXor, +#endif + &CmndCpuCheck, +#ifdef DEBUG_THEO + &CmndException, +#endif + &CmndFlashDump, &CmndFlashMode, &CmndFreemem, &CmndHelp, &CmndRtcDump, &CmndSetSensor, +#ifdef USE_I2C + &CmndI2cWrite, &CmndI2cRead, &CmndI2cStretch, &CmndI2cClock +#endif + }; + +uint32_t CPU_loops = 0; +uint32_t CPU_last_millis = 0; +uint32_t CPU_last_loop_time = 0; +uint8_t CPU_load_check = 0; +uint8_t CPU_show_freemem = 0; + + + +#ifdef DEBUG_THEO +void ExceptionTest(uint8_t type) +{ +# 145 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_99_debug.ino" + if (1 == type) { + char svalue[10]; + snprintf_P(svalue, sizeof(svalue), PSTR("%s"), 7); + } +# 159 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_99_debug.ino" + if (2 == type) { + while(1) delay(1000); + } +} + +#endif + + + +void CpuLoadLoop(void) +{ + CPU_last_loop_time = millis(); + if (CPU_load_check && CPU_last_millis) { + CPU_loops ++; + if ((CPU_last_millis + (CPU_load_check *1000)) <= CPU_last_loop_time) { +#if defined(F_CPU) && (F_CPU == 160000000L) + int CPU_load = 100 - ( (CPU_loops*(1 + 30*sleep)) / (CPU_load_check *800) ); + CPU_loops = CPU_loops / CPU_load_check; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(160MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); +#else + int CPU_load = 100 - ( (CPU_loops*(1 + 30*sleep)) / (CPU_load_check *400) ); + CPU_loops = CPU_loops / CPU_load_check; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(80MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); +#endif + CPU_last_millis = CPU_last_loop_time; + CPU_loops = 0; + } + } +} + + + +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) + + + +extern "C" { +#include + extern cont_t g_cont; +} + +void DebugFreeMem(void) +{ + register uint32_t *sp asm("a1"); + + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_cont.stack), XdrvMailbox.data); +} + +#else + + + + +extern "C" { +#include + extern cont_t* g_pcont; +} + +void DebugFreeMem(void) +{ + register uint32_t *sp asm("a1"); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_pcont->stack), XdrvMailbox.data); +} + +#endif + + + +void DebugRtcDump(char* parms) +{ + #define CFG_COLS 16 + + uint16_t idx; + uint16_t maxrow; + uint16_t row; + uint16_t col; + char *p; +# 246 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_99_debug.ino" + uint8_t buffer[768]; + + system_rtc_mem_read(0, (uint32_t*)&buffer, sizeof(buffer)); + + maxrow = ((sizeof(buffer)+CFG_COLS)/CFG_COLS); + + uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; + uint16_t mrow = strtol(p, &p, 10); + + + + if (0 == mrow) { + mrow = 8; + } + if (srow > maxrow) { + srow = maxrow - mrow; + } + if (mrow < (maxrow - srow)) { + maxrow = srow + mrow; + } + + for (row = srow; row < maxrow; row++) { + idx = row * CFG_COLS; + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); + for (col = 0; col < CFG_COLS; col++) { + if (!(col%4)) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (col = 0; col < CFG_COLS; col++) { + + + + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); + AddLog(LOG_LEVEL_INFO); + } +} + + + +void DebugCfgDump(char* parms) +{ + #define CFG_COLS 16 + + uint16_t idx; + uint16_t maxrow; + uint16_t row; + uint16_t col; + char *p; + + uint8_t *buffer = (uint8_t *) &Settings; + maxrow = ((sizeof(SYSCFG)+CFG_COLS)/CFG_COLS); + + uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; + uint16_t mrow = strtol(p, &p, 10); + + + + if (0 == mrow) { + mrow = 8; + } + if (srow > maxrow) { + srow = maxrow - mrow; + } + if (mrow < (maxrow - srow)) { + maxrow = srow + mrow; + } + + for (row = srow; row < maxrow; row++) { + idx = row * CFG_COLS; + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); + for (col = 0; col < CFG_COLS; col++) { + if (!(col%4)) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (col = 0; col < CFG_COLS; col++) { + + + + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); + AddLog(LOG_LEVEL_INFO); + delay(1); + } +} + +void DebugCfgPeek(char* parms) +{ + char *p; + + uint16_t address = strtol(parms, &p, 16); + if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4; + address = (address >> 2) << 2; + + uint8_t *buffer = (uint8_t *) &Settings; + uint8_t data8 = buffer[address]; + uint16_t data16 = (buffer[address +1] << 8) + buffer[address]; + uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + data16; + + snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), address); + for (uint32_t i = 0; i < 4; i++) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[address +i]); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); + for (uint32_t i = 0; i < 4; i++) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[address +i] > 0x20) && (buffer[address +i] < 0x7F)) ? (char)buffer[address +i] : ' '); + } + snprintf_P(log_data, sizeof(log_data), PSTR("%s| 0x%02X (%d), 0x%04X (%d), 0x%0LX (%lu)"), log_data, data8, data8, data16, data16, data32, data32); + AddLog(LOG_LEVEL_INFO); +} + +void DebugCfgPoke(char* parms) +{ + char *p; + + uint16_t address = strtol(parms, &p, 16); + if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4; + address = (address >> 2) << 2; + + uint32_t data = strtol(p, &p, 16); + + uint8_t *buffer = (uint8_t *) &Settings; + uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; + + uint8_t *nbuffer = (uint8_t *) &data; + for (uint32_t i = 0; i < 4; i++) { buffer[address +i] = nbuffer[+i]; } + + uint32_t ndata32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; + + AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: 0x%0LX (%lu) poked to 0x%0LX (%lu)"), address, data32, data32, ndata32, ndata32); +} + +void SetFlashMode(uint8_t mode) +{ + uint8_t *_buffer; + uint32_t address; + + address = 0; + _buffer = new uint8_t[FLASH_SECTOR_SIZE]; + + if (ESP.flashRead(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) { + if (_buffer[2] != mode) { + _buffer[2] = mode; + if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) { + ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); + } + } + } + delete[] _buffer; +} + + + + + +void CmndHelp(void) +{ + AddLog_P(LOG_LEVEL_INFO, PSTR("HLP: "), kDebugCommands); + ResponseCmndDone(); +} + +void CmndRtcDump(void) +{ + DebugRtcDump(XdrvMailbox.data); + ResponseCmndDone(); +} + +void CmndCfgDump(void) +{ + DebugCfgDump(XdrvMailbox.data); + ResponseCmndDone(); +} + +void CmndCfgPeek(void) +{ + DebugCfgPeek(XdrvMailbox.data); + ResponseCmndDone(); +} + +void CmndCfgPoke(void) +{ + DebugCfgPoke(XdrvMailbox.data); + ResponseCmndDone(); +} + +#ifdef USE_WEBSERVER +void CmndCfgXor(void) +{ + if (XdrvMailbox.data_len > 0) { + Web.config_xor_on_set = XdrvMailbox.payload; + } + ResponseCmndNumber(Web.config_xor_on_set); +} +#endif + +#ifdef DEBUG_THEO +void CmndException(void) +{ + if (XdrvMailbox.data_len > 0) { ExceptionTest(XdrvMailbox.payload); } + ResponseCmndDone(); +} +#endif + +void CmndCpuCheck(void) +{ + if (XdrvMailbox.data_len > 0) { + CPU_load_check = XdrvMailbox.payload; + CPU_last_millis = CPU_last_loop_time; + } + ResponseCmndNumber(CPU_load_check); +} + +void CmndFreemem(void) +{ + if (XdrvMailbox.data_len > 0) { + CPU_show_freemem = XdrvMailbox.payload; + } + ResponseCmndNumber(CPU_show_freemem); +} + +void CmndSetSensor(void) +{ + if (XdrvMailbox.index < MAX_XSNS_DRIVERS) { + if (XdrvMailbox.payload >= 0) { + bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); + if (1 == XdrvMailbox.payload) { + restart_flag = 2; + } + } + Response_P(PSTR("{\"" D_CMND_SETSENSOR "\":")); + XsnsSensorState(); + ResponseJsonEnd(); + } +} + +void CmndFlashMode(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { + SetFlashMode(XdrvMailbox.payload); + } + ResponseCmndNumber(ESP.getFlashChipMode()); +} + +uint32_t DebugSwap32(uint32_t x) { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} + +void CmndFlashDump(void) +{ + + + + const uint32_t flash_start = 0x40200000; + const uint8_t bytes_per_cols = 0x20; + const uint32_t max = (SPIFFS_END + 5) * SPI_FLASH_SEC_SIZE; + + uint32_t start = flash_start; + uint32_t rows = 8; + + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= (max - bytes_per_cols))) { + start += (XdrvMailbox.payload &0x7FFFFFFC); + + char *p; + uint32_t is_payload = strtol(XdrvMailbox.data, &p, 16); + rows = strtol(p, &p, 10); + if (0 == rows) { rows = 8; } + } + uint32_t end = start + (rows * bytes_per_cols); + if ((end - flash_start) > max) { + end = flash_start + max; + } + + for (uint32_t pos = start; pos < end; pos += bytes_per_cols) { + uint32_t* values = (uint32_t*)(pos); + AddLog_P2(LOG_LEVEL_INFO, PSTR("%06X: %08X %08X %08X %08X %08X %08X %08X %08X"), pos - flash_start, + DebugSwap32(values[0]), DebugSwap32(values[1]), DebugSwap32(values[2]), DebugSwap32(values[3]), + DebugSwap32(values[4]), DebugSwap32(values[5]), DebugSwap32(values[6]), DebugSwap32(values[7])); + } + ResponseCmndDone(); +} + +#ifdef USE_I2C +void CmndI2cWrite(void) +{ + + if (i2c_flg) { + char* parms = XdrvMailbox.data; + uint8_t buffer[100]; + uint32_t index = 0; + + char *p; + char *data = strtok_r(parms, " ,", &p); + while (data != NULL && index < sizeof(buffer)) { + buffer[index++] = strtol(data, nullptr, 16); + data = strtok_r(nullptr, " ,", &p); + } + + if (index > 1) { + AddLogBuffer(LOG_LEVEL_INFO, buffer, index); + + Wire.beginTransmission(buffer[0]); + for (uint32_t i = 1; i < index; i++) { + Wire.write(buffer[i]); + } + int result = Wire.endTransmission(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("I2C: Result %d"), result); + } + } + ResponseCmndDone(); +} + +void CmndI2cRead(void) +{ + + if (i2c_flg) { + char* parms = XdrvMailbox.data; + uint8_t buffer[100]; + uint32_t index = 0; + + char *p; + char *data = strtok_r(parms, " ,", &p); + while (data != NULL && index < sizeof(buffer)) { + buffer[index++] = strtol(data, nullptr, 16); + data = strtok_r(nullptr, " ,", &p); + } + + if (index > 0) { + uint8_t size = 1; + if (index > 1) { + size = buffer[1]; + } + Wire.requestFrom(buffer[0], size); + index = 0; + while (Wire.available() && index < sizeof(buffer)) { + buffer[index++] = Wire.read(); + } + if (index > 0) { + AddLogBuffer(LOG_LEVEL_INFO, buffer, index); + } + } + } + ResponseCmndDone(); +} + +void CmndI2cStretch(void) +{ + if (i2c_flg && (XdrvMailbox.payload > 0)) { + Wire.setClockStretchLimit(XdrvMailbox.payload); + } + ResponseCmndDone(); +} + +void CmndI2cClock(void) +{ + if (i2c_flg && (XdrvMailbox.payload > 0)) { + Wire.setClock(XdrvMailbox.payload); + } + ResponseCmndDone(); +} +#endif + + + + + +bool Xdrv99(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_LOOP: + CpuLoadLoop(); + break; + case FUNC_FREE_MEM: + if (CPU_show_freemem) { DebugFreeMem(); } + break; + case FUNC_PRE_INIT: + CPU_last_millis = millis(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kDebugCommands, DebugCommand); + break; + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_interface.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_interface.ino" +#ifdef XFUNC_PTR_IN_ROM +bool (* const xdrv_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xdrv_func_ptr[])(uint8_t) = { +#endif + +#ifdef XDRV_01 + &Xdrv01, +#endif + +#ifdef XDRV_02 + &Xdrv02, +#endif + +#ifdef XDRV_03 + &Xdrv03, +#endif + +#ifdef XDRV_04 + &Xdrv04, +#endif + +#ifdef XDRV_05 + &Xdrv05, +#endif + +#ifdef XDRV_06 + &Xdrv06, +#endif + +#ifdef XDRV_07 + &Xdrv07, +#endif + +#ifdef XDRV_08 + &Xdrv08, +#endif + +#ifdef XDRV_09 + &Xdrv09, +#endif + +#ifdef XDRV_10 + &Xdrv10, +#endif + +#ifdef XDRV_11 + &Xdrv11, +#endif + +#ifdef XDRV_12 + &Xdrv12, +#endif + +#ifdef XDRV_13 + &Xdrv13, +#endif + +#ifdef XDRV_14 + &Xdrv14, +#endif + +#ifdef XDRV_15 + &Xdrv15, +#endif + +#ifdef XDRV_16 + &Xdrv16, +#endif + +#ifdef XDRV_17 + &Xdrv17, +#endif + +#ifdef XDRV_18 + &Xdrv18, +#endif + +#ifdef XDRV_19 + &Xdrv19, +#endif + +#ifdef XDRV_20 + &Xdrv20, +#endif + +#ifdef XDRV_21 + &Xdrv21, +#endif + +#ifdef XDRV_22 + &Xdrv22, +#endif + +#ifdef XDRV_23 + &Xdrv23, +#endif + +#ifdef XDRV_24 + &Xdrv24, +#endif + +#ifdef XDRV_25 + &Xdrv25, +#endif + +#ifdef XDRV_26 + &Xdrv26, +#endif + +#ifdef XDRV_27 + &Xdrv27, +#endif + +#ifdef XDRV_28 + &Xdrv28, +#endif + +#ifdef XDRV_29 + &Xdrv29, +#endif + +#ifdef XDRV_30 + &Xdrv30, +#endif + +#ifdef XDRV_31 + &Xdrv31, +#endif + +#ifdef XDRV_32 + &Xdrv32, +#endif + +#ifdef XDRV_33 + &Xdrv33, +#endif + +#ifdef XDRV_34 + &Xdrv34, +#endif + +#ifdef XDRV_35 + &Xdrv35, +#endif + +#ifdef XDRV_36 + &Xdrv36, +#endif + +#ifdef XDRV_37 + &Xdrv37, +#endif + +#ifdef XDRV_38 + &Xdrv38, +#endif + +#ifdef XDRV_39 + &Xdrv39, +#endif + +#ifdef XDRV_40 + &Xdrv40, +#endif + +#ifdef XDRV_41 + &Xdrv41, +#endif + +#ifdef XDRV_42 + &Xdrv42, +#endif + +#ifdef XDRV_43 + &Xdrv43, +#endif + +#ifdef XDRV_44 + &Xdrv44, +#endif + +#ifdef XDRV_45 + &Xdrv45, +#endif + +#ifdef XDRV_46 + &Xdrv46, +#endif + +#ifdef XDRV_47 + &Xdrv47, +#endif + +#ifdef XDRV_48 + &Xdrv48, +#endif + +#ifdef XDRV_49 + &Xdrv49, +#endif + +#ifdef XDRV_50 + &Xdrv50, +#endif + +#ifdef XDRV_51 + &Xdrv51, +#endif + +#ifdef XDRV_52 + &Xdrv52, +#endif + +#ifdef XDRV_53 + &Xdrv53, +#endif + +#ifdef XDRV_54 + &Xdrv54, +#endif + +#ifdef XDRV_55 + &Xdrv55, +#endif + +#ifdef XDRV_56 + &Xdrv56, +#endif + +#ifdef XDRV_57 + &Xdrv57, +#endif + +#ifdef XDRV_58 + &Xdrv58, +#endif + +#ifdef XDRV_59 + &Xdrv59, +#endif + +#ifdef XDRV_60 + &Xdrv60, +#endif + +#ifdef XDRV_61 + &Xdrv61, +#endif + +#ifdef XDRV_62 + &Xdrv62, +#endif + +#ifdef XDRV_63 + &Xdrv63, +#endif + +#ifdef XDRV_64 + &Xdrv64, +#endif + +#ifdef XDRV_65 + &Xdrv65, +#endif + +#ifdef XDRV_66 + &Xdrv66, +#endif + +#ifdef XDRV_67 + &Xdrv67, +#endif + +#ifdef XDRV_68 + &Xdrv68, +#endif + +#ifdef XDRV_69 + &Xdrv69, +#endif + +#ifdef XDRV_70 + &Xdrv70, +#endif + +#ifdef XDRV_71 + &Xdrv71, +#endif + +#ifdef XDRV_72 + &Xdrv72, +#endif + +#ifdef XDRV_73 + &Xdrv73, +#endif + +#ifdef XDRV_74 + &Xdrv74, +#endif + +#ifdef XDRV_75 + &Xdrv75, +#endif + +#ifdef XDRV_76 + &Xdrv76, +#endif + +#ifdef XDRV_77 + &Xdrv77, +#endif + +#ifdef XDRV_78 + &Xdrv78, +#endif + +#ifdef XDRV_79 + &Xdrv79, +#endif + +#ifdef XDRV_80 + &Xdrv80, +#endif + +#ifdef XDRV_81 + &Xdrv81, +#endif + +#ifdef XDRV_82 + &Xdrv82, +#endif + +#ifdef XDRV_83 + &Xdrv83, +#endif + +#ifdef XDRV_84 + &Xdrv84, +#endif + +#ifdef XDRV_85 + &Xdrv85, +#endif + +#ifdef XDRV_86 + &Xdrv86, +#endif + +#ifdef XDRV_87 + &Xdrv87, +#endif + +#ifdef XDRV_88 + &Xdrv88, +#endif + +#ifdef XDRV_89 + &Xdrv89, +#endif + +#ifdef XDRV_90 + &Xdrv90, +#endif + +#ifdef XDRV_91 + &Xdrv91, +#endif + +#ifdef XDRV_92 + &Xdrv92, +#endif + +#ifdef XDRV_93 + &Xdrv93, +#endif + +#ifdef XDRV_94 + &Xdrv94, +#endif + +#ifdef XDRV_95 + &Xdrv95, +#endif + +#ifdef XDRV_96 + &Xdrv96, +#endif + +#ifdef XDRV_97 + &Xdrv97, +#endif + +#ifdef XDRV_98 + &Xdrv98, +#endif + +#ifdef XDRV_99 + &Xdrv99 +#endif +}; + +const uint8_t xdrv_present = sizeof(xdrv_func_ptr) / sizeof(xdrv_func_ptr[0]); + + + + + +#ifdef XFUNC_PTR_IN_ROM +const uint8_t kXdrvList[] PROGMEM = { +#else +const uint8_t kXdrvList[] = { +#endif + +#ifdef XDRV_01 + XDRV_01, +#endif + +#ifdef XDRV_02 + XDRV_02, +#endif + +#ifdef XDRV_03 + XDRV_03, +#endif + +#ifdef XDRV_04 + XDRV_04, +#endif + +#ifdef XDRV_05 + XDRV_05, +#endif + +#ifdef XDRV_06 + XDRV_06, +#endif + +#ifdef XDRV_07 + XDRV_07, +#endif + +#ifdef XDRV_08 + XDRV_08, +#endif + +#ifdef XDRV_09 + XDRV_09, +#endif + +#ifdef XDRV_10 + XDRV_10, +#endif + +#ifdef XDRV_11 + XDRV_11, +#endif + +#ifdef XDRV_12 + XDRV_12, +#endif + +#ifdef XDRV_13 + XDRV_13, +#endif + +#ifdef XDRV_14 + XDRV_14, +#endif + +#ifdef XDRV_15 + XDRV_15, +#endif + +#ifdef XDRV_16 + XDRV_16, +#endif + +#ifdef XDRV_17 + XDRV_17, +#endif + +#ifdef XDRV_18 + XDRV_18, +#endif + +#ifdef XDRV_19 + XDRV_19, +#endif + +#ifdef XDRV_20 + XDRV_20, +#endif + +#ifdef XDRV_21 + XDRV_21, +#endif + +#ifdef XDRV_22 + XDRV_22, +#endif + +#ifdef XDRV_23 + XDRV_23, +#endif + +#ifdef XDRV_24 + XDRV_24, +#endif + +#ifdef XDRV_25 + XDRV_25, +#endif + +#ifdef XDRV_26 + XDRV_26, +#endif + +#ifdef XDRV_27 + XDRV_27, +#endif + +#ifdef XDRV_28 + XDRV_28, +#endif + +#ifdef XDRV_29 + XDRV_29, +#endif + +#ifdef XDRV_30 + XDRV_30, +#endif + +#ifdef XDRV_31 + XDRV_31, +#endif + +#ifdef XDRV_32 + XDRV_32, +#endif + +#ifdef XDRV_33 + XDRV_33, +#endif + +#ifdef XDRV_34 + XDRV_34, +#endif + +#ifdef XDRV_35 + XDRV_35, +#endif + +#ifdef XDRV_36 + XDRV_36, +#endif + +#ifdef XDRV_37 + XDRV_37, +#endif + +#ifdef XDRV_38 + XDRV_38, +#endif + +#ifdef XDRV_39 + XDRV_39, +#endif + +#ifdef XDRV_40 + XDRV_40, +#endif + +#ifdef XDRV_41 + XDRV_41, +#endif + +#ifdef XDRV_42 + XDRV_42, +#endif + +#ifdef XDRV_43 + XDRV_43, +#endif + +#ifdef XDRV_44 + XDRV_44, +#endif + +#ifdef XDRV_45 + XDRV_45, +#endif + +#ifdef XDRV_46 + XDRV_46, +#endif + +#ifdef XDRV_47 + XDRV_47, +#endif + +#ifdef XDRV_48 + XDRV_48, +#endif + +#ifdef XDRV_49 + XDRV_49, +#endif + +#ifdef XDRV_50 + XDRV_50, +#endif + +#ifdef XDRV_51 + XDRV_51, +#endif + +#ifdef XDRV_52 + XDRV_52, +#endif + +#ifdef XDRV_53 + XDRV_53, +#endif + +#ifdef XDRV_54 + XDRV_54, +#endif + +#ifdef XDRV_55 + XDRV_55, +#endif + +#ifdef XDRV_56 + XDRV_56, +#endif + +#ifdef XDRV_57 + XDRV_57, +#endif + +#ifdef XDRV_58 + XDRV_58, +#endif + +#ifdef XDRV_59 + XDRV_59, +#endif + +#ifdef XDRV_60 + XDRV_60, +#endif + +#ifdef XDRV_61 + XDRV_61, +#endif + +#ifdef XDRV_62 + XDRV_62, +#endif + +#ifdef XDRV_63 + XDRV_63, +#endif + +#ifdef XDRV_64 + XDRV_64, +#endif + +#ifdef XDRV_65 + XDRV_65, +#endif + +#ifdef XDRV_66 + XDRV_66, +#endif + +#ifdef XDRV_67 + XDRV_67, +#endif + +#ifdef XDRV_68 + XDRV_68, +#endif + +#ifdef XDRV_69 + XDRV_69, +#endif + +#ifdef XDRV_70 + XDRV_70, +#endif + +#ifdef XDRV_71 + XDRV_71, +#endif + +#ifdef XDRV_72 + XDRV_72, +#endif + +#ifdef XDRV_73 + XDRV_73, +#endif + +#ifdef XDRV_74 + XDRV_74, +#endif + +#ifdef XDRV_75 + XDRV_75, +#endif + +#ifdef XDRV_76 + XDRV_76, +#endif + +#ifdef XDRV_77 + XDRV_77, +#endif + +#ifdef XDRV_78 + XDRV_78, +#endif + +#ifdef XDRV_79 + XDRV_79, +#endif + +#ifdef XDRV_80 + XDRV_80, +#endif + +#ifdef XDRV_81 + XDRV_81, +#endif + +#ifdef XDRV_82 + XDRV_82, +#endif + +#ifdef XDRV_83 + XDRV_83, +#endif + +#ifdef XDRV_84 + XDRV_84, +#endif + +#ifdef XDRV_85 + XDRV_85, +#endif + +#ifdef XDRV_86 + XDRV_86, +#endif + +#ifdef XDRV_87 + XDRV_87, +#endif + +#ifdef XDRV_88 + XDRV_88, +#endif + +#ifdef XDRV_89 + XDRV_89, +#endif + +#ifdef XDRV_90 + XDRV_90, +#endif + +#ifdef XDRV_91 + XDRV_91, +#endif + +#ifdef XDRV_92 + XDRV_92, +#endif + +#ifdef XDRV_93 + XDRV_93, +#endif + +#ifdef XDRV_94 + XDRV_94, +#endif + +#ifdef XDRV_95 + XDRV_95, +#endif + +#ifdef XDRV_96 + XDRV_96, +#endif + +#ifdef XDRV_97 + XDRV_97, +#endif + +#ifdef XDRV_98 + XDRV_98, +#endif + +#ifdef XDRV_99 + XDRV_99 +#endif +}; + + + +void XsnsDriverState(void) +{ + ResponseAppend_P(PSTR(",\"Drivers\":\"")); + for (uint32_t i = 0; i < sizeof(kXdrvList); i++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t driverid = pgm_read_byte(kXdrvList + i); +#else + uint32_t driverid = kXdrvList[i]; +#endif + ResponseAppend_P(PSTR("%s%d"), (i) ? "," : "", driverid); + } + ResponseAppend_P(PSTR("\"")); +} + + + +bool XdrvRulesProcess(void) +{ + return XdrvCallDriver(10, FUNC_RULES_PROCESS); +} + +#ifdef USE_DEBUG_DRIVER +void ShowFreeMem(const char *where) +{ + char stemp[30]; + snprintf_P(stemp, sizeof(stemp), where); + XdrvMailbox.data = stemp; + XdrvCall(FUNC_FREE_MEM); +} +#endif + + + + + +bool XdrvCallDriver(uint32_t driver, uint8_t Function) +{ + for (uint32_t x = 0; x < xdrv_present; x++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t listed = pgm_read_byte(kXdrvList + x); +#else + uint32_t listed = kXdrvList[x]; +#endif + if (driver == listed) { + return xdrv_func_ptr[x](Function); + } + } + return false; +} + + + + + +bool XdrvCall(uint8_t Function) +{ + bool result = false; + + DEBUG_TRACE_LOG(PSTR("DRV: %d"), Function); + + for (uint32_t x = 0; x < xdrv_present; x++) { + result = xdrv_func_ptr[x](Function); + + if (result && ((FUNC_COMMAND == Function) || + (FUNC_COMMAND_DRIVER == Function) || + (FUNC_MQTT_DATA == Function) || + (FUNC_RULES_PROCESS == Function) || + (FUNC_BUTTON_PRESSED == Function) || + (FUNC_SERIAL == Function) || + (FUNC_MODULE_INIT == Function) || + (FUNC_SET_CHANNELS == Function) || + (FUNC_PIN_STATE == Function) || + (FUNC_SET_DEVICE_POWER == Function) + )) { + break; + } + } + + return result; +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_01_lcd.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_01_lcd.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_LCD + +#define XDSP_01 1 +#define XI2C_03 3 + +#define LCD_ADDRESS1 0x27 +#define LCD_ADDRESS2 0x3F + +#include +#include + +LiquidCrystal_I2C *lcd; + + + +void LcdInitMode(void) +{ + lcd->init(); + lcd->clear(); +} + +void LcdInit(uint8_t mode) +{ + switch(mode) { + case DISPLAY_INIT_MODE: + LcdInitMode(); +#ifdef USE_DISPLAY_MODES1TO5 + DisplayClearScreenBuffer(); +#endif + break; + case DISPLAY_INIT_PARTIAL: + case DISPLAY_INIT_FULL: + break; + } +} + +void LcdInitDriver(void) +{ + if (!Settings.display_model) { + if (I2cSetDevice(LCD_ADDRESS1)) { + Settings.display_address[0] = LCD_ADDRESS1; + Settings.display_model = XDSP_01; + } + else if (I2cSetDevice(LCD_ADDRESS2)) { + Settings.display_address[0] = LCD_ADDRESS2; + Settings.display_model = XDSP_01; + } + } + + if (XDSP_01 == Settings.display_model) { + I2cSetActiveFound(Settings.display_address[0], "LCD"); + + Settings.display_width = Settings.display_cols[0]; + Settings.display_height = Settings.display_rows; + lcd = new LiquidCrystal_I2C(Settings.display_address[0], Settings.display_cols[0], Settings.display_rows); + +#ifdef USE_DISPLAY_MODES1TO5 + DisplayAllocScreenBuffer(); +#endif + + LcdInitMode(); + } +} + +void LcdDrawStringAt(void) +{ + if (dsp_flag) { + dsp_x--; + dsp_y--; + } + lcd->setCursor(dsp_x, dsp_y); + lcd->print(dsp_str); +} + +void LcdDisplayOnOff(uint8_t on) +{ + if (on) { + lcd->backlight(); + } else { + lcd->noBacklight(); + } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void LcdCenter(uint8_t row, char* txt) +{ + char line[Settings.display_cols[0] +2]; + + int len = strlen(txt); + int offset = 0; + if (len >= Settings.display_cols[0]) { + len = Settings.display_cols[0]; + } else { + offset = (Settings.display_cols[0] - len) / 2; + } + memset(line, 0x20, Settings.display_cols[0]); + line[Settings.display_cols[0]] = 0; + for (uint32_t i = 0; i < len; i++) { + line[offset +i] = txt[i]; + } + lcd->setCursor(0, row); + lcd->print(line); +} + +bool LcdPrintLog(void) +{ + bool result = false; + + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\337'); + if (txt != nullptr) { + uint8_t last_row = Settings.display_rows -1; + + for (uint32_t i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + lcd->setCursor(0, i); + lcd->print(disp_screen_buffer[i +1]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + lcd->setCursor(0, last_row); + lcd->print(disp_screen_buffer[last_row]); + + result = true; + } + } + return result; +} + +void LcdTime(void) +{ + char line[Settings.display_cols[0] +1]; + + snprintf_P(line, sizeof(line), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + LcdCenter(0, line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + LcdCenter(1, line); +} + +void LcdRefresh(void) +{ + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + LcdTime(); + break; + case 2: + case 4: + LcdPrintLog(); + break; + case 3: + case 5: { + if (!LcdPrintLog()) { LcdTime(); } + break; + } + } + } +} + +#endif + + + + + +bool Xdsp01(uint8_t function) +{ + if (!I2cEnabled(XI2C_03)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + LcdInitDriver(); + } + else if (XDSP_01 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_INIT: + LcdInit(dsp_init); + break; + case FUNC_DISPLAY_POWER: + LcdDisplayOnOff(disp_power); + break; + case FUNC_DISPLAY_CLEAR: + lcd->clear(); + break; +# 238 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_01_lcd.ino" + case FUNC_DISPLAY_DRAW_STRING: + LcdDrawStringAt(); + break; + case FUNC_DISPLAY_ONOFF: + LcdDisplayOnOff(dsp_on); + break; + + +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + LcdRefresh(); + break; +#endif + } + } + return result; +} + +#endif +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_02_ssd1306.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_02_ssd1306.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SSD1306 + +#define XDSP_02 2 +#define XI2C_04 4 + +#define OLED_RESET 4 + +#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); + +#define OLED_ADDRESS1 0x3C +#define OLED_ADDRESS2 0x3D + +#define OLED_BUFFER_COLS 40 +#define OLED_BUFFER_ROWS 16 + +#define OLED_FONT_WIDTH 6 +#define OLED_FONT_HEIGTH 8 + +#include +#include +#include + +Adafruit_SSD1306 *oled1306; + +extern uint8_t *buffer; + + + +void SSD1306InitDriver(void) +{ + if (!Settings.display_model) { + if (I2cSetDevice(OLED_ADDRESS1)) { + Settings.display_address[0] = OLED_ADDRESS1; + Settings.display_model = XDSP_02; + } + else if (I2cSetDevice(OLED_ADDRESS2)) { + Settings.display_address[0] = OLED_ADDRESS2; + Settings.display_model = XDSP_02; + } + } + + if (XDSP_02 == Settings.display_model) { + I2cSetActiveFound(Settings.display_address[0], "SSD1306"); + + if ((Settings.display_width != 64) && (Settings.display_width != 96) && (Settings.display_width != 128)) { + Settings.display_width = 128; + } + if ((Settings.display_height != 16) && (Settings.display_height != 32) && (Settings.display_height != 48) && (Settings.display_height != 64)) { + Settings.display_height = 64; + } + + uint8_t reset_pin = -1; + if (pin[GPIO_OLED_RESET] < 99) { + reset_pin = pin[GPIO_OLED_RESET]; + } + + + if (buffer) { free(buffer); } + buffer = (unsigned char*)calloc((Settings.display_width * Settings.display_height) / 8,1); + if (!buffer) { return; } + + + + oled1306 = new Adafruit_SSD1306(Settings.display_width, Settings.display_height, &Wire, reset_pin); + oled1306->begin(SSD1306_SWITCHCAPVCC, Settings.display_address[0], reset_pin >= 0); + renderer = oled1306; + renderer->DisplayInit(DISPLAY_INIT_MODE, Settings.display_size, Settings.display_rotate, Settings.display_font); + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + renderer->setTextFont(0); + renderer->setTextSize(2); + renderer->setCursor(20,20); + renderer->println(F("SSD1306")); + renderer->Updateframe(); + renderer->DisplayOnff(1); +#endif + + } +} + + +#ifdef USE_DISPLAY_MODES1TO5 + +void Ssd1306PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\370'); + if (txt != NULL) { + uint8_t last_row = Settings.display_rows -1; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->println(disp_screen_buffer[i]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); + } + } +} + +void Ssd1306Time(void) +{ + char line[12]; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setTextFont(Settings.display_font); + renderer->setCursor(0, 0); + snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + renderer->println(line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + renderer->println(line); + renderer->Updateframe(); +} + +void Ssd1306Refresh(void) +{ + if (!renderer) return; + + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + Ssd1306Time(); + break; + case 2: + case 3: + case 4: + case 5: + Ssd1306PrintLog(); + break; + } + } +} + +#endif + + + + + +bool Xdsp02(byte function) +{ + if (!I2cEnabled(XI2C_04)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SSD1306InitDriver(); + } + else if (XDSP_02 == Settings.display_model) { + switch (function) { +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + Ssd1306Refresh(); + break; +#endif + case FUNC_DISPLAY_MODEL: + result = true; + break; + } + } + return result; +} + +#endif +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_03_matrix.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_03_matrix.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_MATRIX + +#define XDSP_03 3 +#define XI2C_05 5 + +#define MTX_MAX_SCREEN_BUFFER 80 + +#include +#include +#include + +Adafruit_8x8matrix *matrix[8]; +uint8_t mtx_matrices = 0; +uint8_t mtx_state = 0; +uint8_t mtx_counter = 0; +int16_t mtx_x = 0; +int16_t mtx_y = 0; + + +char *mtx_buffer = nullptr; + +uint8_t mtx_mode = 0; +uint8_t mtx_loop = 0; +uint8_t mtx_done = 0; + + + +void MatrixWrite(void) +{ + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->writeDisplay(); + } +} + +void MatrixClear(void) +{ + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + } + MatrixWrite(); +} + +void MatrixFixed(char* txt) +{ + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + matrix[i]->setCursor(-i *8, 0); + matrix[i]->print(txt); + matrix[i]->setBrightness(Settings.display_dimmer); + } + MatrixWrite(); +} + +void MatrixCenter(char* txt) +{ + int offset; + + int len = strlen(txt); + offset = (len < 8) ? offset = ((mtx_matrices *8) - (len *6)) / 2 : 0; + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + matrix[i]->setCursor(-(i *8)+offset, 0); + matrix[i]->print(txt); + matrix[i]->setBrightness(Settings.display_dimmer); + } + MatrixWrite(); +} + +void MatrixScrollLeft(char* txt, int loop) +{ + switch (mtx_state) { + case 1: + mtx_state = 2; + + mtx_x = 8 * mtx_matrices; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), txt); + + disp_refresh = Settings.display_refresh; + case 2: + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + matrix[i]->setCursor(mtx_x - i *8, 0); + matrix[i]->print(txt); + matrix[i]->setBrightness(Settings.display_dimmer); + } + MatrixWrite(); + + mtx_x--; + int16_t len = strlen(txt); + if (mtx_x < -(len *6)) { mtx_state = loop; } + } + break; + } +} + +void MatrixScrollUp(char* txt, int loop) +{ + int wordcounter = 0; + char tmpbuf[200]; + char *words[100]; + + + + char separators[] = " /"; + + switch (mtx_state) { + case 1: + mtx_state = 2; + + mtx_y = 8; + mtx_counter = 0; + disp_refresh = Settings.display_refresh; + case 2: + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + strlcpy(tmpbuf, txt, sizeof(tmpbuf)); + char *p = strtok(tmpbuf, separators); + while (p != nullptr && wordcounter < 40) { + words[wordcounter++] = p; + p = strtok(nullptr, separators); + } + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->clear(); + for (uint32_t j = 0; j < wordcounter; j++) { + matrix[i]->setCursor(-i *8, mtx_y + (j *8)); + matrix[i]->println(words[j]); + } + matrix[i]->setBrightness(Settings.display_dimmer); + } + MatrixWrite(); + if (((mtx_y %8) == 0) && mtx_counter) { + mtx_counter--; + } else { + mtx_y--; + mtx_counter = STATES * 1; + } + if (mtx_y < -(wordcounter *8)) { mtx_state = loop; } + } + break; + } +} + + + +void MatrixInitMode(void) +{ + for (uint32_t i = 0; i < mtx_matrices; i++) { + matrix[i]->setRotation(Settings.display_rotate); + matrix[i]->setBrightness(Settings.display_dimmer); + matrix[i]->blinkRate(0); + matrix[i]->setTextWrap(false); + + + matrix[i]->cp437(true); + } + MatrixClear(); +} + +void MatrixInit(uint8_t mode) +{ + switch(mode) { + case DISPLAY_INIT_MODE: + MatrixInitMode(); + break; + case DISPLAY_INIT_PARTIAL: + case DISPLAY_INIT_FULL: + break; + } +} + +void MatrixInitDriver(void) +{ + mtx_buffer = (char*)(malloc(MTX_MAX_SCREEN_BUFFER)); + if (mtx_buffer != nullptr) { + if (!Settings.display_model) { + if (I2cSetDevice(Settings.display_address[1])) { + Settings.display_model = XDSP_03; + } + } + + if (XDSP_03 == Settings.display_model) { + mtx_state = 1; + for (mtx_matrices = 0; mtx_matrices < 8; mtx_matrices++) { + if (Settings.display_address[mtx_matrices]) { + I2cSetActiveFound(Settings.display_address[mtx_matrices], "8x8Matrix"); + matrix[mtx_matrices] = new Adafruit_8x8matrix(); + matrix[mtx_matrices]->begin(Settings.display_address[mtx_matrices]); + } else { + break; + } + } + + Settings.display_width = mtx_matrices * 8; + Settings.display_height = 8; + + MatrixInitMode(); + } + } +} + +void MatrixOnOff(void) +{ + if (!disp_power) { MatrixClear(); } +} + +void MatrixDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) +{ + strlcpy(mtx_buffer, str, MTX_MAX_SCREEN_BUFFER); + mtx_mode = x &1; + mtx_loop = y &1; + if (!mtx_state) { mtx_state = 1; } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void MatrixPrintLog(uint8_t direction) +{ + char* txt = (!mtx_done) ? DisplayLogBuffer('\370') : mtx_buffer; + if (txt != nullptr) { + if (!mtx_state) { mtx_state = 1; } + + if (!mtx_done) { + + uint8_t space = 0; + uint8_t max_cols = (disp_log_buffer_cols < MTX_MAX_SCREEN_BUFFER) ? disp_log_buffer_cols : MTX_MAX_SCREEN_BUFFER; + mtx_buffer[0] = '\0'; + uint8_t i = 0; + while ((txt[i] != '\0') && (i < max_cols)) { + if (txt[i] == ' ') { + space++; + } else { + space = 0; + } + if (space < 2) { + strncat(mtx_buffer, (const char*)txt +i, (strlen(mtx_buffer) < MTX_MAX_SCREEN_BUFFER -1) ? 1 : 0); + } + i++; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), mtx_buffer); + + mtx_done = 1; + } + + if (direction) { + MatrixScrollUp(mtx_buffer, 0); + } else { + MatrixScrollLeft(mtx_buffer, 0); + } + if (!mtx_state) { mtx_done = 0; } + } else { + char disp_time[9]; + + snprintf_P(disp_time, sizeof(disp_time), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + MatrixFixed(disp_time); + } +} + +#endif + +void MatrixRefresh(void) +{ + if (disp_power) { + switch (Settings.display_mode) { + case 0: { + switch (mtx_mode) { + case 0: + MatrixScrollLeft(mtx_buffer, mtx_loop); + break; + case 1: + MatrixScrollUp(mtx_buffer, mtx_loop); + break; + } + break; + } +#ifdef USE_DISPLAY_MODES1TO5 + case 2: { + char disp_date[9]; + snprintf_P(disp_date, sizeof(disp_date), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year -2000); + MatrixFixed(disp_date); + break; + } + case 3: { + char disp_day[10]; + snprintf_P(disp_day, sizeof(disp_day), PSTR("%d %s"), RtcTime.day_of_month, RtcTime.name_of_month); + MatrixCenter(disp_day); + break; + } + case 4: + MatrixPrintLog(0); + break; + case 1: + case 5: + MatrixPrintLog(1); + break; +#endif + } + } +} + + + + + +bool Xdsp03(uint8_t function) +{ + if (!I2cEnabled(XI2C_05)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + MatrixInitDriver(); + } + else if (XDSP_03 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_INIT: + MatrixInit(dsp_init); + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: + MatrixRefresh(); + break; + case FUNC_DISPLAY_POWER: + MatrixOnOff(); + break; + case FUNC_DISPLAY_DRAW_STRING: + MatrixDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); + break; + } + } + return result; +} + +#endif +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_04_ili9341.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_04_ili9341.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_ILI9341 + +#define XDSP_04 4 + +#define TFT_TOP 16 +#define TFT_BOTTOM 16 +#define TFT_FONT_WIDTH 6 +#define TFT_FONT_HEIGTH 8 + +#include +#include +#include + +Adafruit_ILI9341 *tft; + +uint16_t tft_scroll; + + + +void Ili9341InitMode(void) +{ + tft->setRotation(Settings.display_rotate); + tft->invertDisplay(0); + tft->fillScreen(ILI9341_BLACK); + tft->setTextWrap(false); + tft->cp437(true); + if (!Settings.display_mode) { + tft->setCursor(0, 0); + tft->setTextColor(ILI9341_WHITE, ILI9341_BLACK); + tft->setTextSize(1); + } else { + tft->setScrollMargins(TFT_TOP, TFT_BOTTOM); + tft->setCursor(0, 0); + tft->setTextColor(ILI9341_YELLOW, ILI9341_BLACK); + tft->setTextSize(2); + + + tft_scroll = TFT_TOP; + } +} + +void Ili9341Init(uint8_t mode) +{ + switch(mode) { + case DISPLAY_INIT_MODE: + Ili9341InitMode(); +#ifdef USE_DISPLAY_MODES1TO5 + if (Settings.display_rotate) { + DisplayClearScreenBuffer(); + } +#endif + break; + case DISPLAY_INIT_PARTIAL: + case DISPLAY_INIT_FULL: + break; + } +} + +void Ili9341InitDriver(void) +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_04; + } + + if (XDSP_04 == Settings.display_model) { + if (Settings.display_width != ILI9341_TFTWIDTH) { + Settings.display_width = ILI9341_TFTWIDTH; + } + if (Settings.display_height != ILI9341_TFTHEIGHT) { + Settings.display_height = ILI9341_TFTHEIGHT; + } + tft = new Adafruit_ILI9341(pin[GPIO_SPI_CS], pin[GPIO_SPI_DC]); + tft->begin(); + +#ifdef USE_DISPLAY_MODES1TO5 + if (Settings.display_rotate) { + DisplayAllocScreenBuffer(); + } +#endif + + Ili9341InitMode(); + } +} + +void Ili9341Clear(void) +{ + tft->fillScreen(ILI9341_BLACK); + tft->setCursor(0, 0); +} + +void Ili9341DrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) +{ + uint16_t active_color = ILI9341_WHITE; + + tft->setTextSize(Settings.display_size); + if (!flag) { + tft->setCursor(x, y); + } else { + tft->setCursor((x-1) * TFT_FONT_WIDTH * Settings.display_size, (y-1) * TFT_FONT_HEIGTH * Settings.display_size); + } + if (color) { active_color = color; } + tft->setTextColor(active_color, ILI9341_BLACK); + tft->println(str); +} + +void Ili9341DisplayOnOff(uint8_t on) +{ + + + if (pin[GPIO_BACKLIGHT] < 99) { + pinMode(pin[GPIO_BACKLIGHT], OUTPUT); + digitalWrite(pin[GPIO_BACKLIGHT], on); + } +} + +void Ili9341OnOff(void) +{ + Ili9341DisplayOnOff(disp_power); +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void Ili9341PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (Settings.display_rotate) { + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + } + + char* txt = DisplayLogBuffer('\370'); + if (txt != nullptr) { + uint8_t size = Settings.display_size; + uint16_t theight = size * TFT_FONT_HEIGTH; + + tft->setTextSize(size); + tft->setTextColor(ILI9341_CYAN, ILI9341_BLACK); + if (!Settings.display_rotate) { + tft->setCursor(0, tft_scroll); + tft->fillRect(0, tft_scroll, tft->width(), theight, ILI9341_BLACK); + tft->print(txt); + tft_scroll += theight; + if (tft_scroll >= (tft->height() - TFT_BOTTOM)) { + tft_scroll = TFT_TOP; + } + tft->scrollTo(tft_scroll); + } else { + uint8_t last_row = Settings.display_rows -1; + + tft_scroll = theight; + tft->setCursor(0, tft_scroll); + for (uint32_t i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + + tft->print(disp_screen_buffer[i]); + tft_scroll += theight; + tft->setCursor(0, tft_scroll); + delay(1); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + tft->print(disp_screen_buffer[last_row]); + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); + } + } +} + +void Ili9341Refresh(void) +{ + if (Settings.display_mode) { + char tftdt[Settings.display_cols[0] +1]; + char date4[11]; + char space[Settings.display_cols[0] - 17]; + char time[9]; + + tft->setTextSize(2); + tft->setTextColor(ILI9341_YELLOW, ILI9341_RED); + tft->setCursor(0, 0); + + snprintf_P(date4, sizeof(date4), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + memset(space, 0x20, sizeof(space)); + space[sizeof(space) -1] = '\0'; + snprintf_P(time, sizeof(time), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + snprintf_P(tftdt, sizeof(tftdt), PSTR("%s%s%s"), date4, space, time); + + tft->print(tftdt); + + switch (Settings.display_mode) { + case 1: + case 2: + case 3: + case 4: + case 5: + Ili9341PrintLog(); + break; + } + } +} + +#endif + + + + + +bool Xdsp04(uint8_t function) +{ + bool result = false; + + if (spi_flg) { + if (FUNC_DISPLAY_INIT_DRIVER == function) { + Ili9341InitDriver(); + } + else if (XDSP_04 == Settings.display_model) { + + if (!dsp_color) { dsp_color = ILI9341_WHITE; } + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_INIT: + Ili9341Init(dsp_init); + break; + case FUNC_DISPLAY_POWER: + Ili9341OnOff(); + break; + case FUNC_DISPLAY_CLEAR: + Ili9341Clear(); + break; + case FUNC_DISPLAY_DRAW_HLINE: + tft->writeFastHLine(dsp_x, dsp_y, dsp_len, dsp_color); + break; + case FUNC_DISPLAY_DRAW_VLINE: + tft->writeFastVLine(dsp_x, dsp_y, dsp_len, dsp_color); + break; + case FUNC_DISPLAY_DRAW_LINE: + tft->writeLine(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); + break; + case FUNC_DISPLAY_DRAW_CIRCLE: + tft->drawCircle(dsp_x, dsp_y, dsp_rad, dsp_color); + break; + case FUNC_DISPLAY_FILL_CIRCLE: + tft->fillCircle(dsp_x, dsp_y, dsp_rad, dsp_color); + break; + case FUNC_DISPLAY_DRAW_RECTANGLE: + tft->drawRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); + break; + case FUNC_DISPLAY_FILL_RECTANGLE: + tft->fillRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); + break; + + + + case FUNC_DISPLAY_TEXT_SIZE: + tft->setTextSize(Settings.display_size); + break; + case FUNC_DISPLAY_FONT_SIZE: + + break; + case FUNC_DISPLAY_DRAW_STRING: + Ili9341DrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); + break; + case FUNC_DISPLAY_ONOFF: + Ili9341DisplayOnOff(dsp_on); + break; + case FUNC_DISPLAY_ROTATION: + tft->setRotation(Settings.display_rotate); + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + Ili9341Refresh(); + break; +#endif + } + } + } + return result; +} + +#endif +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_05_epaper_29.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_05_epaper_29.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_EPAPER_29 + +#define XDSP_05 5 + +#define EPD_TOP 12 +#define EPD_FONT_HEIGTH 12 + +#define COLORED 1 +#define UNCOLORED 0 + + + +#define USE_TINY_FONT + +#include +#include + + +extern uint8_t *buffer; +uint16_t epd_scroll; + +Epd *epd; + + + +void EpdInitDriver29() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_05; + } + + if (XDSP_05 == Settings.display_model) { + if (Settings.display_width != EPD_WIDTH) { + Settings.display_width = EPD_WIDTH; + } + if (Settings.display_height != EPD_HEIGHT) { + Settings.display_height = EPD_HEIGHT; + } + + + if (buffer) free(buffer); + buffer=(unsigned char*)calloc((EPD_WIDTH * EPD_HEIGHT) / 8,1); + if (!buffer) return; + + + epd = new Epd(EPD_WIDTH,EPD_HEIGHT); + + + if ((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CLK] < 99) && (pin[GPIO_SPI_MOSI] < 99)) { + epd->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SPI_CS], pin[GPIO_SPI_CLK], pin[GPIO_SPI_MOSI]); + } + else if ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_MOSI] < 99)) { + epd->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SSPI_CS], pin[GPIO_SSPI_SCLK], pin[GPIO_SSPI_MOSI]); + } else { + free(buffer); + return; + } + + renderer = epd; + epd->Init(DISPLAY_INIT_FULL); + epd->Init(DISPLAY_INIT_PARTIAL); + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(1); + renderer->DrawStringAt(50, 50, "Waveshare E-Paper Display!", COLORED,0); + renderer->Updateframe(); + delay(1000); + renderer->fillScreen(0); +#endif + + } +} + + + + + + + +#ifdef USE_DISPLAY_MODES1TO5 +#define EPD_FONT_HEIGTH 12 +void EpdPrintLog29(void) +{ + + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + + char* txt = DisplayLogBuffer('\040'); + if (txt != nullptr) { + uint8_t size = Settings.display_size; + uint16_t theight = size * EPD_FONT_HEIGTH; + + renderer->setTextFont(size); + uint8_t last_row = Settings.display_rows -1; + + + epd_scroll = 0; + for (uint32_t i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[i], COLORED, 0); + epd_scroll += theight; + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[last_row], COLORED, 0); + + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); + } + } +} + +void EpdRefresh29(void) +{ + if (Settings.display_mode) { + + if (!renderer) return; +# 165 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_05_epaper_29.ino" + switch (Settings.display_mode) { + case 1: + case 2: + case 3: + case 4: + case 5: + EpdPrintLog29(); + renderer->Updateframe(); + break; + } + + + } +} + +#endif + + + + + +bool Xdsp05(uint8_t function) +{ + bool result = false; + if (FUNC_DISPLAY_INIT_DRIVER == function) { + EpdInitDriver29(); + } + else if (XDSP_05 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + EpdRefresh29(); + break; +#endif + } + } + return result; +} + +#endif +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_06_epaper_42.ino" +# 21 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_06_epaper_42.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_EPAPER_42 + +#define XDSP_06 6 + +#define COLORED42 1 +#define UNCOLORED42 0 + + + +#define USE_TINY_FONT + +#include +#include + +extern uint8_t *buffer; + +Epd42 *epd42; + + + + +void EpdInitDriver42() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_06; + } + + if (XDSP_06 == Settings.display_model) { + + if (Settings.display_width != EPD_WIDTH42) { + Settings.display_width = EPD_WIDTH42; + } + if (Settings.display_height != EPD_HEIGHT42) { + Settings.display_height = EPD_HEIGHT42; + } + + + if (buffer) free(buffer); + buffer=(unsigned char*)calloc((EPD_WIDTH42 * EPD_HEIGHT42) / 8,1); + if (!buffer) return; + + + epd42 = new Epd42(EPD_WIDTH42,EPD_HEIGHT42); + + #ifdef USE_SPI + if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)) { + epd42->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + } else { + free(buffer); + return; + } + #else + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { + epd42->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + } else { + free(buffer); + return; + } + #endif + + renderer = epd42; + + epd42->Init(); + + renderer->fillScreen(0); + + + epd42->Init(DISPLAY_INIT_FULL); + + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + + epd42->ClearFrame(); + renderer->Updateframe(); + delay(3000); + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->DrawStringAt(50, 140, "Waveshare E-Paper!", COLORED42,0); + renderer->Updateframe(); + delay(350); + renderer->fillScreen(0); +#endif + + } +} + + + + + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void EpdRefresh42() +{ + if (Settings.display_mode) { + + } +} + +#endif + + + + + + +bool Xdsp06(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + EpdInitDriver42(); + } + else if (XDSP_06 == Settings.display_model) { + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + EpdRefresh42(); + break; +#endif + } + } + return result; +} + + +#endif +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_07_sh1106.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_07_sh1106.ino" +#ifdef USE_I2C +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SH1106 + +#define OLED_RESET 4 + +#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); + +extern uint8_t *buffer; + +#define XDSP_07 7 +#define XI2C_06 6 + +#define OLED_ADDRESS1 0x3C +#define OLED_ADDRESS2 0x3D + +#define OLED_BUFFER_COLS 40 +#define OLED_BUFFER_ROWS 16 + +#define OLED_FONT_WIDTH 6 +#define OLED_FONT_HEIGTH 8 + +#include +#include +#include + +Adafruit_SH1106 *oled1106; + + + + +void SH1106InitDriver() +{ + if (!Settings.display_model) { + if (I2cSetDevice(OLED_ADDRESS1)) { + Settings.display_address[0] = OLED_ADDRESS1; + Settings.display_model = XDSP_07; + } + else if (I2cSetDevice(OLED_ADDRESS2)) { + Settings.display_address[0] = OLED_ADDRESS2; + Settings.display_model = XDSP_07; + } + } + + if (XDSP_07 == Settings.display_model) { + I2cSetActiveFound(Settings.display_address[0], "SH1106"); + + if (Settings.display_width != SH1106_LCDWIDTH) { + Settings.display_width = SH1106_LCDWIDTH; + } + if (Settings.display_height != SH1106_LCDHEIGHT) { + Settings.display_height = SH1106_LCDHEIGHT; + } + + + if (buffer) free(buffer); + buffer=(unsigned char*)calloc((SH1106_LCDWIDTH * SH1106_LCDHEIGHT) / 8,1); + if (!buffer) return; + + + oled1106 = new Adafruit_SH1106(SH1106_LCDWIDTH,SH1106_LCDHEIGHT); + renderer=oled1106; + renderer->Begin(SH1106_SWITCHCAPVCC, Settings.display_address[0],0); + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + renderer->setTextColor(1,0); + +#ifdef SHOW_SPLASH + renderer->setTextFont(0); + renderer->setTextSize(2); + renderer->setCursor(20,20); + renderer->println(F("SH1106")); + renderer->Updateframe(); + renderer->DisplayOnff(1); +#endif + } +} + + + +#ifdef USE_DISPLAY_MODES1TO5 + +void SH1106PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\370'); + if (txt != NULL) { + uint8_t last_row = Settings.display_rows -1; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->println(disp_screen_buffer[i]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); + } + } +} + +void SH1106Time(void) +{ + char line[12]; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setTextFont(Settings.display_font); + renderer->setCursor(0, 0); + snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + renderer->println(line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + renderer->println(line); + renderer->Updateframe(); +} + +void SH1106Refresh(void) +{ + if (!renderer) return; + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + SH1106Time(); + break; + case 2: + case 3: + case 4: + case 5: + SH1106PrintLog(); + break; + } + } +} + +#endif + + + + + +bool Xdsp07(uint8_t function) +{ + if (!I2cEnabled(XI2C_06)) { return false; } + + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SH1106InitDriver(); + } + else if (XDSP_07 == Settings.display_model) { + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + SH1106Refresh(); + break; +#endif + } + } + return result; +} + +#endif +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_08_ILI9488.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_08_ILI9488.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_ILI9488 + +#define XDSP_08 8 +#define XI2C_38 38 + +#define COLORED 1 +#define UNCOLORED 0 + + +#define FT6236_address 0x38 + + + +#define USE_TINY_FONT + + +#include +#include + +TouchLocation ili9488_pLoc; +uint8_t ili9488_ctouch_counter = 0; + + +#define BACKPLANE_PIN 2 + +extern uint8_t *buffer; +extern uint8_t color_type; +ILI9488 *ili9488; + +#ifdef USE_TOUCH_BUTTONS +extern VButton *buttons[]; +#endif + +extern const uint16_t picture[]; +uint8_t FT6236_found; + + + +void ILI9488_InitDriver() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_08; + } + + if (XDSP_08 == Settings.display_model) { + + if (Settings.display_width != ILI9488_TFTWIDTH) { + Settings.display_width = ILI9488_TFTWIDTH; + } + if (Settings.display_height != ILI9488_TFTHEIGHT) { + Settings.display_height = ILI9488_TFTHEIGHT; + } + + + buffer=NULL; + + + fg_color = ILI9488_WHITE; + bg_color = ILI9488_BLACK; + + uint8_t bppin=BACKPLANE_PIN; + if (pin[GPIO_BACKLIGHT]<99) { + bppin=pin[GPIO_BACKLIGHT]; + } + + + if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ + ili9488 = new ILI9488(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK],bppin); + } else { + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { + ili9488 = new ILI9488(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK],bppin); + } else { + return; + } + } + + SPI.begin(); + ili9488->begin(); + renderer = ili9488; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->setTextColor(ILI9488_WHITE,ILI9488_BLACK); + renderer->DrawStringAt(50, 50, "ILI9488 TFT Display!", ILI9488_WHITE,0); + delay(1000); + + +#endif + + color_type = COLOR_COLOR; + + + if (I2cEnabled(XI2C_38) && I2cSetDevice(FT6236_address)) { + FT6236begin(FT6236_address); + FT6236_found=1; + I2cSetActiveFound(FT6236_address, "FT6236"); + } else { + FT6236_found=0; + } + + } +} + +#ifdef USE_TOUCH_BUTTONS +void ILI9488_MQTT(uint8_t count,const char *cp) { + ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7); + MqttPublishTeleSensor(); +} + +void ILI9488_RDW_BUTT(uint32_t count,uint32_t pwr) { + buttons[count]->xdrawButton(pwr); + if (pwr) buttons[count]->vpower|=0x80; + else buttons[count]->vpower&=0x7f; +} + +void FT6236Check() { +uint16_t temp; +uint8_t rbutt=0,vbutt=0; +ili9488_ctouch_counter++; +if (2 == ili9488_ctouch_counter) { + + ili9488_ctouch_counter=0; + if (FT6236readTouchLocation(&ili9488_pLoc,1)) { + + if (renderer) { + uint8_t rot=renderer->getRotation(); + switch (rot) { + case 0: + temp=ili9488_pLoc.y; + ili9488_pLoc.y=renderer->height()-ili9488_pLoc.x; + ili9488_pLoc.x=temp; + break; + case 1: + break; + case 2: + break; + case 3: + temp=ili9488_pLoc.y; + ili9488_pLoc.y=ili9488_pLoc.x; + ili9488_pLoc.x=renderer->width()-temp; + break; + } + + for (uint8_t count=0; countvpower&0x7f; + if (buttons[count]->contains(ili9488_pLoc.x,ili9488_pLoc.y)) { + + buttons[count]->press(true); + if (buttons[count]->justPressed()) { + if (!bflags) { + uint8_t pwr=bitRead(power,rbutt); + if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) { + ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON); + ILI9488_RDW_BUTT(count,!pwr); + } + } else { + + const char *cp; + if (bflags==1) { + + buttons[count]->vpower^=0x80; + cp="TBT"; + } else { + + buttons[count]->vpower|=0x80; + cp="PBT"; + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + ILI9488_MQTT(count,cp); + } + } + } + if (!bflags) { + rbutt++; + } else { + vbutt++; + } + } + } + } + } else { + + for (uint8_t count=0; countvpower&0x7f; + buttons[count]->press(false); + if (buttons[count]->justReleased()) { + uint8_t bflags=buttons[count]->vpower&0x7f; + if (bflags>0) { + if (bflags>1) { + + buttons[count]->vpower&=0x7f; + ILI9488_MQTT(count,"PBT"); + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + } + } + if (!bflags) { + + uint8_t pwr=bitRead(power,rbutt); + uint8_t vpwr=(buttons[count]->vpower&0x80)>>7; + if (pwr!=vpwr) { + ILI9488_RDW_BUTT(count,pwr); + } + rbutt++; + } + } + } + ili9488_pLoc.x=0; + ili9488_pLoc.y=0; + } +} +} +#endif + + + + +bool Xdsp08(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + ILI9488_InitDriver(); + } + else if (XDSP_08 == Settings.display_model) { + + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: +#ifdef USE_TOUCH_BUTTONS + if (FT6236_found) FT6236Check(); +#endif + break; + } + } + + return result; +} + +#endif +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_09_SSD1351.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_09_SSD1351.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_SSD1351 + +#define XDSP_09 9 + +#define COLORED 1 +#define UNCOLORED 0 + + + + +#define USE_TINY_FONT + +#include + +extern uint8_t *buffer; +extern uint8_t color_type; +SSD1351 *ssd1351; + + + +void SSD1351_InitDriver() { + if (!Settings.display_model) { + Settings.display_model = XDSP_09; + } + + if (XDSP_09 == Settings.display_model) { + + if (Settings.display_width != SSD1351_WIDTH) { + Settings.display_width = SSD1351_WIDTH; + } + if (Settings.display_height != SSD1351_HEIGHT) { + Settings.display_height = SSD1351_HEIGHT; + } + + buffer=0; + + + fg_color = SSD1351_WHITE; + bg_color = SSD1351_BLACK; + + + if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ + ssd1351 = new SSD1351(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); + } else { + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)){ + ssd1351 = new SSD1351(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); + } else { + return; + } + } + + delay(100); + SPI.begin(); + ssd1351->begin(); + renderer = ssd1351; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + renderer->dim(Settings.display_dimmer); + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->setTextColor(SSD1351_WHITE,SSD1351_BLACK); + renderer->DrawStringAt(10, 60, "SSD1351", SSD1351_RED,0); + delay(1000); + +#endif + color_type = COLOR_COLOR; + } +} + +#ifdef USE_DISPLAY_MODES1TO5 + +void SSD1351PrintLog(void) +{ + disp_refresh--; + if (!disp_refresh) { + disp_refresh = Settings.display_refresh; + if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } + + char* txt = DisplayLogBuffer('\370'); + if (txt != NULL) { + uint8_t last_row = Settings.display_rows -1; + + renderer->clearDisplay(); + renderer->setTextSize(Settings.display_size); + renderer->setCursor(0,0); + for (byte i = 0; i < last_row; i++) { + strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); + renderer->println(disp_screen_buffer[i]); + } + strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); + DisplayFillScreen(last_row); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); + + renderer->println(disp_screen_buffer[last_row]); + renderer->Updateframe(); + } + } +} + +void SSD1351Time(void) +{ + char line[12]; + + renderer->clearDisplay(); + renderer->setTextSize(2); + renderer->setCursor(0, 0); + snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); + renderer->println(line); + snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); + renderer->println(line); + renderer->Updateframe(); +} + +void SSD1351Refresh(void) +{ + if (Settings.display_mode) { + switch (Settings.display_mode) { + case 1: + SSD1351Time(); + break; + case 2: + case 3: + case 4: + case 5: + SSD1351PrintLog(); + break; + } + } +} + +#endif + + + + +bool Xdsp09(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + SSD1351_InitDriver(); + } + else if (XDSP_09 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; +#ifdef USE_DISPLAY_MODES1TO5 + case FUNC_DISPLAY_EVERY_SECOND: + SSD1351Refresh(); + break; +#endif + } + } + return result; +} +#endif +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_10_RA8876.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_10_RA8876.ino" +#ifdef USE_SPI +#ifdef USE_DISPLAY +#ifdef USE_DISPLAY_RA8876 + +#define XDSP_10 10 +#define XI2C_39 39 + +#define COLORED 1 +#define UNCOLORED 0 + + +#define FT5316_address 0x38 + + + +#define USE_TINY_FONT + +#include +#include + +TouchLocation ra8876_pLoc; +uint8_t ra8876_ctouch_counter = 0; + +#ifdef USE_TOUCH_BUTTONS +extern VButton *buttons[]; +#endif + +extern uint8_t *buffer; +extern uint8_t color_type; +RA8876 *ra8876; + +uint8_t FT5316_found; + + +void RA8876_InitDriver() +{ + if (!Settings.display_model) { + Settings.display_model = XDSP_10; + } + + if (XDSP_10 == Settings.display_model) { + + if (Settings.display_width != RA8876_TFTWIDTH) { + Settings.display_width = RA8876_TFTWIDTH; + } + if (Settings.display_height != RA8876_TFTHEIGHT) { + Settings.display_height = RA8876_TFTHEIGHT; + } + buffer=0; + + + fg_color = RA8876_WHITE; + bg_color = RA8876_BLACK; + + + if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]==13) && (pin[GPIO_SSPI_MISO]==12) && (pin[GPIO_SSPI_SCLK]==14)) { + ra8876 = new RA8876(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_MISO],pin[GPIO_SSPI_SCLK],pin[GPIO_BACKLIGHT]); + } else { + if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]==13) && (pin[GPIO_SPI_MISO]==12) && (pin[GPIO_SPI_CLK]==14)) { + ra8876 = new RA8876(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_MISO],pin[GPIO_SPI_CLK],pin[GPIO_BACKLIGHT]); + } else { + return; + } + } + + ra8876->begin(); + renderer = ra8876; + renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); + renderer->dim(Settings.display_dimmer); + + +#ifdef SHOW_SPLASH + + renderer->setTextFont(2); + renderer->setTextColor(RA8876_WHITE,RA8876_BLACK); + renderer->DrawStringAt(600, 300, "RA8876", RA8876_RED,0); + delay(1000); + +#endif + color_type = COLOR_COLOR; + + if (I2cEnabled(XI2C_39) && I2cSetDevice(FT5316_address)) { + FT6236begin(FT5316_address); + FT5316_found=1; + I2cSetActiveFound(FT5316_address, "FT5316"); + } else { + FT5316_found=0; + } + + } +} + +#ifdef USE_TOUCH_BUTTONS +void RA8876_MQTT(uint8_t count,const char *cp) { + ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7); + MqttPublishTeleSensor(); +} + +void RA8876_RDW_BUTT(uint32_t count,uint32_t pwr) { + buttons[count]->xdrawButton(pwr); + if (pwr) buttons[count]->vpower|=0x80; + else buttons[count]->vpower&=0x7f; +} + + +void FT5316Check() { +uint16_t temp; +uint8_t rbutt=0,vbutt=0; +ra8876_ctouch_counter++; +if (2 == ra8876_ctouch_counter) { + + ra8876_ctouch_counter=0; + + if (FT6236readTouchLocation(&ra8876_pLoc,1)) { + ra8876_pLoc.x=ra8876_pLoc.x*RA8876_TFTWIDTH/800; + ra8876_pLoc.y=ra8876_pLoc.y*RA8876_TFTHEIGHT/480; + + + if (renderer) { + + + ra8876_pLoc.x=RA8876_TFTWIDTH-ra8876_pLoc.x; + ra8876_pLoc.y=RA8876_TFTHEIGHT-ra8876_pLoc.y; +# 170 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_10_RA8876.ino" + for (uint8_t count=0; countvpower&0x7f; + if (buttons[count]->contains(ra8876_pLoc.x,ra8876_pLoc.y)) { + + buttons[count]->press(true); + if (buttons[count]->justPressed()) { + if (!bflags) { + + uint8_t pwr=bitRead(power,rbutt); + if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) { + ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON); + RA8876_RDW_BUTT(count,!pwr); + } + } else { + + const char *cp; + if (bflags==1) { + + buttons[count]->vpower^=0x80; + cp="TBT"; + } else { + + buttons[count]->vpower|=0x80; + cp="PBT"; + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + RA8876_MQTT(count,cp); + } + } + } + if (!bflags) { + rbutt++; + } else { + vbutt++; + } + } + } + } + } else { + + for (uint8_t count=0; countvpower&0x7f; + buttons[count]->press(false); + if (buttons[count]->justReleased()) { + if (bflags>0) { + if (bflags>1) { + + buttons[count]->vpower&=0x7f; + RA8876_MQTT(count,"PBT"); + } + buttons[count]->xdrawButton(buttons[count]->vpower&0x80); + } + } + if (!bflags) { + + uint8_t pwr=bitRead(power,rbutt); + uint8_t vpwr=(buttons[count]->vpower&0x80)>>7; + if (pwr!=vpwr) { + RA8876_RDW_BUTT(count,pwr); + } + rbutt++; + } + } + } + ra8876_pLoc.x=0; + ra8876_pLoc.y=0; + } +} +} +#endif +# 426 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_10_RA8876.ino" +bool Xdsp10(uint8_t function) +{ + bool result = false; + + if (FUNC_DISPLAY_INIT_DRIVER == function) { + RA8876_InitDriver(); + } + else if (XDSP_10 == Settings.display_model) { + switch (function) { + case FUNC_DISPLAY_MODEL: + result = true; + break; + case FUNC_DISPLAY_EVERY_50_MSECOND: +#ifdef USE_TOUCH_BUTTONS + if (FT5316_found) FT5316Check(); +#endif + break; + } + } + return result; +} +#endif +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_interface.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_interface.ino" +#ifdef USE_DISPLAY + +#ifdef XFUNC_PTR_IN_ROM +bool (* const xdsp_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xdsp_func_ptr[])(uint8_t) = { +#endif + +#ifdef XDSP_01 + &Xdsp01, +#endif + +#ifdef XDSP_02 + &Xdsp02, +#endif + +#ifdef XDSP_03 + &Xdsp03, +#endif + +#ifdef XDSP_04 + &Xdsp04, +#endif + +#ifdef XDSP_05 + &Xdsp05, +#endif + +#ifdef XDSP_06 + &Xdsp06, +#endif + +#ifdef XDSP_07 + &Xdsp07, +#endif + +#ifdef XDSP_08 + &Xdsp08, +#endif + +#ifdef XDSP_09 + &Xdsp09, +#endif + +#ifdef XDSP_10 + &Xdsp10, +#endif + +#ifdef XDSP_11 + &Xdsp11, +#endif + +#ifdef XDSP_12 + &Xdsp12, +#endif + +#ifdef XDSP_13 + &Xdsp13, +#endif + +#ifdef XDSP_14 + &Xdsp14, +#endif + +#ifdef XDSP_15 + &Xdsp15, +#endif + +#ifdef XDSP_16 + &Xdsp16 +#endif +}; + +const uint8_t xdsp_present = sizeof(xdsp_func_ptr) / sizeof(xdsp_func_ptr[0]); +# 117 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_interface.ino" +uint8_t XdspPresent(void) +{ + return xdsp_present; +} + +bool XdspCall(uint8_t Function) +{ + bool result = false; + + DEBUG_TRACE_LOG(PSTR("DSP: %d"), Function); + + for (uint32_t x = 0; x < xdsp_present; x++) { + result = xdsp_func_ptr[x](Function); + + if (result && (FUNC_DISPLAY_MODEL == Function)) { + break; + } + } + + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_01_ws2812.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_01_ws2812.ino" +#ifdef USE_LIGHT +#ifdef USE_WS2812 +# 38 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_01_ws2812.ino" +#define XLGT_01 1 + +const uint8_t WS2812_SCHEMES = 8; + +const char kWs2812Commands[] PROGMEM = "|" + D_CMND_LED "|" D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_WIDTH ; + +void (* const Ws2812Command[])(void) PROGMEM = { + &CmndLed, &CmndPixels, &CmndRotation, &CmndWidth }; + +#include + +#if (USE_WS2812_CTYPE == NEO_GRB) + typedef NeoGrbFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_BRG) + typedef NeoBrgFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_RBG) + typedef NeoRbgFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_RGBW) + typedef NeoRgbwFeature selectedNeoFeatureType; +#elif (USE_WS2812_CTYPE == NEO_GRBW) + typedef NeoGrbwFeature selectedNeoFeatureType; +#else + typedef NeoRgbFeature selectedNeoFeatureType; +#endif + +#ifdef USE_WS2812_DMA + + +#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) + typedef NeoEsp8266DmaWs2812xMethod selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) + typedef NeoEsp8266DmaSk6812Method selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_APA106) + typedef NeoEsp8266DmaApa106Method selectedNeoSpeedType; +#else + typedef NeoEsp8266Dma800KbpsMethod selectedNeoSpeedType; +#endif + +#else + + +#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) + typedef NeoEsp8266BitBangWs2812xMethod selectedNeoSpeedType; +#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) + typedef NeoEsp8266BitBangSk6812Method selectedNeoSpeedType; +#else + typedef NeoEsp8266BitBang800KbpsMethod selectedNeoSpeedType; +#endif + +#endif + +NeoPixelBus *strip = nullptr; + +struct WsColor { + uint8_t red, green, blue; +}; + +struct ColorScheme { + WsColor* colors; + uint8_t count; +}; + +WsColor kIncandescent[2] = { 255,140,20, 0,0,0 }; +WsColor kRgb[3] = { 255,0,0, 0,255,0, 0,0,255 }; +WsColor kChristmas[2] = { 255,0,0, 0,255,0 }; +WsColor kHanukkah[2] = { 0,0,255, 255,255,255 }; +WsColor kwanzaa[3] = { 255,0,0, 0,0,0, 0,255,0 }; +WsColor kRainbow[7] = { 255,0,0, 255,128,0, 255,255,0, 0,255,0, 0,0,255, 128,0,255, 255,0,255 }; +WsColor kFire[3] = { 255,0,0, 255,102,0, 255,192,0 }; +ColorScheme kSchemes[WS2812_SCHEMES -1] = { + kIncandescent, 2, + kRgb, 3, + kChristmas, 2, + kHanukkah, 2, + kwanzaa, 3, + kRainbow, 7, + kFire, 3 }; + +uint8_t kWidth[5] = { + 1, + 2, + 4, + 8, + 255 }; +uint8_t kWsRepeat[5] = { + 8, + 6, + 4, + 2, + 1 }; + +struct WS2812 { + uint8_t show_next = 1; + uint8_t scheme_offset = 0; + bool suspend_update = false; +} Ws2812; + + + +void Ws2812StripShow(void) +{ +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor c; +#else + RgbColor c; +#endif + + if (Settings.light_correction) { + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + c = strip->GetPixelColor(i); + c.R = ledGamma(c.R); + c.G = ledGamma(c.G); + c.B = ledGamma(c.B); +#if (USE_WS2812_CTYPE > NEO_3LED) + c.W = ledGamma(c.W); +#endif + strip->SetPixelColor(i, c); + } + } + strip->Show(); +} + +int mod(int a, int b) +{ + int ret = a % b; + if (ret < 0) ret += b; + return ret; +} + +void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offset) +{ +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor color; +#else + RgbColor color; +#endif + + uint32_t mod_position = mod(position, (int)Settings.light_pixels); + + color = strip->GetPixelColor(mod_position); + float dimmer = 100 / (float)Settings.light_dimmer; + color.R = tmin(color.R + ((hand_color.red / dimmer) * offset), 255); + color.G = tmin(color.G + ((hand_color.green / dimmer) * offset), 255); + color.B = tmin(color.B + ((hand_color.blue / dimmer) * offset), 255); + strip->SetPixelColor(mod_position, color); +} + +void Ws2812UpdateHand(int position, uint32_t index) +{ + uint32_t width = Settings.light_width; + if (index < WS_MARKER) { width = Settings.ws_width[index]; } + if (!width) { return; } + + position = (position + Settings.light_rotation) % Settings.light_pixels; + + if (Settings.flag.ws_clock_reverse) { + position = Settings.light_pixels -position; + } + WsColor hand_color = { Settings.ws_color[index][WS_RED], Settings.ws_color[index][WS_GREEN], Settings.ws_color[index][WS_BLUE] }; + + Ws2812UpdatePixelColor(position, hand_color, 1); + + uint32_t range = ((width -1) / 2) +1; + for (uint32_t h = 1; h < range; h++) { + float offset = (float)(range - h) / (float)range; + Ws2812UpdatePixelColor(position -h, hand_color, offset); + Ws2812UpdatePixelColor(position +h, hand_color, offset); + } +} + +void Ws2812Clock(void) +{ + strip->ClearTo(0); + int clksize = 60000 / (int)Settings.light_pixels; + + Ws2812UpdateHand((RtcTime.second * 1000) / clksize, WS_SECOND); + Ws2812UpdateHand((RtcTime.minute * 1000) / clksize, WS_MINUTE); + Ws2812UpdateHand((((RtcTime.hour % 12) * 5000) + ((RtcTime.minute * 1000) / 12 )) / clksize, WS_HOUR); + if (Settings.ws_color[WS_MARKER][WS_RED] + Settings.ws_color[WS_MARKER][WS_GREEN] + Settings.ws_color[WS_MARKER][WS_BLUE]) { + for (uint32_t i = 0; i < 12; i++) { + Ws2812UpdateHand((i * 5000) / clksize, WS_MARKER); + } + } + + Ws2812StripShow(); +} + +void Ws2812GradientColor(uint32_t schemenr, struct WsColor* mColor, uint32_t range, uint32_t gradRange, uint32_t i) +{ + + + + + ColorScheme scheme = kSchemes[schemenr]; + uint32_t curRange = i / range; + uint32_t rangeIndex = i % range; + uint32_t colorIndex = rangeIndex / gradRange; + uint32_t start = colorIndex; + uint32_t end = colorIndex +1; + if (curRange % 2 != 0) { + start = (scheme.count -1) - start; + end = (scheme.count -1) - end; + } + float dimmer = 100 / (float)Settings.light_dimmer; + float fmyRed = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / dimmer; + float fmyGrn = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / dimmer; + float fmyBlu = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / dimmer; + mColor->red = (uint8_t)fmyRed; + mColor->green = (uint8_t)fmyGrn; + mColor->blue = (uint8_t)fmyBlu; +} + +void Ws2812Gradient(uint32_t schemenr) +{ + + + + + +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor c; + c.W = 0; +#else + RgbColor c; +#endif + + ColorScheme scheme = kSchemes[schemenr]; + if (scheme.count < 2) { return; } + + uint32_t repeat = kWsRepeat[Settings.light_width]; + uint32_t range = (uint32_t)ceil((float)Settings.light_pixels / (float)repeat); + uint32_t gradRange = (uint32_t)ceil((float)range / (float)(scheme.count - 1)); + uint32_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); + uint32_t offset = speed > 0 ? Light.strip_timer_counter / speed : 0; + + WsColor oldColor, currentColor; + Ws2812GradientColor(schemenr, &oldColor, range, gradRange, offset); + currentColor = oldColor; + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + if (kWsRepeat[Settings.light_width] > 1) { + Ws2812GradientColor(schemenr, ¤tColor, range, gradRange, i +offset); + } + if (Settings.light_speed > 0) { + + c.R = map(Light.strip_timer_counter % speed, 0, speed, oldColor.red, currentColor.red); + c.G = map(Light.strip_timer_counter % speed, 0, speed, oldColor.green, currentColor.green); + c.B = map(Light.strip_timer_counter % speed, 0, speed, oldColor.blue, currentColor.blue); + } + else { + + c.R = currentColor.red; + c.G = currentColor.green; + c.B = currentColor.blue; + } + strip->SetPixelColor(i, c); + oldColor = currentColor; + } + Ws2812StripShow(); +} + +void Ws2812Bars(uint32_t schemenr) +{ + + + + + +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor c; + c.W = 0; +#else + RgbColor c; +#endif + + ColorScheme scheme = kSchemes[schemenr]; + + uint32_t maxSize = Settings.light_pixels / scheme.count; + if (kWidth[Settings.light_width] > maxSize) { maxSize = 0; } + + uint32_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); + uint32_t offset = (speed > 0) ? Light.strip_timer_counter / speed : 0; + + WsColor mcolor[scheme.count]; + memcpy(mcolor, scheme.colors, sizeof(mcolor)); + float dimmer = 100 / (float)Settings.light_dimmer; + for (uint32_t i = 0; i < scheme.count; i++) { + float fmyRed = (float)mcolor[i].red / dimmer; + float fmyGrn = (float)mcolor[i].green / dimmer; + float fmyBlu = (float)mcolor[i].blue / dimmer; + mcolor[i].red = (uint8_t)fmyRed; + mcolor[i].green = (uint8_t)fmyGrn; + mcolor[i].blue = (uint8_t)fmyBlu; + } + uint32_t colorIndex = offset % scheme.count; + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + if (maxSize) { colorIndex = ((i + offset) % (scheme.count * kWidth[Settings.light_width])) / kWidth[Settings.light_width]; } + c.R = mcolor[colorIndex].red; + c.G = mcolor[colorIndex].green; + c.B = mcolor[colorIndex].blue; + strip->SetPixelColor(i, c); + } + Ws2812StripShow(); +} + +void Ws2812Clear(void) +{ + strip->ClearTo(0); + strip->Show(); + Ws2812.show_next = 1; +} + +void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) +{ +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor lcolor; + lcolor.W = white; +#else + RgbColor lcolor; +#endif + + lcolor.R = red; + lcolor.G = green; + lcolor.B = blue; + if (led) { + strip->SetPixelColor(led -1, lcolor); + } else { + + for (uint32_t i = 0; i < Settings.light_pixels; i++) { + strip->SetPixelColor(i, lcolor); + } + } + + if (!Ws2812.suspend_update) { + strip->Show(); + Ws2812.show_next = 1; + } +} + +char* Ws2812GetColor(uint32_t led, char* scolor) +{ + uint8_t sl_ledcolor[4]; + + #if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor lcolor = strip->GetPixelColor(led -1); + sl_ledcolor[3] = lcolor.W; + #else + RgbColor lcolor = strip->GetPixelColor(led -1); + #endif + sl_ledcolor[0] = lcolor.R; + sl_ledcolor[1] = lcolor.G; + sl_ledcolor[2] = lcolor.B; + scolor[0] = '\0'; + for (uint32_t i = 0; i < Light.subtype; i++) { + if (Settings.flag.decimal_text) { + snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", sl_ledcolor[i]); + } else { + snprintf_P(scolor, 25, PSTR("%s%02X"), scolor, sl_ledcolor[i]); + } + } + return scolor; +} + + + + + +void Ws2812ForceSuspend (void) +{ + Ws2812.suspend_update = true; +} + +void Ws2812ForceUpdate (void) +{ + Ws2812.suspend_update = false; + strip->Show(); + Ws2812.show_next = 1; +} + + + +bool Ws2812SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); + + return true; +} + +void Ws2812ShowScheme(void) +{ + uint32_t scheme = Settings.light_scheme - Ws2812.scheme_offset; + + switch (scheme) { + case 0: + if ((1 == state_250mS) || (Ws2812.show_next)) { + Ws2812Clock(); + Ws2812.show_next = 0; + } + break; + default: + if (1 == Settings.light_fade) { + Ws2812Gradient(scheme -1); + } else { + Ws2812Bars(scheme -1); + } + Ws2812.show_next = 1; + break; + } +} + +void Ws2812ModuleSelected(void) +{ + if (pin[GPIO_WS2812] < 99) { + + + strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); + strip->Begin(); + + Ws2812Clear(); + + Ws2812.scheme_offset = Light.max_scheme +1; + Light.max_scheme += WS2812_SCHEMES; + +#if (USE_WS2812_CTYPE > NEO_3LED) + light_type = LT_RGBW; +#else + light_type = LT_RGB; +#endif + light_flg = XLGT_01; + } +} + + + +void CmndLed(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Settings.light_pixels)) { + if (XdrvMailbox.data_len > 0) { + char *p; + uint16_t idx = XdrvMailbox.index; + Ws2812ForceSuspend(); + for (char *color = strtok_r(XdrvMailbox.data, " ", &p); color; color = strtok_r(nullptr, " ", &p)) { + if (LightColorEntry(color, strlen(color))) { + Ws2812SetColor(idx, Light.entry_color[0], Light.entry_color[1], Light.entry_color[2], Light.entry_color[3]); + idx++; + if (idx > Settings.light_pixels) { break; } + } else { + break; + } + } + Ws2812ForceUpdate(); + } + char scolor[LIGHT_COLOR_SIZE]; + ResponseCmndIdxChar(Ws2812GetColor(XdrvMailbox.index, scolor)); + } +} + +void CmndPixels(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= WS2812_MAX_LEDS)) { + Settings.light_pixels = XdrvMailbox.payload; + Settings.light_rotation = 0; + Ws2812Clear(); + Light.update = true; + } + ResponseCmndNumber(Settings.light_pixels); +} + +void CmndRotation(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < Settings.light_pixels)) { + Settings.light_rotation = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.light_rotation); +} + +void CmndWidth(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { + if (1 == XdrvMailbox.index) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 4)) { + Settings.light_width = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.light_width); + } else { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32)) { + Settings.ws_width[XdrvMailbox.index -2] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.ws_width[XdrvMailbox.index -2]); + } + } +} + + + + + +bool Xlgt01(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Ws2812SetChannels(); + break; + case FUNC_SET_SCHEME: + Ws2812ShowScheme(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kWs2812Commands, Ws2812Command); + break; + case FUNC_MODULE_INIT: + Ws2812ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_02_my92x1.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_02_my92x1.ino" +#ifdef USE_LIGHT +#ifdef USE_MY92X1 + + + + +#define XLGT_02 2 + +struct MY92X1 { + uint8_t pdi_pin = 0; + uint8_t pdcki_pin = 0; + uint8_t model = 0; +} My92x1; + +extern "C" { + void os_delay_us(unsigned int); +} + +void LightDiPulse(uint8_t times) +{ + for (uint32_t i = 0; i < times; i++) { + digitalWrite(My92x1.pdi_pin, HIGH); + digitalWrite(My92x1.pdi_pin, LOW); + } +} + +void LightDckiPulse(uint8_t times) +{ + for (uint32_t i = 0; i < times; i++) { + digitalWrite(My92x1.pdcki_pin, HIGH); + digitalWrite(My92x1.pdcki_pin, LOW); + } +} + +void LightMy92x1Write(uint8_t data) +{ + for (uint32_t i = 0; i < 4; i++) { + digitalWrite(My92x1.pdcki_pin, LOW); + digitalWrite(My92x1.pdi_pin, (data & 0x80)); + digitalWrite(My92x1.pdcki_pin, HIGH); + data = data << 1; + digitalWrite(My92x1.pdi_pin, (data & 0x80)); + digitalWrite(My92x1.pdcki_pin, LOW); + digitalWrite(My92x1.pdi_pin, LOW); + data = data << 1; + } +} + +void LightMy92x1Init(void) +{ + uint8_t chips[3] = { 1, 2, 2 }; + + LightDckiPulse(chips[My92x1.model] * 32); + os_delay_us(12); + + + LightDiPulse(12); + os_delay_us(12); + for (uint32_t n = 0; n < chips[My92x1.model]; n++) { + LightMy92x1Write(0x18); + } + os_delay_us(12); + + + LightDiPulse(16); + os_delay_us(12); +} + +void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty_w, uint8_t duty_c) +{ + uint8_t channels[3] = { 4, 6, 6 }; + + uint8_t duty[3][6] = {{ duty_r, duty_g, duty_b, duty_w, 0, 0 }, + { duty_w, duty_c, 0, duty_g, duty_r, duty_b }, + { duty_r, duty_g, duty_b, duty_w, duty_w, duty_w }}; + + os_delay_us(12); + for (uint32_t channel = 0; channel < channels[My92x1.model]; channel++) { + LightMy92x1Write(duty[My92x1.model][channel]); + } + os_delay_us(12); + LightDiPulse(8); + os_delay_us(12); +} + + + +bool My92x1SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); + + return true; +} + +void My92x1ModuleSelected(void) +{ + if ((pin[GPIO_DCKI] < 99) && (pin[GPIO_DI] < 99)) { + My92x1.pdi_pin = pin[GPIO_DI]; + My92x1.pdcki_pin = pin[GPIO_DCKI]; + + pinMode(My92x1.pdi_pin, OUTPUT); + pinMode(My92x1.pdcki_pin, OUTPUT); + digitalWrite(My92x1.pdi_pin, LOW); + digitalWrite(My92x1.pdcki_pin, LOW); + + My92x1.model = 2; + light_type = LT_RGBW; + if (AILIGHT == my_module_type) { + My92x1.model = 0; + + } + else if (SONOFF_B1 == my_module_type) { + My92x1.model = 1; + light_type = LT_RGBWC; + } + + LightMy92x1Init(); + + light_flg = XLGT_02; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: MY29x1 Found")); + } +} + + + + + +bool Xlgt02(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = My92x1SetChannels(); + break; + case FUNC_MODULE_INIT: + My92x1ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_03_sm16716.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_03_sm16716.ino" +#ifdef USE_LIGHT +#ifdef USE_SM16716 + + + + + + + +#define XLGT_03 3 + +#define D_LOG_SM16716 "SM16716: " + +struct SM16716 { + uint8_t pin_clk = 0; + uint8_t pin_dat = 0; + uint8_t pin_sel = 0; + bool enabled = false; +} Sm16716; + +void SM16716_SendBit(uint8_t v) +{ + + + + + + digitalWrite(Sm16716.pin_dat, (v != 0) ? HIGH : LOW); + + digitalWrite(Sm16716.pin_clk, HIGH); + + digitalWrite(Sm16716.pin_clk, LOW); +} + +void SM16716_SendByte(uint8_t v) +{ + uint8_t mask; + + for (mask = 0x80; mask; mask >>= 1) { + SM16716_SendBit(v & mask); + } +} + +void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b) +{ + if (Sm16716.pin_sel < 99) { + bool should_enable = (duty_r | duty_g | duty_b); + if (!Sm16716.enabled && should_enable) { + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color on")); + Sm16716.enabled = true; + digitalWrite(Sm16716.pin_sel, HIGH); + + + delayMicroseconds(1000); + SM16716_Init(); + } + else if (Sm16716.enabled && !should_enable) { + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color off")); + Sm16716.enabled = false; + digitalWrite(Sm16716.pin_sel, LOW); + } + } + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), duty_r, duty_g, duty_b); + + + SM16716_SendBit(1); + SM16716_SendByte(duty_r); + SM16716_SendByte(duty_g); + SM16716_SendByte(duty_b); + + + + + + SM16716_SendBit(0); + SM16716_SendByte(0); + SM16716_SendByte(0); + SM16716_SendByte(0); +} +# 111 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_03_sm16716.ino" +void SM16716_Init(void) +{ + for (uint32_t t_init = 0; t_init < 50; ++t_init) { + SM16716_SendBit(0); + } +} + + + +bool Sm16716SetChannels(void) +{ +# 132 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_03_sm16716.ino" + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + SM16716_Update(cur_col[0], cur_col[1], cur_col[2]); + + return true; +} + +void Sm16716ModuleSelected(void) +{ + if ((pin[GPIO_SM16716_CLK] < 99) && (pin[GPIO_SM16716_DAT] < 99)) { + Sm16716.pin_clk = pin[GPIO_SM16716_CLK]; + Sm16716.pin_dat = pin[GPIO_SM16716_DAT]; + Sm16716.pin_sel = pin[GPIO_SM16716_SEL]; +# 157 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_03_sm16716.ino" + pinMode(Sm16716.pin_clk, OUTPUT); + digitalWrite(Sm16716.pin_clk, LOW); + + pinMode(Sm16716.pin_dat, OUTPUT); + digitalWrite(Sm16716.pin_dat, LOW); + + if (Sm16716.pin_sel < 99) { + pinMode(Sm16716.pin_sel, OUTPUT); + digitalWrite(Sm16716.pin_sel, LOW); + + } else { + + SM16716_Init(); + } + + LightPwmOffset(LST_RGB); + light_type += LST_RGB; + light_flg = XLGT_03; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM16716 Found")); + } +} + + + + + +bool Xlgt03(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Sm16716SetChannels(); + break; + case FUNC_MODULE_INIT: + Sm16716ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_04_sm2135.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_04_sm2135.ino" +#ifdef USE_LIGHT +#ifdef USE_SM2135 + + + + + + +#define XLGT_04 4 + +#define SM2135_ADDR_MC 0xC0 +#define SM2135_ADDR_CH 0xC1 +#define SM2135_ADDR_R 0xC2 +#define SM2135_ADDR_G 0xC3 +#define SM2135_ADDR_B 0xC4 +#define SM2135_ADDR_C 0xC5 +#define SM2135_ADDR_W 0xC6 + +#define SM2135_RGB 0x00 +#define SM2135_CW 0x80 + +#define SM2135_10MA 0x00 +#define SM2135_15MA 0x01 +#define SM2135_20MA 0x02 +#define SM2135_25MA 0x03 +#define SM2135_30MA 0x04 +#define SM2135_35MA 0x05 +#define SM2135_40MA 0x06 +#define SM2135_45MA 0x07 +#define SM2135_50MA 0x08 +#define SM2135_55MA 0x09 +#define SM2135_60MA 0x0A + + +const uint8_t SM2135_CURRENT = (SM2135_20MA << 4) | SM2135_15MA; + +struct SM2135 { + uint8_t clk = 0; + uint8_t data = 0; +} Sm2135; + +uint8_t Sm2135Write(uint8_t data) +{ + for (uint32_t i = 0; i < 8; i++) { + digitalWrite(Sm2135.clk, LOW); + digitalWrite(Sm2135.data, (data & 0x80)); + digitalWrite(Sm2135.clk, HIGH); + data = data << 1; + } + digitalWrite(Sm2135.clk, LOW); + digitalWrite(Sm2135.data, HIGH); + pinMode(Sm2135.data, INPUT); + digitalWrite(Sm2135.clk, HIGH); + uint8_t ack = digitalRead(Sm2135.data); + pinMode(Sm2135.data, OUTPUT); + return ack; +} + +void Sm2135Send(uint8_t *buffer, uint8_t size) +{ + digitalWrite(Sm2135.data, LOW); + for (uint32_t i = 0; i < size; i++) { + Sm2135Write(buffer[i]); + } + digitalWrite(Sm2135.clk, LOW); + digitalWrite(Sm2135.clk, HIGH); + digitalWrite(Sm2135.data, HIGH); +} + + + +bool Sm2135SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + uint8_t data[6]; + + if ((0 == cur_col[0]) && (0 == cur_col[1]) && (0 == cur_col[2])) { +# 106 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_04_sm2135.ino" + data[0] = SM2135_ADDR_MC; + data[1] = SM2135_CURRENT; + data[2] = SM2135_CW; + Sm2135Send(data, 3); + delay(1); + data[0] = SM2135_ADDR_C; + data[1] = cur_col[4]; + data[2] = cur_col[3]; + Sm2135Send(data, 3); + } else { + + + + + + + + data[0] = SM2135_ADDR_MC; + data[1] = SM2135_CURRENT; + data[2] = SM2135_RGB; + data[3] = cur_col[1]; + data[4] = cur_col[0]; + data[5] = cur_col[2]; + Sm2135Send(data, 6); + } + + return true; +} + +void Sm2135ModuleSelected(void) +{ + if ((pin[GPIO_SM2135_CLK] < 99) && (pin[GPIO_SM2135_DAT] < 99)) { + Sm2135.clk = pin[GPIO_SM2135_CLK]; + Sm2135.data = pin[GPIO_SM2135_DAT]; + + pinMode(Sm2135.data, OUTPUT); + digitalWrite(Sm2135.data, HIGH); + pinMode(Sm2135.clk, OUTPUT); + digitalWrite(Sm2135.clk, HIGH); + + light_type = LT_RGBWC; + light_flg = XLGT_04; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM2135 Found")); + } +} + + + + + +bool Xlgt04(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Sm2135SetChannels(); + break; + case FUNC_MODULE_INIT: + Sm2135ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_05_sonoff_l1.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_05_sonoff_l1.ino" +#ifdef USE_LIGHT +#ifdef USE_SONOFF_L1 + + + + +#define XLGT_05 5 + +#define SONOFF_L1_BUFFER_SIZE 140 + +#define SONOFF_L1_MODE_COLORFUL 1 +#define SONOFF_L1_MODE_COLORFUL_GRADIENT 2 +#define SONOFF_L1_MODE_COLORFUL_BREATH 3 +#define SONOFF_L1_MODE_DIY_GRADIENT 4 +#define SONOFF_L1_MODE_DIY_PULSE 5 +#define SONOFF_L1_MODE_DIY_BREATH 6 +#define SONOFF_L1_MODE_DIY_STROBE 7 +#define SONOFF_L1_MODE_RGB_GRADIENT 8 +#define SONOFF_L1_MODE_RGB_PULSE 9 +#define SONOFF_L1_MODE_RGB_BREATH 10 +#define SONOFF_L1_MODE_RGB_STROBE 11 +#define SONOFF_L1_MODE_SYNC_TO_MUSIC 12 + +struct SNFL1 { + uint32_t unlock = 0; + bool receive_ready = true; +} Snfl1; + + + +void SnfL1Send(const char *buffer) +{ + + + Serial.print(buffer); + Serial.write(0x1B); + Serial.flush(); +} + +void SnfL1SerialSendOk(void) +{ + char buffer[16]; + snprintf_P(buffer, sizeof(buffer), PSTR("AT+SEND=ok")); + + SnfL1Send(buffer); +} + +bool SnfL1SerialInput(void) +{ + if (serial_in_byte != 0x1B) { + if (serial_in_byte_counter >= 140) { + serial_in_byte_counter = 0; + } + if (serial_in_byte_counter || (!serial_in_byte_counter && ('A' == serial_in_byte))) { + serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + } + } else { + serial_in_buffer[serial_in_byte_counter++] = 0x00; + + + + + + + if (!strncmp(serial_in_buffer +3, "RESULT", 6)) { + Snfl1.receive_ready = true; + } + else if (!strncmp(serial_in_buffer +3, "UPDATE", 6)) { + char cmnd_dimmer[20]; + char cmnd_color[20]; + char *end_str; + char *string = serial_in_buffer +10; + char *token = strtok_r(string, ",", &end_str); + + bool color_updated[3] = { false, false, false }; + uint8_t current_color[3]; + memcpy(current_color, Settings.light_color, 3); + + bool switch_state = false; + bool is_power_change = false; + bool is_color_change = false; + bool is_brightness_change = false; + + while (token != nullptr) { + char* end_token; + char* token2 = strtok_r(token, ":", &end_token); + char* token3 = strtok_r(nullptr, ":", &end_token); + + if (!strncmp(token2, "\"sequence\"", 10)) { + + + + token = nullptr; + } + + else if (!strncmp(token2, "\"switch\"", 8)) { + switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; + + + + is_power_change = (switch_state != Light.power); + } + + else if (!strncmp(token2, "\"color", 6)) { + char color_channel_name = token2[6]; + int color_index; + switch(color_channel_name) + { + case 'R': color_index = 0; + break; + case 'G': color_index = 1; + break; + case 'B': color_index = 2; + break; + } + int color_value = atoi(token3); + current_color[color_index] = color_value; + color_updated[color_index] = true; + + bool all_color_channels_updated = color_updated[0] && color_updated[1] && color_updated[2]; + if (all_color_channels_updated) { + + + + + + is_color_change = (Light.power && (memcmp(current_color, Settings.light_color, 3) != 0)); + } + snprintf_P(cmnd_color, sizeof(cmnd_color), PSTR(D_CMND_COLOR "2 %02x%02x%02x"), current_color[0], current_color[1], current_color[2]); + } + + else if (!strncmp(token2, "\"bright\"", 8)) { + uint8_t dimmer = atoi(token3); + + + + is_brightness_change = (Light.power && (dimmer > 0) && (dimmer != Settings.light_dimmer)); + snprintf_P(cmnd_dimmer, sizeof(cmnd_dimmer), PSTR(D_CMND_DIMMER " %d"), dimmer); + } + + token = strtok_r(nullptr, ",", &end_str); + } + + if (is_power_change) { + if (Settings.light_scheme > 0) { + if (!switch_state) { + char cmnd_scheme[20]; + snprintf_P(cmnd_scheme, sizeof(cmnd_scheme), PSTR(D_CMND_SCHEME " 0")); + ExecuteCommand(cmnd_scheme, SRC_SWITCH); + } + } else { + ExecuteCommandPower(1, switch_state, SRC_SWITCH); + } + } + else if (is_brightness_change) { + ExecuteCommand(cmnd_dimmer, SRC_SWITCH); + } + else if (Light.power && is_color_change) { + if (0 == Settings.light_scheme) { + if (Settings.light_fade) { + char cmnd_fade[20]; + snprintf_P(cmnd_fade, sizeof(cmnd_fade), PSTR(D_CMND_FADE " 0")); + ExecuteCommand(cmnd_fade, SRC_SWITCH); + } + ExecuteCommand(cmnd_color, SRC_SWITCH); + } + } + } + + SnfL1SerialSendOk(); + + return true; + } + serial_in_byte = 0; + return false; +} + + + +bool SnfL1SetChannels(void) +{ + if (Snfl1.receive_ready || TimeReached(Snfl1.unlock)) { + + uint8_t *scale_col = (uint8_t*)XdrvMailbox.topic; + + char buffer[140]; + snprintf_P(buffer, sizeof(buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d"), + LocalTime(), millis()%1000, + Light.power ? "on" : "off", + scale_col[0], scale_col[1], scale_col[2], + light_state.getDimmer(), + SONOFF_L1_MODE_COLORFUL); + + SnfL1Send(buffer); + + Snfl1.unlock = millis() + 500; + Snfl1.receive_ready = false; + } + return true; +} + +void SnfL1ModuleSelected(void) +{ + if (SONOFF_L1 == my_module_type) { + if ((pin[GPIO_RXD] < 99) && (pin[GPIO_TXD] < 99)) { + SetSerial(19200, TS_SERIAL_8N1); + + light_type = LT_RGB; + light_flg = XLGT_05; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: Sonoff L1 Found")); + } + } +} + + + + + +bool Xlgt05(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SERIAL: + result = SnfL1SerialInput(); + break; + case FUNC_SET_CHANNELS: + result = SnfL1SetChannels(); + break; + case FUNC_MODULE_INIT: + SnfL1ModuleSelected(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_interface.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_interface.ino" +#ifdef USE_LIGHT + +#ifdef XFUNC_PTR_IN_ROM +bool (* const xlgt_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xlgt_func_ptr[])(uint8_t) = { +#endif + +#ifdef XLGT_01 + &Xlgt01, +#endif + +#ifdef XLGT_02 + &Xlgt02, +#endif + +#ifdef XLGT_03 + &Xlgt03, +#endif + +#ifdef XLGT_04 + &Xlgt04, +#endif + +#ifdef XLGT_05 + &Xlgt05, +#endif + +#ifdef XLGT_06 + &Xlgt06, +#endif + +#ifdef XLGT_07 + &Xlgt07, +#endif + +#ifdef XLGT_08 + &Xlgt08, +#endif + +#ifdef XLGT_09 + &Xlgt09, +#endif + +#ifdef XLGT_10 + &Xlgt10, +#endif + +#ifdef XLGT_11 + &Xlgt11, +#endif + +#ifdef XLGT_12 + &Xlgt12, +#endif + +#ifdef XLGT_13 + &Xlgt13, +#endif + +#ifdef XLGT_14 + &Xlgt14, +#endif + +#ifdef XLGT_15 + &Xlgt15, +#endif + +#ifdef XLGT_16 + &Xlgt16 +#endif +}; + +const uint8_t xlgt_present = sizeof(xlgt_func_ptr) / sizeof(xlgt_func_ptr[0]); + +uint8_t xlgt_active = 0; + +bool XlgtCall(uint8_t function) +{ + DEBUG_TRACE_LOG(PSTR("LGT: %d"), function); + + if (FUNC_MODULE_INIT == function) { + for (uint32_t x = 0; x < xlgt_present; x++) { + xlgt_func_ptr[x](function); + if (light_flg) { + xlgt_active = x; + return true; + } + } + } + else if (light_flg) { + return xlgt_func_ptr[xlgt_active](function); + } + return false; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_01_hlw8012.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_01_hlw8012.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_HLW8012 + + + + + + +#define XNRG_01 1 + + +#define HLW_PREF 10000 +#define HLW_UREF 2200 +#define HLW_IREF 4545 + + +#define HJL_PREF 1362 +#define HJL_UREF 822 +#define HJL_IREF 3300 + +#define HLW_POWER_PROBE_TIME 10 +#define HLW_SAMPLE_COUNT 10 + + + +struct HLW { +#ifdef HLW_DEBUG + unsigned long debug[HLW_SAMPLE_COUNT]; +#endif + unsigned long cf_pulse_length = 0; + unsigned long cf_pulse_last_time = 0; + unsigned long cf_power_pulse_length = 0; + + unsigned long cf1_pulse_length = 0; + unsigned long cf1_pulse_last_time = 0; + unsigned long cf1_summed_pulse_length = 0; + unsigned long cf1_pulse_counter = 0; + unsigned long cf1_voltage_pulse_length = 0; + unsigned long cf1_current_pulse_length = 0; + + unsigned long energy_period_counter = 0; + + unsigned long power_ratio = 0; + unsigned long voltage_ratio = 0; + unsigned long current_ratio = 0; + + uint8_t model_type = 0; + uint8_t cf1_timer = 0; + uint8_t power_retry = 0; + bool select_ui_flag = false; + bool ui_flag = true; + bool load_off = true; +} Hlw; + + +#ifndef USE_WS2812_DMA +void HlwCfInterrupt(void) ICACHE_RAM_ATTR; +void HlwCf1Interrupt(void) ICACHE_RAM_ATTR; +#endif + +void HlwCfInterrupt(void) +{ + unsigned long us = micros(); + + if (Hlw.load_off) { + Hlw.cf_pulse_last_time = us; + Hlw.load_off = false; + } else { + Hlw.cf_pulse_length = us - Hlw.cf_pulse_last_time; + Hlw.cf_pulse_last_time = us; + Hlw.energy_period_counter++; + } + Energy.data_valid[0] = 0; +} + +void HlwCf1Interrupt(void) +{ + unsigned long us = micros(); + + Hlw.cf1_pulse_length = us - Hlw.cf1_pulse_last_time; + Hlw.cf1_pulse_last_time = us; + if ((Hlw.cf1_timer > 2) && (Hlw.cf1_timer < 8)) { + Hlw.cf1_summed_pulse_length += Hlw.cf1_pulse_length; +#ifdef HLW_DEBUG + Hlw.debug[Hlw.cf1_pulse_counter] = Hlw.cf1_pulse_length; +#endif + Hlw.cf1_pulse_counter++; + if (HLW_SAMPLE_COUNT == Hlw.cf1_pulse_counter) { + Hlw.cf1_timer = 8; + } + } + Energy.data_valid[0] = 0; +} + + + +void HlwEvery200ms(void) +{ + unsigned long cf1_pulse_length = 0; + unsigned long hlw_w = 0; + unsigned long hlw_u = 0; + unsigned long hlw_i = 0; + + if (micros() - Hlw.cf_pulse_last_time > (HLW_POWER_PROBE_TIME * 1000000)) { + Hlw.cf_pulse_length = 0; + Hlw.load_off = true; + } + Hlw.cf_power_pulse_length = Hlw.cf_pulse_length; + + if (Hlw.cf_power_pulse_length && Energy.power_on && !Hlw.load_off) { + hlw_w = (Hlw.power_ratio * Settings.energy_power_calibration) / Hlw.cf_power_pulse_length ; + Energy.active_power[0] = (float)hlw_w / 10; + Hlw.power_retry = 1; + } else { + if (Hlw.power_retry) { + Hlw.power_retry--; + } else { + Energy.active_power[0] = 0; + } + } + + if (pin[GPIO_NRG_CF1] < 99) { + Hlw.cf1_timer++; + if (Hlw.cf1_timer >= 8) { + Hlw.cf1_timer = 0; + Hlw.select_ui_flag = (Hlw.select_ui_flag) ? false : true; + DigitalWrite(GPIO_NRG_SEL, Hlw.select_ui_flag); + + if (Hlw.cf1_pulse_counter) { + cf1_pulse_length = Hlw.cf1_summed_pulse_length / Hlw.cf1_pulse_counter; + } + +#ifdef HLW_DEBUG + + char stemp[100]; + stemp[0] = '\0'; + for (uint32_t i = 0; i < Hlw.cf1_pulse_counter; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("%s %d"), stemp, Hlw.debug[i]); + } + for (uint32_t i = 0; i < Hlw.cf1_pulse_counter; i++) { + for (uint32_t j = i + 1; j < Hlw.cf1_pulse_counter; j++) { + if (Hlw.debug[i] > Hlw.debug[j]) { + std::swap(Hlw.debug[i], Hlw.debug[j]); + } + } + } + unsigned long median = Hlw.debug[(Hlw.cf1_pulse_counter +1) / 2]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: power %d, ui %d, cnt %d, smpl%s, sum %d, mean %d, median %d"), + Hlw.cf_power_pulse_length , Hlw.select_ui_flag, Hlw.cf1_pulse_counter, stemp, Hlw.cf1_summed_pulse_length, cf1_pulse_length, median); +#endif + + if (Hlw.select_ui_flag == Hlw.ui_flag) { + Hlw.cf1_voltage_pulse_length = cf1_pulse_length; + + if (Hlw.cf1_voltage_pulse_length && Energy.power_on) { + hlw_u = (Hlw.voltage_ratio * Settings.energy_voltage_calibration) / Hlw.cf1_voltage_pulse_length ; + Energy.voltage[0] = (float)hlw_u / 10; + } else { + Energy.voltage[0] = 0; + } + + } else { + Hlw.cf1_current_pulse_length = cf1_pulse_length; + + if (Hlw.cf1_current_pulse_length && Energy.active_power[0]) { + hlw_i = (Hlw.current_ratio * Settings.energy_current_calibration) / Hlw.cf1_current_pulse_length; + Energy.current[0] = (float)hlw_i / 1000; + } else { + Energy.current[0] = 0; + } + + } + Hlw.cf1_summed_pulse_length = 0; + Hlw.cf1_pulse_counter = 0; + } + } +} + +void HlwEverySecond(void) +{ + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { + Hlw.cf1_voltage_pulse_length = 0; + Hlw.cf1_current_pulse_length = 0; + Hlw.cf_power_pulse_length = 0; + } else { + unsigned long hlw_len; + + if (Hlw.energy_period_counter) { + hlw_len = 10000 / Hlw.energy_period_counter; + Hlw.energy_period_counter = 0; + if (hlw_len) { + Energy.kWhtoday_delta += ((Hlw.power_ratio * Settings.energy_power_calibration) / hlw_len) / 36; + EnergyUpdateToday(); + } + } + } +} + +void HlwSnsInit(void) +{ + if (!Settings.energy_power_calibration || (4975 == Settings.energy_power_calibration)) { + Settings.energy_power_calibration = HLW_PREF_PULSE; + Settings.energy_voltage_calibration = HLW_UREF_PULSE; + Settings.energy_current_calibration = HLW_IREF_PULSE; + } + + if (Hlw.model_type) { + Hlw.power_ratio = HJL_PREF; + Hlw.voltage_ratio = HJL_UREF; + Hlw.current_ratio = HJL_IREF; + } else { + Hlw.power_ratio = HLW_PREF; + Hlw.voltage_ratio = HLW_UREF; + Hlw.current_ratio = HLW_IREF; + } + + if (pin[GPIO_NRG_SEL] < 99) { + pinMode(pin[GPIO_NRG_SEL], OUTPUT); + digitalWrite(pin[GPIO_NRG_SEL], Hlw.select_ui_flag); + } + if (pin[GPIO_NRG_CF1] < 99) { + pinMode(pin[GPIO_NRG_CF1], INPUT_PULLUP); + attachInterrupt(pin[GPIO_NRG_CF1], HlwCf1Interrupt, FALLING); + } + pinMode(pin[GPIO_HLW_CF], INPUT_PULLUP); + attachInterrupt(pin[GPIO_HLW_CF], HlwCfInterrupt, FALLING); +} + +void HlwDrvInit(void) +{ + Hlw.model_type = 0; + if (pin[GPIO_HJL_CF] < 99) { + pin[GPIO_HLW_CF] = pin[GPIO_HJL_CF]; + pin[GPIO_HJL_CF] = 99; + Hlw.model_type = 1; + } + + if (pin[GPIO_HLW_CF] < 99) { + + Hlw.ui_flag = true; + if (pin[GPIO_NRG_SEL_INV] < 99) { + pin[GPIO_NRG_SEL] = pin[GPIO_NRG_SEL_INV]; + pin[GPIO_NRG_SEL_INV] = 99; + Hlw.ui_flag = false; + } + + if (pin[GPIO_NRG_CF1] < 99) { + if (99 == pin[GPIO_NRG_SEL]) { + Energy.current_available = false; + } + } else { + Energy.current_available = false; + Energy.voltage_available = false; + } + + energy_flg = XNRG_01; + } +} + +bool HlwCommand(void) +{ + bool serviced = true; + + if ((CMND_POWERCAL == Energy.command_code) || (CMND_VOLTAGECAL == Energy.command_code) || (CMND_CURRENTCAL == Energy.command_code)) { + + } + else if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Hlw.cf_power_pulse_length ) { + Settings.energy_power_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * Hlw.cf_power_pulse_length ) / Hlw.power_ratio; + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Hlw.cf1_voltage_pulse_length ) { + Settings.energy_voltage_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * Hlw.cf1_voltage_pulse_length ) / Hlw.voltage_ratio; + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Hlw.cf1_current_pulse_length) { + Settings.energy_current_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data)) * Hlw.cf1_current_pulse_length) / Hlw.current_ratio; + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg01(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_200_MSECOND: + HlwEvery200ms(); + break; + case FUNC_ENERGY_EVERY_SECOND: + HlwEverySecond(); + break; + case FUNC_COMMAND: + result = HlwCommand(); + break; + case FUNC_INIT: + HlwSnsInit(); + break; + case FUNC_PRE_INIT: + HlwDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_02_cse7766.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_02_cse7766.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_CSE7766 + + + + + + + +#define XNRG_02 2 + +#define CSE_MAX_INVALID_POWER 128 + +#define CSE_NOT_CALIBRATED 0xAA + +#define CSE_PULSES_NOT_INITIALIZED -1 + +#define CSE_PREF 1000 +#define CSE_UREF 100 + +#define CSE_BUFFER_SIZE 25 + +#include + +TasmotaSerial *CseSerial = nullptr; + +struct CSE { + long voltage_cycle = 0; + long current_cycle = 0; + long power_cycle = 0; + long power_cycle_first = 0; + long cf_pulses = 0; + long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; + + int byte_counter = 0; + uint8_t *rx_buffer = nullptr; + uint8_t power_invalid = 0; + bool received = false; +} Cse; + +void CseReceived(void) +{ + + + + + + + uint8_t header = Cse.rx_buffer[0]; + if ((header & 0xFC) == 0xFC) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware")); + return; + } + + + if (HLW_UREF_PULSE == Settings.energy_voltage_calibration) { + long voltage_coefficient = 191200; + if (CSE_NOT_CALIBRATED != header) { + voltage_coefficient = Cse.rx_buffer[2] << 16 | Cse.rx_buffer[3] << 8 | Cse.rx_buffer[4]; + } + Settings.energy_voltage_calibration = voltage_coefficient / CSE_UREF; + } + if (HLW_IREF_PULSE == Settings.energy_current_calibration) { + long current_coefficient = 16140; + if (CSE_NOT_CALIBRATED != header) { + current_coefficient = Cse.rx_buffer[8] << 16 | Cse.rx_buffer[9] << 8 | Cse.rx_buffer[10]; + } + Settings.energy_current_calibration = current_coefficient; + } + if (HLW_PREF_PULSE == Settings.energy_power_calibration) { + long power_coefficient = 5364000; + if (CSE_NOT_CALIBRATED != header) { + power_coefficient = Cse.rx_buffer[14] << 16 | Cse.rx_buffer[15] << 8 | Cse.rx_buffer[16]; + } + Settings.energy_power_calibration = power_coefficient / CSE_PREF; + } + + uint8_t adjustement = Cse.rx_buffer[20]; + Cse.voltage_cycle = Cse.rx_buffer[5] << 16 | Cse.rx_buffer[6] << 8 | Cse.rx_buffer[7]; + Cse.current_cycle = Cse.rx_buffer[11] << 16 | Cse.rx_buffer[12] << 8 | Cse.rx_buffer[13]; + Cse.power_cycle = Cse.rx_buffer[17] << 16 | Cse.rx_buffer[18] << 8 | Cse.rx_buffer[19]; + Cse.cf_pulses = Cse.rx_buffer[21] << 8 | Cse.rx_buffer[22]; + + if (Energy.power_on) { + if (adjustement & 0x40) { + Energy.voltage[0] = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)Cse.voltage_cycle; + } + if (adjustement & 0x10) { + Cse.power_invalid = 0; + if ((header & 0xF2) == 0xF2) { + Energy.active_power[0] = 0; + } else { + if (0 == Cse.power_cycle_first) { Cse.power_cycle_first = Cse.power_cycle; } + if (Cse.power_cycle_first != Cse.power_cycle) { + Cse.power_cycle_first = -1; + Energy.active_power[0] = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)Cse.power_cycle; + } else { + Energy.active_power[0] = 0; + } + } + } else { + if (Cse.power_invalid < Settings.param[P_CSE7766_INVALID_POWER]) { + Cse.power_invalid++; + } else { + Cse.power_cycle_first = 0; + Energy.active_power[0] = 0; + } + } + if (adjustement & 0x20) { + if (0 == Energy.active_power[0]) { + Energy.current[0] = 0; + } else { + Energy.current[0] = (float)Settings.energy_current_calibration / (float)Cse.current_cycle; + } + } + } else { + Cse.power_cycle_first = 0; + Energy.voltage[0] = 0; + Energy.active_power[0] = 0; + Energy.current[0] = 0; + } +} + +bool CseSerialInput(void) +{ + while (CseSerial->available()) { + yield(); + uint8_t serial_in_byte = CseSerial->read(); + + if (Cse.received) { + Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte; + if (24 == Cse.byte_counter) { + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, Cse.rx_buffer, 24); + + uint8_t checksum = 0; + for (uint32_t i = 2; i < 23; i++) { checksum += Cse.rx_buffer[i]; } + if (checksum == Cse.rx_buffer[23]) { + Energy.data_valid[0] = 0; + CseReceived(); + Cse.received = false; + return true; + } else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE)); + do { + memmove(Cse.rx_buffer, Cse.rx_buffer +1, 24); + Cse.byte_counter--; + } while ((Cse.byte_counter > 2) && (0x5A != Cse.rx_buffer[1])); + if (0x5A != Cse.rx_buffer[1]) { + Cse.received = false; + Cse.byte_counter = 0; + } + } + } + } else { + if ((0x5A == serial_in_byte) && (1 == Cse.byte_counter)) { + Cse.received = true; + } else { + Cse.byte_counter = 0; + } + Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte; + } + } +} + + + +void CseEverySecond(void) +{ + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { + Cse.voltage_cycle = 0; + Cse.current_cycle = 0; + Cse.power_cycle = 0; + } else { + long cf_frequency = 0; + + if (CSE_PULSES_NOT_INITIALIZED == Cse.cf_pulses_last_time) { + Cse.cf_pulses_last_time = Cse.cf_pulses; + } else { + if (Cse.cf_pulses < Cse.cf_pulses_last_time) { + cf_frequency = (65536 - Cse.cf_pulses_last_time) + Cse.cf_pulses; + } else { + cf_frequency = Cse.cf_pulses - Cse.cf_pulses_last_time; + } + if (cf_frequency && Energy.active_power[0]) { + unsigned long delta = (cf_frequency * Settings.energy_power_calibration) / 36; + + + + if (delta <= (4000*100/36) * 10 ) { + Cse.cf_pulses_last_time = Cse.cf_pulses; + Energy.kWhtoday_delta += delta; + } + else { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Load overflow")); + Cse.cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; + } + EnergyUpdateToday(); + } + } + } +} + +void CseSnsInit(void) +{ + + + CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], -1, 1); + if (CseSerial->begin(4800, 2)) { + if (CseSerial->hardwareSerial()) { + SetSerial(4800, TS_SERIAL_8E1); + ClaimSerial(); + } + if (0 == Settings.param[P_CSE7766_INVALID_POWER]) { + Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; + } + Cse.power_invalid = Settings.param[P_CSE7766_INVALID_POWER]; + } else { + energy_flg = ENERGY_NONE; + } +} + +void CseDrvInit(void) +{ + Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE)); + if (Cse.rx_buffer != nullptr) { + + if (pin[GPIO_CSE7766_RX] < 99) { + energy_flg = XNRG_02; + } + } +} + +bool CseCommand(void) +{ + bool serviced = true; + + if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Cse.power_cycle) { + Settings.energy_power_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.power_cycle) / CSE_PREF; + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Cse.voltage_cycle) { + Settings.energy_voltage_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.voltage_cycle) / CSE_UREF; + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Cse.current_cycle) { + Settings.energy_current_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.current_cycle) / 1000; + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg02(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_LOOP: + if (CseSerial) { CseSerialInput(); } + break; + case FUNC_ENERGY_EVERY_SECOND: + CseEverySecond(); + break; + case FUNC_COMMAND: + result = CseCommand(); + break; + case FUNC_INIT: + CseSnsInit(); + break; + case FUNC_PRE_INIT: + CseDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_03_pzem004t.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_03_pzem004t.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_PZEM004T +# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_03_pzem004t.ino" +#define XNRG_03 3 + +const uint32_t PZEM_STABILIZE = 30; + +#include + +TasmotaSerial *PzemSerial = nullptr; + +#define PZEM_VOLTAGE (uint8_t)0xB0 +#define RESP_VOLTAGE (uint8_t)0xA0 + +#define PZEM_CURRENT (uint8_t)0xB1 +#define RESP_CURRENT (uint8_t)0xA1 + +#define PZEM_POWER (uint8_t)0xB2 +#define RESP_POWER (uint8_t)0xA2 + +#define PZEM_ENERGY (uint8_t)0xB3 +#define RESP_ENERGY (uint8_t)0xA3 + +#define PZEM_SET_ADDRESS (uint8_t)0xB4 +#define RESP_SET_ADDRESS (uint8_t)0xA4 + +#define PZEM_POWER_ALARM (uint8_t)0xB5 +#define RESP_POWER_ALARM (uint8_t)0xA5 + +#define PZEM_DEFAULT_READ_TIMEOUT 500 + + + +struct PZEM { + float energy = 0; + float last_energy = 0; + uint8_t send_retry = 0; + uint8_t read_state = 0; + uint8_t phase = 0; + uint8_t address = 0; +} Pzem; + +struct PZEMCommand { + uint8_t command; + uint8_t addr[4]; + uint8_t data; + uint8_t crc; +}; + +uint8_t PzemCrc(uint8_t *data) +{ + uint16_t crc = 0; + for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) { + crc += *data++; + } + return (uint8_t)(crc & 0xFF); +} + +void PzemSend(uint8_t cmd) +{ + PZEMCommand pzem; + + pzem.command = cmd; + pzem.addr[0] = 192; + pzem.addr[1] = 168; + pzem.addr[2] = 1; + pzem.addr[3] = ((PZEM_SET_ADDRESS == cmd) && Pzem.address) ? Pzem.address : 1 + Pzem.phase; + pzem.data = 0; + + uint8_t *bytes = (uint8_t*)&pzem; + pzem.crc = PzemCrc(bytes); + + PzemSerial->flush(); + PzemSerial->write(bytes, sizeof(pzem)); + + Pzem.address = 0; +} + +bool PzemReceiveReady(void) +{ + return PzemSerial->available() >= (int)sizeof(PZEMCommand); +} + +bool PzemRecieve(uint8_t resp, float *data) +{ +# 124 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_03_pzem004t.ino" + uint8_t buffer[sizeof(PZEMCommand)] = { 0 }; + + unsigned long start = millis(); + uint8_t len = 0; + while ((len < sizeof(PZEMCommand)) && (millis() - start < PZEM_DEFAULT_READ_TIMEOUT)) { + if (PzemSerial->available() > 0) { + uint8_t c = (uint8_t)PzemSerial->read(); + if (!len && ((c & 0xF8) != 0xA0)) { + continue; + } + buffer[len++] = c; + } + } + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, len); + + if (len != sizeof(PZEMCommand)) { + + return false; + } + if (buffer[6] != PzemCrc(buffer)) { + + return false; + } + if (buffer[0] != resp) { + + return false; + } + + switch (resp) { + case RESP_VOLTAGE: + *data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 10.0); + break; + case RESP_CURRENT: + *data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 100.0); + break; + case RESP_POWER: + *data = (float)(buffer[1] << 8) + buffer[2]; + break; + case RESP_ENERGY: + *data = (float)((uint32_t)buffer[1] << 16) + ((uint16_t)buffer[2] << 8) + buffer[3]; + break; + } + return true; +} + + + +const uint8_t pzem_commands[] { PZEM_SET_ADDRESS, PZEM_VOLTAGE, PZEM_CURRENT, PZEM_POWER, PZEM_ENERGY }; +const uint8_t pzem_responses[] { RESP_SET_ADDRESS, RESP_VOLTAGE, RESP_CURRENT, RESP_POWER, RESP_ENERGY }; + +void PzemEvery250ms(void) +{ + bool data_ready = PzemReceiveReady(); + + if (data_ready) { + float value = 0; + if (PzemRecieve(pzem_responses[Pzem.read_state], &value)) { + Energy.data_valid[Pzem.phase] = 0; + switch (Pzem.read_state) { + case 1: + Energy.voltage[Pzem.phase] = value; + break; + case 2: + Energy.current[Pzem.phase] = value; + break; + case 3: + Energy.active_power[Pzem.phase] = value; + break; + case 4: + Pzem.energy += value; + if (Pzem.phase == Energy.phase_count -1) { + if (Pzem.energy > Pzem.last_energy) { + if (uptime > PZEM_STABILIZE) { + EnergyUpdateTotal(Pzem.energy, false); + } + Pzem.last_energy = Pzem.energy; + } + Pzem.energy = 0; + } + break; + } + Pzem.read_state++; + if (5 == Pzem.read_state) { + Pzem.read_state = 1; + } + + + } + } + + if (0 == Pzem.send_retry || data_ready) { + if (1 == Pzem.read_state) { + if (0 == Pzem.phase) { + Pzem.phase = Energy.phase_count -1; + } else { + Pzem.phase--; + } + + + } + + if (Pzem.address) { + Pzem.read_state = 0; + } + + Pzem.send_retry = 5; + PzemSend(pzem_commands[Pzem.read_state]); + } + else { + Pzem.send_retry--; + if ((Energy.phase_count > 1) && (0 == Pzem.send_retry) && (uptime < PZEM_STABILIZE)) { + Energy.phase_count--; + } + } +} + +void PzemSnsInit(void) +{ + + PzemSerial = new TasmotaSerial(pin[GPIO_PZEM004_RX], pin[GPIO_PZEM0XX_TX], 1); + if (PzemSerial->begin(9600)) { + if (PzemSerial->hardwareSerial()) { + ClaimSerial(); + } + Energy.phase_count = 3; + Pzem.phase = 0; + Pzem.read_state = 1; + } else { + energy_flg = ENERGY_NONE; + } +} + +void PzemDrvInit(void) +{ + if ((pin[GPIO_PZEM004_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { + energy_flg = XNRG_03; + } +} + +bool PzemCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4)) { + Pzem.address = XdrvMailbox.payload; + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg03(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (PzemSerial && (uptime > 4)) { PzemEvery250ms(); } + break; + case FUNC_COMMAND: + result = PzemCommand(); + break; + case FUNC_INIT: + PzemSnsInit(); + break; + case FUNC_PRE_INIT: + PzemDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_04_mcp39f501.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_04_mcp39f501.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_MCP39F501 + + + + + + + +#define XNRG_04 4 + +#define MCP_BAUDRATE 4800 +#define MCP_TIMEOUT 4 +#define MCP_CALIBRATION_TIMEOUT 2 + +#define MCP_CALIBRATE_POWER 0x001 +#define MCP_CALIBRATE_VOLTAGE 0x002 +#define MCP_CALIBRATE_CURRENT 0x004 +#define MCP_CALIBRATE_FREQUENCY 0x008 +#define MCP_SINGLE_WIRE_FLAG 0x100 + +#define MCP_START_FRAME 0xA5 +#define MCP_ACK_FRAME 0x06 +#define MCP_ERROR_NAK 0x15 +#define MCP_ERROR_CRC 0x51 + +#define MCP_SINGLE_WIRE 0xAB + +#define MCP_SET_ADDRESS 0x41 + +#define MCP_READ 0x4E +#define MCP_READ_16 0x52 +#define MCP_READ_32 0x44 + +#define MCP_WRITE 0x4D +#define MCP_WRITE_16 0x57 +#define MCP_WRITE_32 0x45 + +#define MCP_SAVE_REGISTERS 0x53 + +#define MCP_CALIBRATION_BASE 0x0028 +#define MCP_CALIBRATION_LEN 52 + +#define MCP_FREQUENCY_REF_BASE 0x0094 +#define MCP_FREQUENCY_GAIN_BASE 0x00AE +#define MCP_FREQUENCY_LEN 4 + +#define MCP_BUFFER_SIZE 60 + +#include +TasmotaSerial *McpSerial = nullptr; + +typedef struct mcp_cal_registers_type { + uint16_t gain_current_rms; + uint16_t gain_voltage_rms; + uint16_t gain_active_power; + uint16_t gain_reactive_power; + sint32_t offset_current_rms; + sint32_t offset_active_power; + sint32_t offset_reactive_power; + sint16_t dc_offset_current; + sint16_t phase_compensation; + uint16_t apparent_power_divisor; + + uint32_t system_configuration; + uint16_t dio_configuration; + uint32_t range; + + uint32_t calibration_current; + uint16_t calibration_voltage; + uint32_t calibration_active_power; + uint32_t calibration_reactive_power; + uint16_t accumulation_interval; +} mcp_cal_registers_type; + +char *mcp_buffer = nullptr; +unsigned long mcp_window = 0; +unsigned long mcp_kWhcounter = 0; +uint32_t mcp_system_configuration = 0x03000000; +uint32_t mcp_active_power; + + +uint32_t mcp_current_rms; +uint16_t mcp_voltage_rms; +uint16_t mcp_line_frequency; + +uint8_t mcp_address = 0; +uint8_t mcp_calibration_active = 0; +uint8_t mcp_init = 0; +uint8_t mcp_timeout = 0; +uint8_t mcp_calibrate = 0; +uint8_t mcp_byte_counter = 0; + + + + + + +uint8_t McpChecksum(uint8_t *data) +{ + uint8_t checksum = 0; + uint8_t offset = 0; + uint8_t len = data[1] -1; + + for (uint32_t i = offset; i < len; i++) { checksum += data[i]; } + return checksum; +} + +unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size) +{ + unsigned long result = 0; + unsigned long pow = 1; + + for (uint32_t i = 0; i < size; i++) { + result = result + (uint8_t)data[offset + i] * pow; + pow = pow * 256; + } + return result; +} + +void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size) +{ + for (uint32_t i = 0; i < size; i++) { + data[offset + i] = ((value >> (i * 8)) & 0xFF); + } +} + +void McpSend(uint8_t *data) +{ + if (mcp_timeout) { return; } + mcp_timeout = MCP_TIMEOUT; + + data[0] = MCP_START_FRAME; + data[data[1] -1] = McpChecksum(data); + + + + for (uint32_t i = 0; i < data[1]; i++) { + McpSerial->write(data[i]); + } +} + + + +void McpGetAddress(void) +{ + uint8_t data[] = { MCP_START_FRAME, 7, MCP_SET_ADDRESS, 0x00, 0x26, MCP_READ_16, 0x00 }; + + McpSend(data); +} + +void McpAddressReceive(void) +{ + + mcp_address = mcp_buffer[3]; +} + + + +void McpGetCalibration(void) +{ + if (mcp_calibration_active) { return; } + mcp_calibration_active = MCP_CALIBRATION_TIMEOUT; + + uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, (MCP_CALIBRATION_BASE >> 8) & 0xFF, MCP_CALIBRATION_BASE & 0xFF, MCP_READ, MCP_CALIBRATION_LEN, 0x00 }; + + McpSend(data); +} + +void McpParseCalibration(void) +{ + bool action = false; + mcp_cal_registers_type cal_registers; + + + cal_registers.gain_current_rms = McpExtractInt(mcp_buffer, 2, 2); + cal_registers.gain_voltage_rms = McpExtractInt(mcp_buffer, 4, 2); + cal_registers.gain_active_power = McpExtractInt(mcp_buffer, 6, 2); + cal_registers.gain_reactive_power = McpExtractInt(mcp_buffer, 8, 2); + cal_registers.offset_current_rms = McpExtractInt(mcp_buffer, 10, 4); + cal_registers.offset_active_power = McpExtractInt(mcp_buffer, 14, 4); + cal_registers.offset_reactive_power = McpExtractInt(mcp_buffer, 18, 4); + cal_registers.dc_offset_current = McpExtractInt(mcp_buffer, 22, 2); + cal_registers.phase_compensation = McpExtractInt(mcp_buffer, 24, 2); + cal_registers.apparent_power_divisor = McpExtractInt(mcp_buffer, 26, 2); + + cal_registers.system_configuration = McpExtractInt(mcp_buffer, 28, 4); + cal_registers.dio_configuration = McpExtractInt(mcp_buffer, 32, 2); + cal_registers.range = McpExtractInt(mcp_buffer, 34, 4); + + cal_registers.calibration_current = McpExtractInt(mcp_buffer, 38, 4); + cal_registers.calibration_voltage = McpExtractInt(mcp_buffer, 42, 2); + cal_registers.calibration_active_power = McpExtractInt(mcp_buffer, 44, 4); + cal_registers.calibration_reactive_power = McpExtractInt(mcp_buffer, 48, 4); + cal_registers.accumulation_interval = McpExtractInt(mcp_buffer, 52, 2); + + if (mcp_calibrate & MCP_CALIBRATE_POWER) { + cal_registers.calibration_active_power = Settings.energy_power_calibration; + if (McpCalibrationCalc(&cal_registers, 16)) { action = true; } + } + if (mcp_calibrate & MCP_CALIBRATE_VOLTAGE) { + cal_registers.calibration_voltage = Settings.energy_voltage_calibration; + if (McpCalibrationCalc(&cal_registers, 0)) { action = true; } + } + if (mcp_calibrate & MCP_CALIBRATE_CURRENT) { + cal_registers.calibration_current = Settings.energy_current_calibration; + if (McpCalibrationCalc(&cal_registers, 8)) { action = true; } + } + mcp_timeout = 0; + if (action) { McpSetCalibration(&cal_registers); } + + mcp_calibrate = 0; + + Settings.energy_power_calibration = cal_registers.calibration_active_power; + Settings.energy_voltage_calibration = cal_registers.calibration_voltage; + Settings.energy_current_calibration = cal_registers.calibration_current; + + mcp_system_configuration = cal_registers.system_configuration; + + if (mcp_system_configuration & MCP_SINGLE_WIRE_FLAG) { + mcp_system_configuration &= ~MCP_SINGLE_WIRE_FLAG; + McpSetSystemConfiguration(2); + } +} + +bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift) +{ + uint32_t measured; + uint32_t expected; + uint16_t *gain; + uint32_t new_gain; + + if (range_shift == 0) { + measured = mcp_voltage_rms; + expected = cal_registers->calibration_voltage; + gain = &(cal_registers->gain_voltage_rms); + } else if (range_shift == 8) { + measured = mcp_current_rms; + expected = cal_registers->calibration_current; + gain = &(cal_registers->gain_current_rms); + } else if (range_shift == 16) { + measured = mcp_active_power; + expected = cal_registers->calibration_active_power; + gain = &(cal_registers->gain_active_power); + } else { + return false; + } + + if (measured == 0) { + return false; + } + + uint32_t range = (cal_registers->range >> range_shift) & 0xFF; + +calc: + new_gain = (*gain) * expected / measured; + + if (new_gain < 25000) { + range++; + if (measured > 6) { + measured = measured / 2; + goto calc; + } + } + + if (new_gain > 55000) { + range--; + measured = measured * 2; + goto calc; + } + + *gain = new_gain; + uint32_t old_range = (cal_registers->range >> range_shift) & 0xFF; + cal_registers->range = cal_registers->range ^ (old_range << range_shift); + cal_registers->range = cal_registers->range | (range << range_shift); + + return true; +} + + + + + + +void McpSetCalibration(struct mcp_cal_registers_type *cal_registers) +{ + uint8_t data[7 + MCP_CALIBRATION_LEN + 2 + 1]; + + data[1] = sizeof(data); + data[2] = MCP_SET_ADDRESS; + data[3] = (MCP_CALIBRATION_BASE >> 8) & 0xFF; + data[4] = (MCP_CALIBRATION_BASE >> 0) & 0xFF; + + data[5] = MCP_WRITE; + data[6] = MCP_CALIBRATION_LEN; + + McpSetInt(cal_registers->gain_current_rms, data, 0+7, 2); + McpSetInt(cal_registers->gain_voltage_rms, data, 2+7, 2); + McpSetInt(cal_registers->gain_active_power, data, 4+7, 2); + McpSetInt(cal_registers->gain_reactive_power, data, 6+7, 2); + McpSetInt(cal_registers->offset_current_rms, data, 8+7, 4); + McpSetInt(cal_registers->offset_active_power, data, 12+7, 4); + McpSetInt(cal_registers->offset_reactive_power, data, 16+7, 4); + McpSetInt(cal_registers->dc_offset_current, data, 20+7, 2); + McpSetInt(cal_registers->phase_compensation, data, 22+7, 2); + McpSetInt(cal_registers->apparent_power_divisor, data, 24+7, 2); + + McpSetInt(cal_registers->system_configuration, data, 26+7, 4); + McpSetInt(cal_registers->dio_configuration, data, 30+7, 2); + McpSetInt(cal_registers->range, data, 32+7, 4); + + McpSetInt(cal_registers->calibration_current, data, 36+7, 4); + McpSetInt(cal_registers->calibration_voltage, data, 40+7, 2); + McpSetInt(cal_registers->calibration_active_power, data, 42+7, 4); + McpSetInt(cal_registers->calibration_reactive_power, data, 46+7, 4); + McpSetInt(cal_registers->accumulation_interval, data, 50+7, 2); + + data[MCP_CALIBRATION_LEN+7] = MCP_SAVE_REGISTERS; + data[MCP_CALIBRATION_LEN+8] = mcp_address; + + McpSend(data); +} + + + +void McpSetSystemConfiguration(uint16 interval) +{ + + uint8_t data[17]; + + data[ 1] = sizeof(data); + data[ 2] = MCP_SET_ADDRESS; + data[ 3] = 0x00; + data[ 4] = 0x42; + data[ 5] = MCP_WRITE_32; + data[ 6] = (mcp_system_configuration >> 24) & 0xFF; + data[ 7] = (mcp_system_configuration >> 16) & 0xFF; + data[ 8] = (mcp_system_configuration >> 8) & 0xFF; + data[ 9] = (mcp_system_configuration >> 0) & 0xFF; + data[10] = MCP_SET_ADDRESS; + data[11] = 0x00; + data[12] = 0x5A; + data[13] = MCP_WRITE_16; + data[14] = (interval >> 8) & 0xFF; + data[15] = (interval >> 0) & 0xFF; + + McpSend(data); +} + + + +void McpGetFrequency(void) +{ + if (mcp_calibration_active) { return; } + mcp_calibration_active = MCP_CALIBRATION_TIMEOUT; + + uint8_t data[] = { MCP_START_FRAME, 11, MCP_SET_ADDRESS, (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF, MCP_FREQUENCY_REF_BASE & 0xFF, MCP_READ_16, + MCP_SET_ADDRESS, (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF, MCP_FREQUENCY_GAIN_BASE & 0xFF, MCP_READ_16, 0x00 }; + + McpSend(data); +} + +void McpParseFrequency(void) +{ + + uint16_t line_frequency_ref = mcp_buffer[2] * 256 + mcp_buffer[3]; + uint16_t gain_line_frequency = mcp_buffer[4] * 256 + mcp_buffer[5]; + + if (mcp_calibrate & MCP_CALIBRATE_FREQUENCY) { + line_frequency_ref = Settings.energy_frequency_calibration; + + if ((0xFFFF == mcp_line_frequency) || (0 == gain_line_frequency)) { + mcp_line_frequency = 50000; + gain_line_frequency = 0x8000; + } + gain_line_frequency = gain_line_frequency * line_frequency_ref / mcp_line_frequency; + + mcp_timeout = 0; + McpSetFrequency(line_frequency_ref, gain_line_frequency); + } + + Settings.energy_frequency_calibration = line_frequency_ref; + + mcp_calibrate = 0; +} + +void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency) +{ + + uint8_t data[17]; + + data[ 1] = sizeof(data); + data[ 2] = MCP_SET_ADDRESS; + data[ 3] = (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF; + data[ 4] = (MCP_FREQUENCY_REF_BASE >> 0) & 0xFF; + + data[ 5] = MCP_WRITE_16; + data[ 6] = (line_frequency_ref >> 8) & 0xFF; + data[ 7] = (line_frequency_ref >> 0) & 0xFF; + + data[ 8] = MCP_SET_ADDRESS; + data[ 9] = (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF; + data[10] = (MCP_FREQUENCY_GAIN_BASE >> 0) & 0xFF; + + data[11] = MCP_WRITE_16; + data[12] = (gain_line_frequency >> 8) & 0xFF; + data[13] = (gain_line_frequency >> 0) & 0xFF; + + data[14] = MCP_SAVE_REGISTERS; + data[15] = mcp_address; + + McpSend(data); +} + + + +void McpGetData(void) +{ + uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, 0x00, 0x04, MCP_READ, 22, 0x00 }; + + McpSend(data); +} + +void McpParseData(void) +{ + + + + + + mcp_current_rms = McpExtractInt(mcp_buffer, 2, 4); + mcp_voltage_rms = McpExtractInt(mcp_buffer, 6, 2); + mcp_active_power = McpExtractInt(mcp_buffer, 8, 4); + + + mcp_line_frequency = McpExtractInt(mcp_buffer, 22, 2); + + if (Energy.power_on) { + Energy.data_valid[0] = 0; + Energy.frequency[0] = (float)mcp_line_frequency / 1000; + Energy.voltage[0] = (float)mcp_voltage_rms / 10; + Energy.active_power[0] = (float)mcp_active_power / 100; + if (0 == Energy.active_power[0]) { + Energy.current[0] = 0; + } else { + Energy.current[0] = (float)mcp_current_rms / 10000; + } + } else { + Energy.data_valid[0] = ENERGY_WATCHDOG; + } +} + + + +void McpSerialInput(void) +{ + while ((McpSerial->available()) && (mcp_byte_counter < MCP_BUFFER_SIZE)) { + yield(); + mcp_buffer[mcp_byte_counter++] = McpSerial->read(); + mcp_window = millis(); + } + + + if ((mcp_byte_counter) && (millis() - mcp_window > (24000 / MCP_BAUDRATE) +1)) { + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t*)mcp_buffer, mcp_byte_counter); + + if (MCP_BUFFER_SIZE == mcp_byte_counter) { + + } + else if (1 == mcp_byte_counter) { + if (MCP_ERROR_CRC == mcp_buffer[0]) { + + mcp_timeout = 0; + } + else if (MCP_ERROR_NAK == mcp_buffer[0]) { + + mcp_timeout = 0; + } + } + else if (MCP_ACK_FRAME == mcp_buffer[0]) { + if (mcp_byte_counter == mcp_buffer[1]) { + + if (McpChecksum((uint8_t *)mcp_buffer) != mcp_buffer[mcp_byte_counter -1]) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); + } else { + if (5 == mcp_buffer[1]) { McpAddressReceive(); } + if (25 == mcp_buffer[1]) { McpParseData(); } + if (MCP_CALIBRATION_LEN + 3 == mcp_buffer[1]) { McpParseCalibration(); } + if (MCP_FREQUENCY_LEN + 3 == mcp_buffer[1]) { McpParseFrequency(); } + } + + } + mcp_timeout = 0; + } + else if (MCP_SINGLE_WIRE == mcp_buffer[0]) { + mcp_timeout = 0; + } + + mcp_byte_counter = 0; + McpSerial->flush(); + } +} + + + +void McpEverySecond(void) +{ + if (Energy.data_valid[0] > ENERGY_WATCHDOG) { + mcp_voltage_rms = 0; + mcp_current_rms = 0; + mcp_active_power = 0; + mcp_line_frequency = 0; + } + + if (mcp_active_power) { + Energy.kWhtoday_delta += ((mcp_active_power * 10) / 36); + EnergyUpdateToday(); + } + + if (mcp_timeout) { + mcp_timeout--; + } + else if (mcp_calibration_active) { + mcp_calibration_active--; + } + else if (mcp_init) { + if (2 == mcp_init) { + McpGetCalibration(); + } + else if (1 == mcp_init) { + McpGetFrequency(); + } + mcp_init--; + } + else if (!mcp_address) { + McpGetAddress(); + } + else { + McpGetData(); + } +} + +void McpSnsInit(void) +{ + + McpSerial = new TasmotaSerial(pin[GPIO_MCP39F5_RX], pin[GPIO_MCP39F5_TX], 1); + if (McpSerial->begin(MCP_BAUDRATE)) { + if (McpSerial->hardwareSerial()) { + ClaimSerial(); + mcp_buffer = serial_in_buffer; + } else { + mcp_buffer = (char*)(malloc(MCP_BUFFER_SIZE)); + } + DigitalWrite(GPIO_MCP39F5_RST, 1); + } else { + energy_flg = ENERGY_NONE; + } +} + +void McpDrvInit(void) +{ + if ((pin[GPIO_MCP39F5_RX] < 99) && (pin[GPIO_MCP39F5_TX] < 99)) { + if (pin[GPIO_MCP39F5_RST] < 99) { + pinMode(pin[GPIO_MCP39F5_RST], OUTPUT); + digitalWrite(pin[GPIO_MCP39F5_RST], 0); + } + mcp_calibrate = 0; + mcp_timeout = 2; + mcp_init = 2; + energy_flg = XNRG_04; + } +} + +bool McpCommand(void) +{ + bool serviced = true; + unsigned long value = 0; + + if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && mcp_active_power) { + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 100); + if ((value > 100) && (value < 200000)) { + Settings.energy_power_calibration = value; + mcp_calibrate |= MCP_CALIBRATE_POWER; + McpGetCalibration(); + } + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && mcp_voltage_rms) { + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); + if ((value > 1000) && (value < 2600)) { + Settings.energy_voltage_calibration = value; + mcp_calibrate |= MCP_CALIBRATE_VOLTAGE; + McpGetCalibration(); + } + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && mcp_current_rms) { + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); + if ((value > 100) && (value < 80000)) { + Settings.energy_current_calibration = value; + mcp_calibrate |= MCP_CALIBRATE_CURRENT; + McpGetCalibration(); + } + } + } + else if (CMND_FREQUENCYSET == Energy.command_code) { + if (XdrvMailbox.data_len && mcp_line_frequency) { + value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 1000); + if ((value > 45000) && (value < 65000)) { + Settings.energy_frequency_calibration = value; + mcp_calibrate |= MCP_CALIBRATE_FREQUENCY; + McpGetFrequency(); + } + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg04(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_LOOP: + if (McpSerial) { McpSerialInput(); } + break; + case FUNC_ENERGY_EVERY_SECOND: + if (McpSerial) { McpEverySecond(); } + break; + case FUNC_COMMAND: + result = McpCommand(); + break; + case FUNC_INIT: + McpSnsInit(); + break; + case FUNC_PRE_INIT: + McpDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_05_pzem_ac.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_05_pzem_ac.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_PZEM_AC +# 33 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_05_pzem_ac.ino" +#define XNRG_05 5 + +const uint8_t PZEM_AC_DEVICE_ADDRESS = 0x01; +const uint32_t PZEM_AC_STABILIZE = 30; + +#include +TasmotaModbus *PzemAcModbus; + +struct PZEMAC { + float energy = 0; + float last_energy = 0; + uint8_t send_retry = 0; + uint8_t phase = 0; + uint8_t address = 0; + uint8_t address_step = ADDR_IDLE; +} PzemAc; + +void PzemAcEverySecond(void) +{ + bool data_ready = PzemAcModbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[30]; + + uint8_t registers = 10; + if (ADDR_RECEIVE == PzemAc.address_step) { + registers = 2; + PzemAc.address_step--; + } + uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, registers); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemAcModbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); + } else { + Energy.data_valid[PzemAc.phase] = 0; + if (10 == registers) { + + + + + + Energy.voltage[PzemAc.phase] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; + Energy.current[PzemAc.phase] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; + Energy.active_power[PzemAc.phase] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; + Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; + Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; + + PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); + if (PzemAc.phase == Energy.phase_count -1) { + if (PzemAc.energy > PzemAc.last_energy) { + if (uptime > PZEM_AC_STABILIZE) { + EnergyUpdateTotal(PzemAc.energy, false); + } + PzemAc.last_energy = PzemAc.energy; + } + PzemAc.energy = 0; + } + + } + } + } + + if (0 == PzemAc.send_retry || data_ready) { + if (0 == PzemAc.phase) { + PzemAc.phase = Energy.phase_count -1; + } else { + PzemAc.phase--; + } + PzemAc.send_retry = ENERGY_WATCHDOG; + if (ADDR_SEND == PzemAc.address_step) { + PzemAcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemAc.address); + PzemAc.address_step--; + } else { + PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, 0x04, 0, 10); + } + } + else { + PzemAc.send_retry--; + if ((Energy.phase_count > 1) && (0 == PzemAc.send_retry) && (uptime < PZEM_AC_STABILIZE)) { + Energy.phase_count--; + } + } +} + +void PzemAcSnsInit(void) +{ + PzemAcModbus = new TasmotaModbus(pin[GPIO_PZEM016_RX], pin[GPIO_PZEM0XX_TX]); + uint8_t result = PzemAcModbus->Begin(9600); + if (result) { + if (2 == result) { ClaimSerial(); } + Energy.phase_count = 3; + PzemAc.phase = 0; + } else { + energy_flg = ENERGY_NONE; + } +} + +void PzemAcDrvInit(void) +{ + if ((pin[GPIO_PZEM016_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { + energy_flg = XNRG_05; + } +} + +bool PzemAcCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + PzemAc.address = XdrvMailbox.payload; + PzemAc.address_step = ADDR_SEND; + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg05(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { PzemAcEverySecond(); } + break; + case FUNC_COMMAND: + result = PzemAcCommand(); + break; + case FUNC_INIT: + PzemAcSnsInit(); + break; + case FUNC_PRE_INIT: + PzemAcDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_06_pzem_dc.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_06_pzem_dc.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_PZEM_DC +# 32 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_06_pzem_dc.ino" +#define XNRG_06 6 + +const uint8_t PZEM_DC_DEVICE_ADDRESS = 0x01; +const uint32_t PZEM_DC_STABILIZE = 30; + +#include +TasmotaModbus *PzemDcModbus; + +struct PZEMDC { + float energy = 0; + float last_energy = 0; + uint8_t send_retry = 0; + uint8_t channel = 0; + uint8_t address = 0; + uint8_t address_step = ADDR_IDLE; +} PzemDc; + +void PzemDcEverySecond(void) +{ + bool data_ready = PzemDcModbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[26]; + + uint8_t registers = 8; + if (ADDR_RECEIVE == PzemDc.address_step) { + registers = 2; + PzemDc.address_step--; + } + uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, registers); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemDcModbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error); + } else { + Energy.data_valid[PzemDc.channel] = 0; + if (8 == registers) { + + + + + + Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; + Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; + Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; + + PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); + if (PzemDc.channel == Energy.phase_count -1) { + if (PzemDc.energy > PzemDc.last_energy) { + if (uptime > PZEM_DC_STABILIZE) { + EnergyUpdateTotal(PzemDc.energy, false); + } + PzemDc.last_energy = PzemDc.energy; + } + PzemDc.energy = 0; + } + } + } + } + + if (0 == PzemDc.send_retry || data_ready) { + if (0 == PzemDc.channel) { + PzemDc.channel = Energy.phase_count -1; + } else { + PzemDc.channel--; + } + PzemDc.send_retry = ENERGY_WATCHDOG; + if (ADDR_SEND == PzemDc.address_step) { + PzemDcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemDc.address); + PzemDc.address_step--; + } else { + PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, 0x04, 0, 8); + } + } + else { + PzemDc.send_retry--; + if ((Energy.phase_count > 1) && (0 == PzemDc.send_retry) && (uptime < PZEM_DC_STABILIZE)) { + Energy.phase_count--; + } + } +} + +void PzemDcSnsInit(void) +{ + PzemDcModbus = new TasmotaModbus(pin[GPIO_PZEM017_RX], pin[GPIO_PZEM0XX_TX]); + uint8_t result = PzemDcModbus->Begin(9600, 2); + if (result) { + if (2 == result) { ClaimSerial(); } + Energy.type_dc = true; + Energy.phase_count = 3; + PzemDc.channel = 0; + } else { + energy_flg = ENERGY_NONE; + } +} + +void PzemDcDrvInit(void) +{ + if ((pin[GPIO_PZEM017_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { + energy_flg = XNRG_06; + } +} + +bool PzemDcCommand(void) +{ + bool serviced = true; + + if (CMND_MODULEADDRESS == Energy.command_code) { + PzemDc.address = XdrvMailbox.payload; + PzemDc.address_step = ADDR_SEND; + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg06(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { PzemDcEverySecond(); } + break; + case FUNC_COMMAND: + result = PzemDcCommand(); + break; + case FUNC_INIT: + PzemDcSnsInit(); + break; + case FUNC_PRE_INIT: + PzemDcDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_07_ade7953.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_07_ade7953.ino" +#ifdef USE_I2C +#ifdef USE_ENERGY_SENSOR +#ifdef USE_ADE7953 +# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_07_ade7953.ino" +#define XNRG_07 7 +#define XI2C_07 7 + +#define ADE7953_PREF 1540 +#define ADE7953_UREF 26000 +#define ADE7953_IREF 10000 + +#define ADE7953_ADDR 0x38 + +const uint16_t Ade7953Registers[] { + 0x31B, + 0x313, + 0x311, + 0x315, + 0x31A, + 0x312, + 0x310, + 0x314, + 0x31C, + 0x10E +}; + +struct Ade7953 { + uint32_t voltage_rms = 0; + uint32_t period = 0; + uint32_t current_rms[2] = { 0, 0 }; + uint32_t active_power[2] = { 0, 0 }; + uint8_t init_step = 0; +} Ade7953; + +int Ade7953RegSize(uint16_t reg) +{ + int size = 0; + switch ((reg >> 8) & 0x0F) { + case 0x03: + size++; + case 0x02: + size++; + case 0x01: + size++; + case 0x00: + case 0x07: + case 0x08: + size++; + } + return size; +} + +void Ade7953Write(uint16_t reg, uint32_t val) +{ + int size = Ade7953RegSize(reg); + if (size) { + Wire.beginTransmission(ADE7953_ADDR); + Wire.write((reg >> 8) & 0xFF); + Wire.write(reg & 0xFF); + while (size--) { + Wire.write((val >> (8 * size)) & 0xFF); + } + Wire.endTransmission(); + delayMicroseconds(5); + } +} + +int32_t Ade7953Read(uint16_t reg) +{ + uint32_t response = 0; + + int size = Ade7953RegSize(reg); + if (size) { + Wire.beginTransmission(ADE7953_ADDR); + Wire.write((reg >> 8) & 0xFF); + Wire.write(reg & 0xFF); + Wire.endTransmission(0); + Wire.requestFrom(ADE7953_ADDR, size); + if (size <= Wire.available()) { + for (uint32_t i = 0; i < size; i++) { + response = response << 8 | Wire.read(); + } + } + } + return response; +} + +void Ade7953Init(void) +{ + Ade7953Write(0x102, 0x0004); + Ade7953Write(0x0FE, 0x00AD); + Ade7953Write(0x120, 0x0030); +} + +void Ade7953GetData(void) +{ + int32_t reg[2][4]; + for (uint32_t i = 0; i < sizeof(Ade7953Registers)/sizeof(uint16_t); i++) { + int32_t value = Ade7953Read(Ade7953Registers[i]); + if (8 == i) { + Ade7953.voltage_rms = value; + } else if (9 == i) { + Ade7953.period = value; + } else { + reg[i >> 2][i &3] = value; + } + } + AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: %d, %d, [%d, %d, %d, %d], [%d, %d, %d, %d]"), + Ade7953.voltage_rms, Ade7953.period, + reg[0][0], reg[0][1], reg[0][2], reg[0][3], + reg[1][0], reg[1][1], reg[1][2], reg[1][3]); + + uint32_t apparent_power[2] = { 0, 0 }; + uint32_t reactive_power[2] = { 0, 0 }; + + for (uint32_t channel = 0; channel < 2; channel++) { + Ade7953.current_rms[channel] = reg[channel][0]; + if (Ade7953.current_rms[channel] < 2000) { + Ade7953.current_rms[channel] = 0; + Ade7953.active_power[channel] = 0; + } else { + Ade7953.active_power[channel] = abs(reg[channel][1]); + apparent_power[channel] = abs(reg[channel][2]); + reactive_power[channel] = abs(reg[channel][3]); + } + } + + uint32_t current_rms_sum = Ade7953.current_rms[0] + Ade7953.current_rms[1]; + uint32_t active_power_sum = Ade7953.active_power[0] + Ade7953.active_power[1]; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ADE: U %d, C %d, I %d + %d = %d, P %d + %d = %d"), + Ade7953.voltage_rms, Ade7953.period, + Ade7953.current_rms[0], Ade7953.current_rms[1], current_rms_sum, + Ade7953.active_power[0], Ade7953.active_power[1], active_power_sum); + + if (Energy.power_on) { + Energy.voltage[0] = (float)Ade7953.voltage_rms / Settings.energy_voltage_calibration; + Energy.frequency[0] = 223750.0f / ( (float)Ade7953.period + 1); + + for (uint32_t channel = 0; channel < 2; channel++) { + Energy.data_valid[channel] = 0; + Energy.active_power[channel] = (float)Ade7953.active_power[channel] / (Settings.energy_power_calibration / 10); + Energy.reactive_power[channel] = (float)reactive_power[channel] / (Settings.energy_power_calibration / 10); + Energy.apparent_power[channel] = (float)apparent_power[channel] / (Settings.energy_power_calibration / 10); + if (0 == Energy.active_power[channel]) { + Energy.current[channel] = 0; + } else { + Energy.current[channel] = (float)Ade7953.current_rms[channel] / (Settings.energy_current_calibration * 10); + } + } + } else { + Energy.data_valid[0] = ENERGY_WATCHDOG; + Energy.data_valid[1] = ENERGY_WATCHDOG; + } + + if (active_power_sum) { + Energy.kWhtoday_delta += ((active_power_sum * (100000 / (Settings.energy_power_calibration / 10))) / 3600); + EnergyUpdateToday(); + } +} + +void Ade7953EnergyEverySecond(void) +{ + if (Ade7953.init_step) { + if (1 == Ade7953.init_step) { + Ade7953Init(); + } + Ade7953.init_step--; + } else { + Ade7953GetData(); + } +} + +void Ade7953DrvInit(void) +{ + if (pin[GPIO_ADE7953_IRQ] < 99) { + delay(100); + if (I2cSetDevice(ADE7953_ADDR)) { + if (HLW_PREF_PULSE == Settings.energy_power_calibration) { + Settings.energy_power_calibration = ADE7953_PREF; + Settings.energy_voltage_calibration = ADE7953_UREF; + Settings.energy_current_calibration = ADE7953_IREF; + } + I2cSetActiveFound(ADE7953_ADDR, "ADE7953"); + Ade7953.init_step = 2; + + Energy.phase_count = 2; + Energy.voltage_common = true; + + energy_flg = XNRG_07; + } + } +} + +bool Ade7953Command(void) +{ + bool serviced = true; + + uint32_t channel = (2 == XdrvMailbox.index) ? 1 : 0; + uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); + + if (CMND_POWERCAL == Energy.command_code) { + if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_PREF; } + + } + else if (CMND_VOLTAGECAL == Energy.command_code) { + if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_UREF; } + + } + else if (CMND_CURRENTCAL == Energy.command_code) { + if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_IREF; } + + } + else if (CMND_POWERSET == Energy.command_code) { + if (XdrvMailbox.data_len && Ade7953.active_power[channel]) { + if ((value > 100) && (value < 200000)) { + Settings.energy_power_calibration = (Ade7953.active_power[channel] * 1000) / value; + } + } + } + else if (CMND_VOLTAGESET == Energy.command_code) { + if (XdrvMailbox.data_len && Ade7953.voltage_rms) { + if ((value > 10000) && (value < 26000)) { + Settings.energy_voltage_calibration = (Ade7953.voltage_rms * 100) / value; + } + } + } + else if (CMND_CURRENTSET == Energy.command_code) { + if (XdrvMailbox.data_len && Ade7953.current_rms[channel]) { + if ((value > 2000) && (value < 1000000)) { + Settings.energy_current_calibration = ((Ade7953.current_rms[channel] * 100) / value) * 100; + } + } + } + else serviced = false; + + return serviced; +} + + + + + +bool Xnrg07(uint8_t function) +{ + if (!I2cEnabled(XI2C_07)) { return false; } + + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + Ade7953EnergyEverySecond(); + break; + case FUNC_COMMAND: + result = Ade7953Command(); + break; + case FUNC_PRE_INIT: + Ade7953DrvInit(); + break; + } + return result; +} + +#endif +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_08_sdm120.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_08_sdm120.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SDM120 + + + + + + +#define XNRG_08 8 + + +#ifndef SDM120_SPEED + #define SDM120_SPEED 2400 +#endif + +#ifndef SDM120_ADDR + #define SDM120_ADDR 1 +#endif + +#include +TasmotaModbus *Sdm120Modbus; + +const uint8_t sdm120_table = 8; +const uint8_t sdm220_table = 13; + +const uint16_t sdm120_start_addresses[] { + 0x0000, + 0x0006, + 0x000C, + 0x0012, + 0x0018, + 0x001E, + 0x0046, + 0x0156, + + 0X0048, + 0X004A, + 0X004C, + 0X004E, + 0X0024 +}; + +struct SDM120 { + float total_active = 0; + float import_active = NAN; + float import_reactive = 0; + float export_reactive = 0; + float phase_angle = 0; + uint8_t read_state = 0; + uint8_t send_retry = 0; + uint8_t start_address_count = sdm220_table; +} Sdm120; + + + +void SDM120Every250ms(void) +{ + bool data_ready = Sdm120Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + + uint32_t error = Sdm120Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm120Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM120 error %d"), error); + } else { + Energy.data_valid[0] = 0; + + + + + float value; + ((uint8_t*)&value)[3] = buffer[3]; + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Sdm120.read_state) { + case 0: + Energy.voltage[0] = value; + break; + + case 1: + Energy.current[0] = value; + break; + + case 2: + Energy.active_power[0] = value; + break; + + case 3: + Energy.apparent_power[0] = value; + break; + + case 4: + Energy.reactive_power[0] = value; + break; + + case 5: + Energy.power_factor[0] = value; + break; + + case 6: + Energy.frequency[0] = value; + break; + + case 7: + Sdm120.total_active = value; + break; + + case 8: + Sdm120.import_active = value; + break; + + case 9: + Energy.export_active = value; + break; + + case 10: + Sdm120.import_reactive = value; + break; + + case 11: + Sdm120.export_reactive = value; + break; + + case 12: + Sdm120.phase_angle = value; + break; + } + + Sdm120.read_state++; + if (Sdm120.read_state == Sdm120.start_address_count) { + Sdm120.read_state = 0; + + if (Sdm120.start_address_count > sdm120_table) { + if (!isnan(Sdm120.import_active)) { + Sdm120.total_active = Sdm120.import_active; + } else { + Sdm120.start_address_count = sdm120_table; + } + } + EnergyUpdateTotal(Sdm120.total_active, true); + } + } + } + + if (0 == Sdm120.send_retry || data_ready) { + Sdm120.send_retry = 5; + Sdm120Modbus->Send(SDM120_ADDR, 0x04, sdm120_start_addresses[Sdm120.read_state], 2); + } else { + Sdm120.send_retry--; + } +} + +void Sdm120SnsInit(void) +{ + Sdm120Modbus = new TasmotaModbus(pin[GPIO_SDM120_RX], pin[GPIO_SDM120_TX]); + uint8_t result = Sdm120Modbus->Begin(SDM120_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Sdm120DrvInit(void) +{ + if ((pin[GPIO_SDM120_RX] < 99) && (pin[GPIO_SDM120_TX] < 99)) { + energy_flg = XNRG_08; + } +} + +void Sdm220Reset(void) +{ + if (isnan(Sdm120.import_active)) { return; } + + Sdm120.import_active = 0; + Sdm120.import_reactive = 0; + Sdm120.export_reactive = 0; + Sdm120.phase_angle = 0; +} + +#ifdef USE_WEBSERVER +const char HTTP_ENERGY_SDM220[] PROGMEM = + "{s}" D_IMPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" + "{s}" D_EXPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" + "{s}" D_PHASE_ANGLE "{m}%s " D_UNIT_ANGLE "{e}"; +#endif + +void Sdm220Show(bool json) +{ + if (isnan(Sdm120.import_active)) { return; } + + char import_active_chr[FLOATSZ]; + dtostrfd(Sdm120.import_active, Settings.flag2.energy_resolution, import_active_chr); + char import_reactive_chr[FLOATSZ]; + dtostrfd(Sdm120.import_reactive, Settings.flag2.energy_resolution, import_reactive_chr); + char export_reactive_chr[FLOATSZ]; + dtostrfd(Sdm120.export_reactive, Settings.flag2.energy_resolution, export_reactive_chr); + char phase_angle_chr[FLOATSZ]; + dtostrfd(Sdm120.phase_angle, 2, phase_angle_chr); + + if (json) { + ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT_ACTIVE "\":%s,\"" D_JSON_IMPORT_REACTIVE "\":%s,\"" D_JSON_EXPORT_REACTIVE "\":%s,\"" D_JSON_PHASE_ANGLE "\":%s"), + import_active_chr, import_reactive_chr, export_reactive_chr, phase_angle_chr); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_ENERGY_SDM220, import_reactive_chr, export_reactive_chr, phase_angle_chr); +#endif + } +} + + + + + +bool Xnrg08(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { SDM120Every250ms(); } + break; + case FUNC_JSON_APPEND: + Sdm220Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sdm220Show(0); + break; +#endif + case FUNC_ENERGY_RESET: + Sdm220Reset(); + break; + case FUNC_INIT: + Sdm120SnsInit(); + break; + case FUNC_PRE_INIT: + Sdm120DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_09_dds2382.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_09_dds2382.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_DDS2382 + + + + + + +#define XNRG_09 9 + +#ifndef DDS2382_SPEED +#define DDS2382_SPEED 9600 +#endif +#ifndef DDS2382_ADDR +#define DDS2382_ADDR 1 +#endif + +#include +TasmotaModbus *Dds2382Modbus; + +uint8_t Dds2382_send_retry = 0; + +void Dds2382EverySecond(void) +{ + bool data_ready = Dds2382Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[46]; + + uint32_t error = Dds2382Modbus->ReceiveBuffer(buffer, 18); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Dds2382Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "DDS2382 response error %d"), error); + } else { + Energy.data_valid[0] = 0; +# 67 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_09_dds2382.ino" + Energy.voltage[0] = (float)((buffer[27] << 8) + buffer[28]) / 10.0; + Energy.current[0] = (float)((buffer[29] << 8) + buffer[30]) / 100.0; + Energy.active_power[0] = (float)((buffer[31] << 8) + buffer[32]); + Energy.reactive_power[0] = (float)((buffer[33] << 8) + buffer[34]); + Energy.power_factor[0] = (float)((buffer[35] << 8) + buffer[36]) / 1000.0; + Energy.frequency[0] = (float)((buffer[37] << 8) + buffer[38]) / 100.0; + uint8_t offset = 11; + if (Settings.flag3.dds2382_model) { + offset = 19; + } + Energy.export_active = (float)((buffer[offset] << 24) + (buffer[offset +1] << 16) + (buffer[offset +2] << 8) + buffer[offset +3]) / 100.0; + float import_active = (float)((buffer[offset +4] << 24) + (buffer[offset +5] << 16) + (buffer[offset +6] << 8) + buffer[offset +7]) / 100.0; + + EnergyUpdateTotal(import_active, true); + } + } + + if (0 == Dds2382_send_retry || data_ready) { + Dds2382_send_retry = 5; + Dds2382Modbus->Send(DDS2382_ADDR, 0x03, 0, 18); + } else { + Dds2382_send_retry--; + } +} + +void Dds2382SnsInit(void) +{ + Dds2382Modbus = new TasmotaModbus(pin[GPIO_DDS2382_RX], pin[GPIO_DDS2382_TX]); + uint8_t result = Dds2382Modbus->Begin(DDS2382_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Dds2382DrvInit(void) +{ + if ((pin[GPIO_DDS2382_RX] < 99) && (pin[GPIO_DDS2382_TX] < 99)) { + energy_flg = XNRG_09; + } +} + + + + + +bool Xnrg09(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_ENERGY_EVERY_SECOND: + if (uptime > 4) { Dds2382EverySecond(); } + break; + case FUNC_INIT: + Dds2382SnsInit(); + break; + case FUNC_PRE_INIT: + Dds2382DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_10_sdm630.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_10_sdm630.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SDM630 + + + + + + +#define XNRG_10 10 + + +#ifndef SDM630_SPEED + #define SDM630_SPEED 9600 +#endif + +#ifndef SDM630_ADDR + #define SDM630_ADDR 1 +#endif + +#include +TasmotaModbus *Sdm630Modbus; + +const uint16_t sdm630_start_addresses[] { + 0x0000, + 0x0002, + 0x0004, + 0x0006, + 0x0008, + 0x000A, + 0x000C, + 0x000E, + 0x0010, + 0x0018, + 0x001A, + 0x001C, + 0x001E, + 0x0020, + 0x0022, + 0x0156 +}; + +struct SDM630 { + uint8_t read_state = 0; + uint8_t send_retry = 0; +} Sdm630; + + + +void SDM630Every250ms(void) +{ + bool data_ready = Sdm630Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + + uint32_t error = Sdm630Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm630Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM630 error %d"), error); + } else { + Energy.data_valid[0] = 0; + Energy.data_valid[1] = 0; + Energy.data_valid[2] = 0; + + + + + float value; + ((uint8_t*)&value)[3] = buffer[3]; + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Sdm630.read_state) { + case 0: + Energy.voltage[0] = value; + break; + + case 1: + Energy.voltage[1] = value; + break; + + case 2: + Energy.voltage[2] = value; + break; + + case 3: + Energy.current[0] = value; + break; + + case 4: + Energy.current[1] = value; + break; + + case 5: + Energy.current[2] = value; + break; + + case 6: + Energy.active_power[0] = value; + break; + + case 7: + Energy.active_power[1] = value; + break; + + case 8: + Energy.active_power[2] = value; + break; + + case 9: + Energy.reactive_power[0] = value; + break; + + case 10: + Energy.reactive_power[1] = value; + break; + + case 11: + Energy.reactive_power[2] = value; + break; + + case 12: + Energy.power_factor[0] = value; + break; + + case 13: + Energy.power_factor[1] = value; + break; + + case 14: + Energy.power_factor[2] = value; + break; + + case 15: + EnergyUpdateTotal(value, true); + break; + } + + Sdm630.read_state++; + if (sizeof(sdm630_start_addresses)/2 == Sdm630.read_state) { + Sdm630.read_state = 0; + } + } + } + + if (0 == Sdm630.send_retry || data_ready) { + Sdm630.send_retry = 5; + Sdm630Modbus->Send(SDM630_ADDR, 0x04, sdm630_start_addresses[Sdm630.read_state], 2); + } else { + Sdm630.send_retry--; + } +} + +void Sdm630SnsInit(void) +{ + Sdm630Modbus = new TasmotaModbus(pin[GPIO_SDM630_RX], pin[GPIO_SDM630_TX]); + uint8_t result = Sdm630Modbus->Begin(SDM630_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + Energy.phase_count = 3; + } else { + energy_flg = ENERGY_NONE; + } +} + +void Sdm630DrvInit(void) +{ + if ((pin[GPIO_SDM630_RX] < 99) && (pin[GPIO_SDM630_TX] < 99)) { + energy_flg = XNRG_10; + } +} + + + + + +bool Xnrg10(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { SDM630Every250ms(); } + break; + case FUNC_INIT: + Sdm630SnsInit(); + break; + case FUNC_PRE_INIT: + Sdm630DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_11_ddsu666.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_11_ddsu666.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_DDSU666 + + + + +#define XNRG_11 11 + + +#ifndef DDSU666_SPEED + #define DDSU666_SPEED 9600 +#endif + +#ifndef DDSU666_ADDR + #define DDSU666_ADDR 1 +#endif + +#include +TasmotaModbus *Ddsu666Modbus; + +const uint16_t Ddsu666_start_addresses[] { + 0x2000, + 0x2002, + 0x2004, + 0x2006, + 0x200A, + 0x200E, + 0X4000, + 0X400A, +}; + +struct DDSU666 { + float import_active = NAN; + uint8_t read_state = 0; + uint8_t send_retry = 0; +} Ddsu666; + + + +void DDSU666Every250ms(void) +{ + bool data_ready = Ddsu666Modbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + + uint32_t error = Ddsu666Modbus->ReceiveBuffer(buffer, 2); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Ddsu666Modbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: Ddsu666 error %d"), error); + } else { + Energy.data_valid[0] = 0; + + + + + float value; + ((uint8_t*)&value)[3] = buffer[3]; + ((uint8_t*)&value)[2] = buffer[4]; + ((uint8_t*)&value)[1] = buffer[5]; + ((uint8_t*)&value)[0] = buffer[6]; + + switch(Ddsu666.read_state) { + case 0: + Energy.voltage[0] = value; + break; + + case 1: + Energy.current[0] = value; + break; + + case 2: + Energy.active_power[0] = value * 1000; + break; + + case 3: + Energy.reactive_power[0] = value * 1000; + break; + + case 4: + Energy.power_factor[0] = value; + break; + + case 5: + Energy.frequency[0] = value; + break; + + case 6: + Ddsu666.import_active = value; + break; + + case 7: + Energy.export_active = value; + break; + } + + Ddsu666.read_state++; + + if (Ddsu666.read_state == 8) { + Ddsu666.read_state = 0; + EnergyUpdateTotal(Ddsu666.import_active, true); + } + } + } + + if (0 == Ddsu666.send_retry || data_ready) { + Ddsu666.send_retry = 5; + Ddsu666Modbus->Send(DDSU666_ADDR, 0x04, Ddsu666_start_addresses[Ddsu666.read_state], 2); + } else { + Ddsu666.send_retry--; + } +} + +void Ddsu666SnsInit(void) +{ + Ddsu666Modbus = new TasmotaModbus(pin[GPIO_DDSU666_RX], pin[GPIO_DDSU666_TX]); + uint8_t result = Ddsu666Modbus->Begin(DDSU666_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void Ddsu666DrvInit(void) +{ + if ((pin[GPIO_DDSU666_RX] < 99) && (pin[GPIO_DDSU666_TX] < 99)) { + energy_flg = XNRG_11; + } +} + + + + + +bool Xnrg11(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { DDSU666Every250ms(); } + break; + case FUNC_INIT: + Ddsu666SnsInit(); + break; + case FUNC_PRE_INIT: + Ddsu666DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_12_solaxX1.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_12_solaxX1.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_SOLAX_X1 + + + + +#define XNRG_12 12 + +#ifndef SOLAXX1_SPEED +#define SOLAXX1_SPEED 9600 +#endif + +#define INVERTER_ADDRESS 0x0A + +#define D_SOLAX_X1 "SolaxX1" + +#include + +enum solaxX1_Error +{ + solaxX1_ERR_NO_ERROR, + solaxX1_ERR_CRC_ERROR +}; + +union { + uint32_t ErrMessage; + struct { + + uint8_t TzProtectFault:1; + uint8_t MainsLostFault:1; + uint8_t GridVoltFault:1; + uint8_t GridFreqFault:1; + uint8_t PLLLostFault:1; + uint8_t BusVoltFault:1; + uint8_t ErrBit06:1; + uint8_t OciFault:1; + + uint8_t Dci_OCP_Fault:1; + uint8_t ResidualCurrentFault:1; + uint8_t PvVoltFault:1; + uint8_t Ac10Mins_Voltage_Fault:1; + uint8_t IsolationFault:1; + uint8_t TemperatureOverFault:1; + uint8_t FanFault:1; + uint8_t ErrBit15:1; + + uint8_t SpiCommsFault:1; + uint8_t SciCommsFault:1; + uint8_t ErrBit18:1; + uint8_t InputConfigFault:1; + uint8_t EepromFault:1; + uint8_t RelayFault:1; + uint8_t SampleConsistenceFault:1; + uint8_t ResidualCurrent_DeviceFault:1; + + uint8_t ErrBit24:1; + uint8_t ErrBit25:1; + uint8_t ErrBit26:1; + uint8_t ErrBit27:1; + uint8_t ErrBit28:1; + uint8_t DCI_DeviceFault:1; + uint8_t OtherDeviceFault:1; + uint8_t ErrBit31:1; + }; +} ErrCode; + +const char kSolaxMode[] PROGMEM = D_WAITING "|" D_CHECKING "|" D_WORKING "|" D_FAILURE; + +const char kSolaxError[] PROGMEM = + D_SOLAX_ERROR_0 "|" D_SOLAX_ERROR_1 "|" D_SOLAX_ERROR_2 "|" D_SOLAX_ERROR_3 "|" D_SOLAX_ERROR_4 "|" D_SOLAX_ERROR_5 "|" + D_SOLAX_ERROR_6 "|" D_SOLAX_ERROR_7 "|" D_SOLAX_ERROR_8; + + + +TasmotaSerial *solaxX1Serial; + +uint8_t solaxX1_Init = 1; + +struct SOLAXX1 { + float temperature = 0; + float energy_today = 0; + float dc1_voltage = 0; + float dc2_voltage = 0; + float dc1_current = 0; + float dc2_current = 0; + float energy_total = 0; + float runtime_total = 0; + float dc1_power = 0; + float dc2_power = 0; + + uint8_t status = 0; + uint32_t errorCode = 0; +} solaxX1; + +union { + uint8_t status; + struct { + uint8_t freeBit7:1; + uint8_t freeBit6:1; + uint8_t freeBit5:1; + uint8_t queryOffline:1; + uint8_t queryOfflineSend:1; + uint8_t hasAddress:1; + uint8_t inverterAddressSend:1; + uint8_t inverterSnReceived:1; + }; +} protocolStatus; + +uint8_t header[2] = {0xAA, 0x55}; +uint8_t source[2] = {0x00, 0x00}; +uint8_t destination[2] = {0x00, 0x00}; +uint8_t controlCode[1] = {0x00}; +uint8_t functionCode[1] = {0x00}; +uint8_t dataLength[1] = {0x00}; +uint8_t data[16] = {0}; + +uint8_t message[30]; + + + +bool solaxX1_RS485ReceiveReady(void) +{ + return (solaxX1Serial->available() > 1); +} + +void solaxX1_RS485Send(uint16_t msgLen) +{ + memcpy(message, header, 2); + memcpy(message + 2, source, 2); + memcpy(message + 4, destination, 2); + memcpy(message + 6, controlCode, 1); + memcpy(message + 7, functionCode, 1); + memcpy(message + 8, dataLength, 1); + memcpy(message + 9, data, sizeof(data)); + uint16_t crc = solaxX1_calculateCRC(message, msgLen); + + while (solaxX1Serial->available() > 0) + { + solaxX1Serial->read(); + } + + solaxX1Serial->flush(); + solaxX1Serial->write(message, msgLen); + solaxX1Serial->write(highByte(crc)); + solaxX1Serial->write(lowByte(crc)); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, msgLen); +} + +uint8_t solaxX1_RS485Receive(uint8_t *value) +{ + uint8_t len = 0; + + while (solaxX1Serial->available() > 0) + { + value[len++] = (uint8_t)solaxX1Serial->read(); + } + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, value, len); + + uint16_t crc = solaxX1_calculateCRC(value, len - 2); + + if (value[len - 1] == lowByte(crc) && value[len - 2] == highByte(crc)) + { + return solaxX1_ERR_NO_ERROR; + } + else + { + return solaxX1_ERR_CRC_ERROR; + } +} + +uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) +{ + uint8_t i; + uint16_t wChkSum; + wChkSum = 0; + + for (i = 0; i < bLen; i++) + { + wChkSum = wChkSum + bExternTxPackage[i]; + } + return wChkSum; +} + +void solaxX1_SendInverterAddress(void) +{ + source[0] = 0x00; + destination[0] = 0x00; + destination[1] = 0x00; + controlCode[0] = 0x10; + functionCode[0] = 0x01; + dataLength[0] = 0x0F; + data[14] = INVERTER_ADDRESS; + solaxX1_RS485Send(24); +} + +void solaxX1_QueryLiveData(void) +{ + source[0] = 0x01; + destination[0] = 0x00; + destination[1] = INVERTER_ADDRESS; + controlCode[0] = 0x11; + functionCode[0] = 0x02; + dataLength[0] = 0x00; + solaxX1_RS485Send(9); +} + +uint8_t solaxX1_ParseErrorCode(uint32_t code){ + ErrCode.ErrMessage = code; + + if (code == 0) return 0; + if (ErrCode.MainsLostFault) return 1; + if (ErrCode.GridVoltFault) return 2; + if (ErrCode.GridFreqFault) return 3; + if (ErrCode.PvVoltFault) return 4; + if (ErrCode.IsolationFault) return 5; + if (ErrCode.TemperatureOverFault) return 6; + if (ErrCode.FanFault) return 7; + if (ErrCode.OtherDeviceFault) return 8; +} + + + +uint8_t solaxX1_send_retry = 0; +uint8_t solaxX1_nodata_count = 0; + +void solaxX1250MSecond(void) +{ + uint8_t value[61] = {0}; + bool data_ready = solaxX1_RS485ReceiveReady(); + + if (protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) + { + if (data_ready) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Data response CRC error")); + } + else + { + solaxX1_nodata_count = 0; + solaxX1_send_retry = 12; + Energy.data_valid[0] = 0; + + solaxX1.temperature = (float)((value[9] << 8) | value[10]); + solaxX1.energy_today = (float)((value[11] << 8) | value[12]) * 0.1f; + solaxX1.dc1_voltage = (float)((value[13] << 8) | value[14]) * 0.1f; + solaxX1.dc2_voltage = (float)((value[15] << 8) | value[16]) * 0.1f; + solaxX1.dc1_current = (float)((value[17] << 8) | value[18]) * 0.1f; + solaxX1.dc2_current = (float)((value[19] << 8) | value[20]) * 0.1f; + Energy.current[0] = (float)((value[21] << 8) | value[22]) * 0.1f; + Energy.voltage[0] = (float)((value[23] << 8) | value[24]) * 0.1f; + Energy.frequency[0] = (float)((value[25] << 8) | value[26]) * 0.01f; + Energy.active_power[0] = (float)((value[27] << 8) | value[28]); + + solaxX1.energy_total = (float)((value[31] << 8) | (value[32] << 8) | (value[33] << 8) | value[34]) * 0.1f; + solaxX1.runtime_total = (float)((value[35] << 8) | (value[36] << 8) | (value[37] << 8) | value[38]); + solaxX1.status = (uint8_t)((value[39] << 8) | value[40]); + + + + + + + + solaxX1.errorCode = (uint32_t)((value[58] << 8) | (value[57] << 8) | (value[56] << 8) | value[55]); + + solaxX1.dc1_power = solaxX1.dc1_voltage * solaxX1.dc1_current; + solaxX1.dc2_power = solaxX1.dc2_voltage * solaxX1.dc2_current; + + solaxX1_QueryLiveData(); + EnergyUpdateTotal(solaxX1.energy_total, true); + } + } + + if (0 == solaxX1_send_retry && 255 != solaxX1_nodata_count) { + solaxX1_send_retry = 12; + solaxX1_QueryLiveData(); + } + + + + if (255 == solaxX1_nodata_count) { + solaxX1_nodata_count = 0; + solaxX1_send_retry = 12; + } + } + else + { + if ((solaxX1_nodata_count % 4) == 0) { DEBUG_SENSOR_LOG(PSTR("SX1: No Data count: %d"), solaxX1_nodata_count); } + if (solaxX1_nodata_count < 10 * 4) + { + solaxX1_nodata_count++; + } + else if (255 != solaxX1_nodata_count) + { + + solaxX1_nodata_count = 255; + solaxX1_send_retry = 12; + protocolStatus.status = 0b00001000; + Energy.data_valid[0] = ENERGY_WATCHDOG; + + solaxX1.temperature = solaxX1.dc1_voltage = solaxX1.dc2_voltage = solaxX1.dc1_current = solaxX1.dc2_current = solaxX1.dc1_power = 0; + solaxX1.dc2_power = solaxX1.status = Energy.current[0] = Energy.voltage[0] = Energy.frequency[0] = Energy.active_power[0] = 0; + + } + } + + if (!protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) + { + if (data_ready) + { + + if (protocolStatus.inverterAddressSend) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Address confirmation response CRC error")); + } + else + { + if (value[6] == 0x10 && value[7] == 0x81 && value[9] == 0x06) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Set hasAddress")); + protocolStatus.status = 0b00100000; + } + } + } + + + if (protocolStatus.queryOfflineSend) + { + uint8_t error = solaxX1_RS485Receive(value); + if (error) + { + DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline response CRC error")); + } + else + { + + if (value[6] == 0x10 && value[7] == 0x80 && protocolStatus.inverterSnReceived == false) + { + for (uint8_t i = 9; i <= 22; i++) + { + data[i - 9] = value[i]; + } + solaxX1_SendInverterAddress(); + protocolStatus.status = 0b1100000; + DEBUG_SENSOR_LOG(PSTR("SX1: Set inverterSnReceived and inverterAddressSend")); + } + } + } + } + + if (solaxX1_send_retry == 0) + { + if (protocolStatus.queryOfflineSend) + { + protocolStatus.status = 0b00001000; + DEBUG_SENSOR_LOG(PSTR("SX1: Set Query Offline")); + } + solaxX1_send_retry = 12; + } + + + if (protocolStatus.queryOffline) + { + + source[0] = 0x01; + destination[1] = 0x00; + controlCode[0] = 0x10; + functionCode[0] = 0x00; + dataLength[0] = 0x00; + solaxX1_RS485Send(9); + protocolStatus.status = 0b00010000; + DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline Send")); + } + } + + if (!data_ready) + solaxX1_send_retry--; +} + +void solaxX1SnsInit(void) +{ + AddLog_P(LOG_LEVEL_DEBUG, PSTR("SX1: Solax X1 Inverter Init")); + DEBUG_SENSOR_LOG(PSTR("SX1: RX pin: %d, TX pin: %d"), pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX]); + protocolStatus.status = 0b00100000; + + solaxX1Serial = new TasmotaSerial(pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX], 1); + if (solaxX1Serial->begin(SOLAXX1_SPEED)) { + if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void solaxX1DrvInit(void) +{ + if ((pin[GPIO_SOLAXX1_RX] < 99) && (pin[GPIO_SOLAXX1_TX] < 99)) { + energy_flg = XNRG_12; + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_solaxX1_DATA1[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_SOLAR_POWER "{m}%s " D_UNIT_WATT "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}" D_SOLAX_X1 " " D_PV1_POWER "{m}%s " D_UNIT_WATT "{e}"; +#ifdef SOLAXX1_PV2 +const char HTTP_SNS_solaxX1_DATA2[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_PV2_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}" D_SOLAX_X1 " " D_PV2_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}" D_SOLAX_X1 " " D_PV2_POWER "{m}%s " D_UNIT_WATT "{e}"; +#endif +const char HTTP_SNS_solaxX1_DATA3[] PROGMEM = + "{s}" D_SOLAX_X1 " " D_UPTIME "{m}%s " D_UNIT_HOUR "{e}" + "{s}" D_SOLAX_X1 " " D_STATUS "{m}%s" + "{s}" D_SOLAX_X1 " " D_ERROR "{m}%s"; +#endif + +void solaxX1Show(bool json) +{ + char solar_power[33]; + dtostrfd(solaxX1.dc1_power + solaxX1.dc2_power, Settings.flag2.wattage_resolution, solar_power); + char pv1_voltage[33]; + dtostrfd(solaxX1.dc1_voltage, Settings.flag2.voltage_resolution, pv1_voltage); + char pv1_current[33]; + dtostrfd(solaxX1.dc1_current, Settings.flag2.current_resolution, pv1_current); + char pv1_power[33]; + dtostrfd(solaxX1.dc1_power, Settings.flag2.wattage_resolution, pv1_power); +#ifdef SOLAXX1_PV2 + char pv2_voltage[33]; + dtostrfd(solaxX1.dc2_voltage, Settings.flag2.voltage_resolution, pv2_voltage); + char pv2_current[33]; + dtostrfd(solaxX1.dc2_current, Settings.flag2.current_resolution, pv2_current); + char pv2_power[33]; + dtostrfd(solaxX1.dc2_power, Settings.flag2.wattage_resolution, pv2_power); +#endif + char temperature[33]; + dtostrfd(solaxX1.temperature, Settings.flag2.temperature_resolution, temperature); + char runtime[33]; + dtostrfd(solaxX1.runtime_total, 0, runtime); + char status[33]; + GetTextIndexed(status, sizeof(status), solaxX1.status, kSolaxMode); + + if (json) + { + ResponseAppend_P(PSTR(",\"" D_JSON_SOLAR_POWER "\":%s,\"" D_JSON_PV1_VOLTAGE "\":%s,\"" D_JSON_PV1_CURRENT "\":%s,\"" D_JSON_PV1_POWER "\":%s"), + solar_power, pv1_voltage, pv1_current, pv1_power); +#ifdef SOLAXX1_PV2 + ResponseAppend_P(PSTR(",\"" D_JSON_PV2_VOLTAGE "\":%s,\"" D_JSON_PV2_CURRENT "\":%s,\"" D_JSON_PV2_POWER "\":%s"), + pv2_voltage, pv2_current, pv2_power); +#endif + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RUNTIME "\":%s,\"" D_JSON_STATUS "\":\"%s\",\"" D_JSON_ERROR "\":%d"), + temperature, runtime, status, solaxX1.errorCode); + +#ifdef USE_WEBSERVER + } + else + { + WSContentSend_PD(HTTP_SNS_solaxX1_DATA1, solar_power, pv1_voltage, pv1_current, pv1_power); +#ifdef SOLAXX1_PV2 + WSContentSend_PD(HTTP_SNS_solaxX1_DATA2, pv2_voltage, pv2_current, pv2_power); +#endif + WSContentSend_PD(HTTP_SNS_TEMP, D_SOLAX_X1, temperature, TempUnit()); + char errorCodeString[33]; + WSContentSend_PD(HTTP_SNS_solaxX1_DATA3, runtime, status, + GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError)); +#endif + } +} + + + + + +bool Xnrg12(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { solaxX1250MSecond(); } + break; + case FUNC_JSON_APPEND: + solaxX1Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + solaxX1Show(0); + break; +#endif + case FUNC_INIT: + solaxX1SnsInit(); + break; + case FUNC_PRE_INIT: + solaxX1DrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" +#ifdef USE_ENERGY_SENSOR +#ifdef USE_LE01MR +# 71 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" +#define XNRG_13 13 + + +#ifndef LE01MR_SPEED + #define LE01MR_SPEED 2400 +#endif + +#ifndef LE01MR_ADDR + #define LE01MR_ADDR 1 +#endif + +#include +TasmotaModbus *FifLEModbus; + +const uint8_t le01mr_table_sz = 9; + +const uint16_t le01mr_register_addresses[] { + + 0x0130, + 0x0131, + 0x0158, + 0x0139, + 0x0140, + 0x0148, + 0x0150, + 0xA000, + 0xA01E +}; + +struct LE01MR { + float total_active = 0; + float total_reactive = 0; + uint8_t read_state = 0; + uint8_t send_retry = 0; + uint8_t start_address_count = le01mr_table_sz; +} Le01mr; + + + +void FifLEEvery250ms(void) +{ + bool data_ready = FifLEModbus->ReceiveReady(); + + if (data_ready) { + uint8_t buffer[14]; + uint8_t reg_count = 2; + if (Le01mr.read_state < 3) { + reg_count=1; + } + + uint32_t error = FifLEModbus->ReceiveBuffer(buffer, reg_count); + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, FifLEModbus->ReceiveCount()); + + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("FiF-LE: LE01MR Modbus error %d"), error); + } else { + Energy.data_valid[0] = 0; +# 146 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" + uint32_t value_buff = 0; + + if (Le01mr.read_state >= 0 && Le01mr.read_state < 3) { + value_buff = ((uint32_t)buffer[3])<<8 | buffer[4]; + } else { + value_buff = ((uint32_t)buffer[3])<<24 | ((uint32_t)buffer[4])<<16 | ((uint32_t)buffer[5])<<8 | buffer[6]; + } + + switch(Le01mr.read_state) { + case 0: + Energy.frequency[0] = value_buff * 0.01f; + break; + + case 1: + Energy.voltage[0] = value_buff * 0.01f; + break; + + case 2: + Energy.power_factor[0] = ((int16_t)value_buff) * 0.001f; + break; + + case 3: + Energy.current[0] = value_buff * 0.001f; + break; + + case 4: + Energy.active_power[0] = value_buff * 1.0f; + break; + + case 5: + Energy.reactive_power[0] = value_buff * 1.0f; + break; + + case 6: + Energy.apparent_power[0] = value_buff * 1.0f; + break; + + case 7: + Le01mr.total_active = value_buff * 0.01f; + break; + + case 8: + Le01mr.total_reactive = value_buff * 0.01f; + break; + } + + Le01mr.read_state++; + if (Le01mr.read_state == Le01mr.start_address_count) { + Le01mr.read_state = 0; + + EnergyUpdateTotal(Le01mr.total_active, true); + } + } + } + + if (0 == Le01mr.send_retry || data_ready) { + uint8_t reg_count = 2; + + Le01mr.send_retry = 5; + + if (Le01mr.read_state < 3) reg_count=1; + + FifLEModbus->Send(LE01MR_ADDR, 0x03, le01mr_register_addresses[Le01mr.read_state], reg_count); + } else { + Le01mr.send_retry--; + } +} + +void FifLESnsInit(void) +{ + FifLEModbus = new TasmotaModbus(pin[GPIO_LE01MR_RX], pin[GPIO_LE01MR_TX]); + uint8_t result = FifLEModbus->Begin(LE01MR_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + } else { + energy_flg = ENERGY_NONE; + } +} + +void FifLEDrvInit(void) +{ + if ((pin[GPIO_LE01MR_RX] < 99) && (pin[GPIO_LE01MR_TX] < 99)) { + energy_flg = XNRG_13; + } +} + +void FifLEReset(void) +{ + Le01mr.total_active = 0; + Le01mr.total_reactive = 0; +} + +#ifdef USE_WEBSERVER +const char HTTP_ENERGY_LE01MR[] PROGMEM = + "{s}" D_TOTAL_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}" + "{s}" D_TOTAL_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" + ; +#endif + +void FifLEShow(bool json) +{ + char total_reactive_chr[FLOATSZ]; + dtostrfd(Le01mr.total_reactive, Settings.flag2.energy_resolution, total_reactive_chr); + char total_active_chr[FLOATSZ]; + dtostrfd(Le01mr.total_active, Settings.flag2.energy_resolution, total_active_chr); + + if (json) { + ResponseAppend_P(PSTR(",\"" D_JSON_TOTAL_ACTIVE "\":%s,\"" D_JSON_TOTAL_REACTIVE "\":%s"), + total_active_chr, total_reactive_chr); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_ENERGY_LE01MR, total_active_chr, total_reactive_chr); +#endif + } +} + + + + + +bool Xnrg13(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + if (uptime > 4) { + FifLEEvery250ms(); + } + break; + case FUNC_JSON_APPEND: + FifLEShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + FifLEShow(0); + break; +#endif + case FUNC_ENERGY_RESET: + FifLEReset(); + break; + case FUNC_INIT: + FifLESnsInit(); + break; + case FUNC_PRE_INIT: + FifLEDrvInit(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_interface.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_interface.ino" +#ifdef USE_ENERGY_SENSOR + +#ifdef XFUNC_PTR_IN_ROM +bool (* const xnrg_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xnrg_func_ptr[])(uint8_t) = { +#endif + +#ifdef XNRG_01 + &Xnrg01, +#endif + +#ifdef XNRG_02 + &Xnrg02, +#endif + +#ifdef XNRG_03 + &Xnrg03, +#endif + +#ifdef XNRG_04 + &Xnrg04, +#endif + +#ifdef XNRG_05 + &Xnrg05, +#endif + +#ifdef XNRG_06 + &Xnrg06, +#endif + +#ifdef XNRG_07 + &Xnrg07, +#endif + +#ifdef XNRG_08 + &Xnrg08, +#endif + +#ifdef XNRG_09 + &Xnrg09, +#endif + +#ifdef XNRG_10 + &Xnrg10, +#endif + +#ifdef XNRG_11 + &Xnrg11, +#endif + +#ifdef XNRG_12 + &Xnrg12, +#endif + +#ifdef XNRG_13 + &Xnrg13, +#endif + +#ifdef XNRG_14 + &Xnrg14, +#endif + +#ifdef XNRG_15 + &Xnrg15, +#endif + +#ifdef XNRG_16 + &Xnrg16 +#endif +}; + +const uint8_t xnrg_present = sizeof(xnrg_func_ptr) / sizeof(xnrg_func_ptr[0]); + +uint8_t xnrg_active = 0; + +bool XnrgCall(uint8_t function) +{ + DEBUG_TRACE_LOG(PSTR("NRG: %d"), function); + + if (FUNC_PRE_INIT == function) { + for (uint32_t x = 0; x < xnrg_present; x++) { + xnrg_func_ptr[x](function); + if (energy_flg) { + xnrg_active = x; + return true; + } + } + } + else if (energy_flg) { + return xnrg_func_ptr[xnrg_active](function); + } + return false; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_01_counter.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_01_counter.ino" +#ifdef USE_COUNTER + + + + +#define XSNS_01 1 + +#define D_PRFX_COUNTER "Counter" +#define D_CMND_COUNTERTYPE "Type" +#define D_CMND_COUNTERDEBOUNCE "Debounce" + +const char kCounterCommands[] PROGMEM = D_PRFX_COUNTER "|" + "|" D_CMND_COUNTERTYPE "|" D_CMND_COUNTERDEBOUNCE ; + +void (* const CounterCommand[])(void) PROGMEM = { + &CmndCounter, &CmndCounterType, &CmndCounterDebounce }; + +struct COUNTER { + uint32_t timer[MAX_COUNTERS]; + uint8_t no_pullup = 0; + bool any_counter = false; +} Counter; + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 +void CounterUpdate(uint8_t index) ICACHE_RAM_ATTR; +void CounterUpdate1(void) ICACHE_RAM_ATTR; +void CounterUpdate2(void) ICACHE_RAM_ATTR; +void CounterUpdate3(void) ICACHE_RAM_ATTR; +void CounterUpdate4(void) ICACHE_RAM_ATTR; +#endif + +void CounterUpdate(uint8_t index) +{ + uint32_t time = micros(); + uint32_t debounce_time = time - Counter.timer[index]; + if (debounce_time > Settings.pulse_counter_debounce * 1000) { + Counter.timer[index] = time; + if (bitRead(Settings.pulse_counter_type, index)) { + RtcSettings.pulse_counter[index] = debounce_time; + } else { + RtcSettings.pulse_counter[index]++; + } + } +} + +void CounterUpdate1(void) +{ + CounterUpdate(0); +} + +void CounterUpdate2(void) +{ + CounterUpdate(1); +} + +void CounterUpdate3(void) +{ + CounterUpdate(2); +} + +void CounterUpdate4(void) +{ + CounterUpdate(3); +} + + + +bool CounterPinState(void) +{ + if ((XdrvMailbox.index >= GPIO_CNTR1_NP) && (XdrvMailbox.index < (GPIO_CNTR1_NP + MAX_COUNTERS))) { + bitSet(Counter.no_pullup, XdrvMailbox.index - GPIO_CNTR1_NP); + XdrvMailbox.index -= (GPIO_CNTR1_NP - GPIO_CNTR1); + return true; + } + return false; +} + +void CounterInit(void) +{ + typedef void (*function) () ; + function counter_callbacks[] = { CounterUpdate1, CounterUpdate2, CounterUpdate3, CounterUpdate4 }; + + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + Counter.any_counter = true; + pinMode(pin[GPIO_CNTR1 +i], bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); + attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], FALLING); + } + } +} + +void CounterEverySecond(void) +{ + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + if (bitRead(Settings.pulse_counter_type, i)) { + uint32_t time = micros() - Counter.timer[i]; + if (time > 4200000000) { + RtcSettings.pulse_counter[i] = 4200000000; + } + } + } + } +} + +void CounterSaveState(void) +{ + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + Settings.pulse_counter[i] = RtcSettings.pulse_counter[i]; + } + } +} + +void CounterShow(bool json) +{ + bool header = false; + uint8_t dsxflg = 0; + for (uint32_t i = 0; i < MAX_COUNTERS; i++) { + if (pin[GPIO_CNTR1 +i] < 99) { + char counter[33]; + if (bitRead(Settings.pulse_counter_type, i)) { + dtostrfd((double)RtcSettings.pulse_counter[i] / 1000000, 6, counter); + } else { + dsxflg++; + snprintf_P(counter, sizeof(counter), PSTR("%lu"), RtcSettings.pulse_counter[i]); + } + + if (json) { + if (!header) { + ResponseAppend_P(PSTR(",\"COUNTER\":{")); + } + ResponseAppend_P(PSTR("%s\"C%d\":%s"), (header)?",":"", i +1, counter); + header = true; +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (1 == dsxflg)) { + DomoticzSensor(DZ_COUNT, RtcSettings.pulse_counter[i]); + dsxflg++; + } +#endif + if ((0 == tele_period ) && (Settings.flag3.counter_reset_on_tele)) { + RtcSettings.pulse_counter[i] = 0; + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(PSTR("{s}" D_COUNTER "%d{m}%s%s{e}"), + i +1, counter, (bitRead(Settings.pulse_counter_type, i)) ? " " D_UNIT_SECOND : ""); +#endif + } + } + } + if (header) { + ResponseJsonEnd(); + } +} + + + + + +void CmndCounter(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { + if ((XdrvMailbox.data_len > 0) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { + if ((XdrvMailbox.data[0] == '-') || (XdrvMailbox.data[0] == '+')) { + RtcSettings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; + Settings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; + } else { + RtcSettings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload; + Settings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload; + } + } + ResponseCmndIdxNumber(RtcSettings.pulse_counter[XdrvMailbox.index -1]); + } +} + +void CmndCounterType(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { + bitWrite(Settings.pulse_counter_type, XdrvMailbox.index -1, XdrvMailbox.payload &1); + RtcSettings.pulse_counter[XdrvMailbox.index -1] = 0; + Settings.pulse_counter[XdrvMailbox.index -1] = 0; + } + ResponseCmndIdxNumber(bitRead(Settings.pulse_counter_type, XdrvMailbox.index -1)); + } +} + +void CmndCounterDebounce(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { + Settings.pulse_counter_debounce = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.pulse_counter_debounce); +} + + + + + +bool Xsns01(uint8_t function) +{ + bool result = false; + + if (Counter.any_counter) { + switch (function) { + case FUNC_EVERY_SECOND: + CounterEverySecond(); + break; + case FUNC_JSON_APPEND: + CounterShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + CounterShow(0); + break; +#endif + case FUNC_SAVE_BEFORE_RESTART: + case FUNC_SAVE_AT_MIDNIGHT: + CounterSaveState(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kCounterCommands, CounterCommand); + break; + } + } else { + switch (function) { + case FUNC_INIT: + CounterInit(); + break; + case FUNC_PIN_STATE: + result = CounterPinState(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_02_analog.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_02_analog.ino" +#ifndef USE_ADC_VCC + + + + +#define XSNS_02 2 + +#define TO_CELSIUS(x) ((x) - 273.15) +#define TO_KELVIN(x) ((x) + 273.15) + + +#define ANALOG_V33 3.3 +#define ANALOG_T0 TO_KELVIN(25.0) + + + + + +#define ANALOG_NTC_BRIDGE_RESISTANCE 32000 +#define ANALOG_NTC_RESISTANCE 10000 +#define ANALOG_NTC_B_COEFFICIENT 3350 + + + + + +#define ANALOG_LDR_BRIDGE_RESISTANCE 10000 +#define ANALOG_LDR_LUX_CALC_SCALAR 12518931 +#define ANALOG_LDR_LUX_CALC_EXPONENT -1.4050 +# 58 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_02_analog.ino" +#define ANALOG_CT_FLAGS 0 +#define ANALOG_CT_MULTIPLIER 2146 +#define ANALOG_CT_VOLTAGE 2300 + +#define CT_FLAG_ENERGY_RESET (1 << 0) + +struct { + float temperature = 0; + float current = 0; + float energy = 0; + uint32_t previous_millis = 0; + uint16_t last_value = 0; +} Adc; + +void AdcInit(void) +{ + if ((Settings.adc_param_type != my_adc0) || (Settings.adc_param1 > 1000000)) { + if (ADC0_TEMP == my_adc0) { + + Settings.adc_param_type = ADC0_TEMP; + Settings.adc_param1 = ANALOG_NTC_BRIDGE_RESISTANCE; + Settings.adc_param2 = ANALOG_NTC_RESISTANCE; + Settings.adc_param3 = ANALOG_NTC_B_COEFFICIENT * 10000; + } + else if (ADC0_LIGHT == my_adc0) { + Settings.adc_param_type = ADC0_LIGHT; + Settings.adc_param1 = ANALOG_LDR_BRIDGE_RESISTANCE; + Settings.adc_param2 = ANALOG_LDR_LUX_CALC_SCALAR; + Settings.adc_param3 = ANALOG_LDR_LUX_CALC_EXPONENT * 10000; + } + else if (ADC0_RANGE == my_adc0) { + Settings.adc_param_type = ADC0_RANGE; + Settings.adc_param1 = 0; + Settings.adc_param2 = 1023; + Settings.adc_param3 = 0; + Settings.adc_param4 = 100; + } + else if (ADC0_CT_POWER == my_adc0) { + Settings.adc_param_type = ADC0_CT_POWER; + Settings.adc_param1 = ANALOG_CT_FLAGS; + Settings.adc_param2 = ANALOG_CT_MULTIPLIER; + Settings.adc_param3 = ANALOG_CT_VOLTAGE; + } + } +} + +uint16_t AdcRead(uint8_t factor) +{ + + + + + + uint8_t samples = 1 << factor; + uint16_t analog = 0; + for (uint32_t i = 0; i < samples; i++) { + analog += analogRead(A0); + delay(1); + } + analog >>= factor; + return analog; +} + +#ifdef USE_RULES +void AdcEvery250ms(void) +{ + if (ADC0_INPUT == my_adc0) { + uint16_t new_value = AdcRead(5); + if ((new_value < Adc.last_value -10) || (new_value > Adc.last_value +10)) { + Adc.last_value = new_value; + uint16_t value = Adc.last_value / 10; + Response_P(PSTR("{\"ANALOG\":{\"A0div10\":%d}}"), (value > 99) ? 100 : value); + XdrvRulesProcess(); + } + } +} +#endif + +uint16_t AdcGetLux(void) +{ + int adc = AdcRead(2); + + double resistorVoltage = ((double)adc / 1023) * ANALOG_V33; + double ldrVoltage = ANALOG_V33 - resistorVoltage; + double ldrResistance = ldrVoltage / resistorVoltage * (double)Settings.adc_param1; + double ldrLux = (double)Settings.adc_param2 * FastPrecisePow(ldrResistance, (double)Settings.adc_param3 / 10000); + + return (uint16_t)ldrLux; +} + +uint16_t AdcGetRange(void) +{ + + + + int adc = AdcRead(2); + double adcrange = ( ((double)Settings.adc_param2 - (double)adc) / ( ((double)Settings.adc_param2 - (double)Settings.adc_param1)) * ((double)Settings.adc_param3 - (double)Settings.adc_param4) + (double)Settings.adc_param4 ); + return (uint16_t)adcrange; +} + +void AdcGetCurrentPower(uint8_t factor) +{ + + + + + + uint8_t samples = 1 << factor; + uint16_t analog = 0; + uint16_t analog_min = 1023; + uint16_t analog_max = 0; + for (uint32_t i = 0; i < samples; i++) { + analog = analogRead(A0); + if (analog < analog_min) { + analog_min = analog; + } + if (analog > analog_max) { + analog_max = analog; + } + delay(1); + } + + Adc.current = (float)(analog_max-analog_min) * ((float)(Settings.adc_param2) / 100000); + float power = Adc.current * (float)(Settings.adc_param3) / 10; + uint32_t current_millis = millis(); + Adc.energy = Adc.energy + ((power * (current_millis - Adc.previous_millis)) / 3600000000); + Adc.previous_millis = current_millis; +} + +void AdcEverySecond(void) +{ + if (ADC0_TEMP == my_adc0) { + int adc = AdcRead(2); + + double Rt = (adc * Settings.adc_param1) / (1024.0 * ANALOG_V33 - (double)adc); + double BC = (double)Settings.adc_param3 / 10000; + double T = BC / (BC / ANALOG_T0 + TaylorLog(Rt / (double)Settings.adc_param2)); + Adc.temperature = ConvertTemp(TO_CELSIUS(T)); + } + else if (ADC0_CT_POWER == my_adc0) { + AdcGetCurrentPower(5); + } +} + +void AdcShow(bool json) +{ + if (ADC0_INPUT == my_adc0) { + uint16_t analog = AdcRead(5); + + if (json) { + ResponseAppend_P(PSTR(",\"ANALOG\":{\"A0\":%d}"), analog); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_ANALOG, "", 0, analog); +#endif + } + } + + else if (ADC0_TEMP == my_adc0) { + char temperature[33]; + dtostrfd(Adc.temperature, Settings.flag2.temperature_resolution, temperature); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMP, "ANALOG", temperature); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_TEMP, temperature); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, Adc.temperature); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, "", temperature, TempUnit()); +#endif + } + } + + else if (ADC0_LIGHT == my_adc0) { + uint16_t adc_light = AdcGetLux(); + + if (json) { + ResponseAppend_P(JSON_SNS_ILLUMINANCE, "ANALOG", adc_light); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_ILLUMINANCE, adc_light); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, "", adc_light); +#endif + } + } + + else if (ADC0_RANGE == my_adc0) { + uint16_t adc_range = AdcGetRange(); + + if (json) { + ResponseAppend_P(JSON_SNS_RANGE, "ANALOG", adc_range); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_RANGE, "", adc_range); +#endif + } + } + + else if (ADC0_CT_POWER == my_adc0) { + AdcGetCurrentPower(5); + + float voltage = (float)(Settings.adc_param3) / 10; + char voltage_chr[FLOATSZ]; + dtostrfd(voltage, Settings.flag2.voltage_resolution, voltage_chr); + char current_chr[FLOATSZ]; + dtostrfd(Adc.current, Settings.flag2.current_resolution, current_chr); + char power_chr[FLOATSZ]; + dtostrfd(voltage * Adc.current, Settings.flag2.wattage_resolution, power_chr); + char energy_chr[FLOATSZ]; + dtostrfd(Adc.energy, Settings.flag2.energy_resolution, energy_chr); + + if (json) { + ResponseAppend_P(PSTR(",\"ANALOG\":{\"" D_JSON_ENERGY "\":%s,\"" D_JSON_POWERUSAGE "\":%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s}"), + energy_chr, power_chr, voltage_chr, current_chr); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_POWER_ENERGY, power_chr); + DomoticzSensor(DZ_VOLTAGE, voltage_chr); + DomoticzSensor(DZ_CURRENT, current_chr); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_VOLTAGE, voltage_chr); + WSContentSend_PD(HTTP_SNS_CURRENT, current_chr); + WSContentSend_PD(HTTP_SNS_POWER, power_chr); + WSContentSend_PD(HTTP_SNS_ENERGY_TOTAL, energy_chr); +#endif + } + } + +} + + + + + +const char kAdcCommands[] PROGMEM = "|" + D_CMND_ADC "|" D_CMND_ADCS "|" D_CMND_ADCPARAM; + +void (* const AdcCommand[])(void) PROGMEM = { + &CmndAdc, &CmndAdcs, &CmndAdcParam }; + +void CmndAdc(void) +{ + if (ValidAdc() && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < ADC0_END)) { + Settings.my_adc0 = XdrvMailbox.payload; + restart_flag = 2; + } + char stemp1[TOPSZ]; + Response_P(PSTR("{\"" D_CMND_ADC "0\":{\"%d\":\"%s\"}}"), Settings.my_adc0, GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_adc0, kAdc0Names)); +} + +void CmndAdcs(void) +{ + Response_P(PSTR("{\"" D_CMND_ADCS "\":{")); + bool jsflg = false; + char stemp1[TOPSZ]; + for (uint32_t i = 0; i < ADC0_END; i++) { + if (jsflg) { + ResponseAppend_P(PSTR(",")); + } + jsflg = true; + ResponseAppend_P(PSTR("\"%d\":\"%s\""), i, GetTextIndexed(stemp1, sizeof(stemp1), i, kAdc0Names)); + } + ResponseJsonEndEnd(); +} + +void CmndAdcParam(void) +{ + if (XdrvMailbox.data_len) { + if ((ADC0_TEMP == XdrvMailbox.payload) || + (ADC0_LIGHT == XdrvMailbox.payload) || + (ADC0_RANGE == XdrvMailbox.payload) || + (ADC0_CT_POWER == XdrvMailbox.payload)) { + if (strstr(XdrvMailbox.data, ",") != nullptr) { + char sub_string[XdrvMailbox.data_len +1]; + + + + Settings.adc_param_type = XdrvMailbox.payload; + Settings.adc_param1 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Settings.adc_param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10); + if (ADC0_RANGE == XdrvMailbox.payload) { + Settings.adc_param3 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 4), nullptr, 10)); + Settings.adc_param4 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 5), nullptr, 10)); + } else { + Settings.adc_param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); + } + if (ADC0_CT_POWER == XdrvMailbox.payload) { + if ((Settings.adc_param1 & CT_FLAG_ENERGY_RESET) > 0) { + Adc.energy = 0; + Settings.adc_param1 ^= CT_FLAG_ENERGY_RESET; + } + } + } else { + + + + + Settings.adc_param_type = 0; + AdcInit(); + } + } + } + + + Response_P(PSTR("{\"" D_CMND_ADCPARAM "\":[%d,%d,%d"), Settings.adc_param_type, Settings.adc_param1, Settings.adc_param2); + if (ADC0_RANGE == my_adc0) { + ResponseAppend_P(PSTR(",%d,%d"), Settings.adc_param3, Settings.adc_param4); + } else { + int value = Settings.adc_param3; + uint8_t precision; + for (precision = 4; precision > 0; precision--) { + if (value % 10) { break; } + value /= 10; + } + char param3[33]; + dtostrfd(((double)Settings.adc_param3)/10000, precision, param3); + ResponseAppend_P(PSTR(",%s"), param3); + } + ResponseAppend_P(PSTR("]}")); +} + + + + + +bool Xsns02(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_COMMAND: + result = DecodeCommand(kAdcCommands, AdcCommand); + break; + default: + if ((ADC0_INPUT == my_adc0) || + (ADC0_TEMP == my_adc0) || + (ADC0_LIGHT == my_adc0) || + (ADC0_RANGE == my_adc0) || + (ADC0_CT_POWER == my_adc0)) { + switch (function) { +#ifdef USE_RULES + case FUNC_EVERY_250_MSECOND: + AdcEvery250ms(); + break; +#endif + case FUNC_EVERY_SECOND: + AdcEverySecond(); + break; + case FUNC_INIT: + AdcInit(); + break; + case FUNC_JSON_APPEND: + AdcShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + AdcShow(0); + break; +#endif + } + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_04_snfsc.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_04_snfsc.ino" +#ifdef USE_SONOFF_SC +# 57 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_04_snfsc.ino" +#define XSNS_04 4 + +uint16_t sc_value[5] = { 0 }; + +void SonoffScSend(const char *data) +{ + Serial.write(data); + Serial.write('\x1B'); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_TRANSMIT " %s"), data); +} + +void SonoffScInit(void) +{ + + SonoffScSend("AT+START"); + +} + +void SonoffScSerialInput(char *rcvstat) +{ + char *p; + char *str; + uint16_t value[5] = { 0 }; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_RECEIVED " %s"), rcvstat); + + if (!strncasecmp_P(rcvstat, PSTR("AT+UPDATE="), 10)) { + int8_t i = -1; + for (str = strtok_r(rcvstat, ":", &p); str && i < 5; str = strtok_r(nullptr, ":", &p)) { + value[i++] = atoi(str); + } + if (value[0] > 0) { + for (uint32_t i = 0; i < 5; i++) { + sc_value[i] = value[i]; + } + sc_value[2] = (11 - sc_value[2]) * 10; + sc_value[3] *= 10; + sc_value[4] = (11 - sc_value[4]) * 10; + SonoffScSend("AT+SEND=ok"); + } else { + SonoffScSend("AT+SEND=fail"); + } + } + else if (!strcasecmp_P(rcvstat, PSTR("AT+STATUS?"))) { + SonoffScSend("AT+STATUS=4"); + } +} + + + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SCPLUS[] PROGMEM = + "{s}" D_LIGHT "{m}%d%%{e}{s}" D_NOISE "{m}%d%%{e}{s}" D_AIR_QUALITY "{m}%d%%{e}"; +#endif + +void SonoffScShow(bool json) +{ + if (sc_value[0] > 0) { + float t = ConvertTemp(sc_value[1]); + float h = ConvertHumidity(sc_value[0]); + + char temperature[33]; + dtostrfd(t, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(h, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(PSTR(",\"SonoffSC\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s,\"" D_JSON_LIGHT "\":%d,\"" D_JSON_NOISE "\":%d,\"" D_JSON_AIRQUALITY "\":%d}"), + temperature, humidity, sc_value[2], sc_value[3], sc_value[4]); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzTempHumSensor(temperature, humidity); + DomoticzSensor(DZ_ILLUMINANCE, sc_value[2]); + DomoticzSensor(DZ_COUNT, sc_value[3]); + DomoticzSensor(DZ_AIRQUALITY, 500 + ((100 - sc_value[4]) * 20)); + } +#endif + +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, t); + KnxSensor(KNX_HUMIDITY, h); + } +#endif + +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, "", temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, "", humidity); + WSContentSend_PD(HTTP_SNS_SCPLUS, sc_value[2], sc_value[3], sc_value[4]); +#endif + } + } +} + + + + + +bool Xsns04(uint8_t function) +{ + bool result = false; + + if (SONOFF_SC == my_module_type) { + switch (function) { + case FUNC_JSON_APPEND: + SonoffScShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + SonoffScShow(0); + break; +#endif + case FUNC_INIT: + SonoffScInit(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_05_ds18x20.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_05_ds18x20.ino" +#ifdef USE_DS18x20 + + + + +#define XSNS_05 5 + + + +#define DS18S20_CHIPID 0x10 +#define DS1822_CHIPID 0x22 +#define DS18B20_CHIPID 0x28 +#define MAX31850_CHIPID 0x3B + +#define W1_SKIP_ROM 0xCC +#define W1_CONVERT_TEMP 0x44 +#define W1_WRITE_EEPROM 0x48 +#define W1_WRITE_SCRATCHPAD 0x4E +#define W1_READ_SCRATCHPAD 0xBE + +#define DS18X20_MAX_SENSORS 8 + +const char kDs18x20Types[] PROGMEM = "DS18x20|DS18S20|DS1822|DS18B20|MAX31850"; + +uint8_t ds18x20_chipids[] = { 0, DS18S20_CHIPID, DS1822_CHIPID, DS18B20_CHIPID, MAX31850_CHIPID }; + +struct DS18X20STRUCT { + uint8_t address[8]; + uint8_t index; + uint8_t valid; + float temperature; +} ds18x20_sensor[DS18X20_MAX_SENSORS]; +uint8_t ds18x20_sensors = 0; +uint8_t ds18x20_pin = 0; +uint8_t ds18x20_pin_out = 0; +bool ds18x20_dual_mode = false; +char ds18x20_types[12]; +#ifdef W1_PARASITE_POWER +uint8_t ds18x20_sensor_curr = 0; +unsigned long w1_power_until = 0; +#endif + + + + + +#define W1_MATCH_ROM 0x55 +#define W1_SEARCH_ROM 0xF0 + +uint8_t onewire_last_discrepancy = 0; +uint8_t onewire_last_family_discrepancy = 0; +bool onewire_last_device_flag = false; +unsigned char onewire_rom_id[8] = { 0 }; + + + +uint8_t OneWireReset(void) +{ + uint8_t retries = 125; + + if (!ds18x20_dual_mode) { + pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); + do { + if (--retries == 0) { + return 0; + } + delayMicroseconds(2); + } while (!digitalRead(ds18x20_pin)); + pinMode(ds18x20_pin, OUTPUT); + digitalWrite(ds18x20_pin, LOW); + delayMicroseconds(480); + pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); + } else { + digitalWrite(ds18x20_pin_out, HIGH); + do { + if (--retries == 0) { + return 0; + } + delayMicroseconds(2); + } while (!digitalRead(ds18x20_pin)); + digitalWrite(ds18x20_pin_out, LOW); + delayMicroseconds(480); + digitalWrite(ds18x20_pin_out, HIGH); + } + delayMicroseconds(70); + uint8_t r = !digitalRead(ds18x20_pin); + delayMicroseconds(410); + return r; +} + +void OneWireWriteBit(uint8_t v) +{ + static const uint8_t delay_low[2] = { 65, 10 }; + static const uint8_t delay_high[2] = { 5, 55 }; + + v &= 1; + if (!ds18x20_dual_mode) { + digitalWrite(ds18x20_pin, LOW); + pinMode(ds18x20_pin, OUTPUT); + delayMicroseconds(delay_low[v]); + digitalWrite(ds18x20_pin, HIGH); + } else { + digitalWrite(ds18x20_pin_out, LOW); + delayMicroseconds(delay_low[v]); + digitalWrite(ds18x20_pin_out, HIGH); + } + delayMicroseconds(delay_high[v]); +} + +uint8_t OneWireReadBit(void) +{ + if (!ds18x20_dual_mode) { + pinMode(ds18x20_pin, OUTPUT); + digitalWrite(ds18x20_pin, LOW); + delayMicroseconds(3); + pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); + } else { + digitalWrite(ds18x20_pin_out, LOW); + delayMicroseconds(3); + digitalWrite(ds18x20_pin_out, HIGH); + } + delayMicroseconds(10); + uint8_t r = digitalRead(ds18x20_pin); + delayMicroseconds(53); + return r; +} + + + +void OneWireWrite(uint8_t v) +{ + for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { + OneWireWriteBit((bit_mask & v) ? 1 : 0); + } +} + +uint8_t OneWireRead(void) +{ + uint8_t r = 0; + + for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { + if (OneWireReadBit()) { + r |= bit_mask; + } + } + return r; +} + +void OneWireSelect(const uint8_t rom[8]) +{ + OneWireWrite(W1_MATCH_ROM); + for (uint32_t i = 0; i < 8; i++) { + OneWireWrite(rom[i]); + } +} + +void OneWireResetSearch(void) +{ + onewire_last_discrepancy = 0; + onewire_last_device_flag = false; + onewire_last_family_discrepancy = 0; + for (uint32_t i = 0; i < 8; i++) { + onewire_rom_id[i] = 0; + } +} + +uint8_t OneWireSearch(uint8_t *newAddr) +{ + uint8_t id_bit_number = 1; + uint8_t last_zero = 0; + uint8_t rom_byte_number = 0; + uint8_t search_result = 0; + uint8_t id_bit; + uint8_t cmp_id_bit; + unsigned char rom_byte_mask = 1; + unsigned char search_direction; + + if (!onewire_last_device_flag) { + if (!OneWireReset()) { + onewire_last_discrepancy = 0; + onewire_last_device_flag = false; + onewire_last_family_discrepancy = 0; + return false; + } + OneWireWrite(W1_SEARCH_ROM); + do { + id_bit = OneWireReadBit(); + cmp_id_bit = OneWireReadBit(); + + if ((id_bit == 1) && (cmp_id_bit == 1)) { + break; + } else { + if (id_bit != cmp_id_bit) { + search_direction = id_bit; + } else { + if (id_bit_number < onewire_last_discrepancy) { + search_direction = ((onewire_rom_id[rom_byte_number] & rom_byte_mask) > 0); + } else { + search_direction = (id_bit_number == onewire_last_discrepancy); + } + if (search_direction == 0) { + last_zero = id_bit_number; + if (last_zero < 9) { + onewire_last_family_discrepancy = last_zero; + } + } + } + if (search_direction == 1) { + onewire_rom_id[rom_byte_number] |= rom_byte_mask; + } else { + onewire_rom_id[rom_byte_number] &= ~rom_byte_mask; + } + OneWireWriteBit(search_direction); + id_bit_number++; + rom_byte_mask <<= 1; + if (rom_byte_mask == 0) { + rom_byte_number++; + rom_byte_mask = 1; + } + } + } while (rom_byte_number < 8); + if (!(id_bit_number < 65)) { + onewire_last_discrepancy = last_zero; + if (onewire_last_discrepancy == 0) { + onewire_last_device_flag = true; + } + search_result = true; + } + } + if (!search_result || !onewire_rom_id[0]) { + onewire_last_discrepancy = 0; + onewire_last_device_flag = false; + onewire_last_family_discrepancy = 0; + search_result = false; + } + for (uint32_t i = 0; i < 8; i++) { + newAddr[i] = onewire_rom_id[i]; + } + return search_result; +} + +bool OneWireCrc8(uint8_t *addr) +{ + uint8_t crc = 0; + uint8_t len = 8; + + while (len--) { + uint8_t inbyte = *addr++; + for (uint32_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) { + crc ^= 0x8C; + } + inbyte >>= 1; + } + } + return (crc == *addr); +} + + + +void Ds18x20Init(void) +{ + uint64_t ids[DS18X20_MAX_SENSORS]; + + ds18x20_pin = pin[GPIO_DSB]; + if (pin[GPIO_DSB_OUT] < 99) { + ds18x20_pin_out = pin[GPIO_DSB_OUT]; + ds18x20_dual_mode = true; + pinMode(ds18x20_pin_out, OUTPUT); + pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); + } + + OneWireResetSearch(); + + ds18x20_sensors = 0; + while (ds18x20_sensors < DS18X20_MAX_SENSORS) { + if (!OneWireSearch(ds18x20_sensor[ds18x20_sensors].address)) { + break; + } + if (OneWireCrc8(ds18x20_sensor[ds18x20_sensors].address) && + ((ds18x20_sensor[ds18x20_sensors].address[0] == DS18S20_CHIPID) || + (ds18x20_sensor[ds18x20_sensors].address[0] == DS1822_CHIPID) || + (ds18x20_sensor[ds18x20_sensors].address[0] == DS18B20_CHIPID) || + (ds18x20_sensor[ds18x20_sensors].address[0] == MAX31850_CHIPID))) { + ds18x20_sensor[ds18x20_sensors].index = ds18x20_sensors; + ids[ds18x20_sensors] = ds18x20_sensor[ds18x20_sensors].address[0]; + for (uint32_t j = 6; j > 0; j--) { + ids[ds18x20_sensors] = ids[ds18x20_sensors] << 8 | ds18x20_sensor[ds18x20_sensors].address[j]; + } + ds18x20_sensors++; + } + } + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + for (uint32_t j = i + 1; j < ds18x20_sensors; j++) { + if (ids[ds18x20_sensor[i].index] > ids[ds18x20_sensor[j].index]) { + std::swap(ds18x20_sensor[i].index, ds18x20_sensor[j].index); + } + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); +} + +void Ds18x20Convert(void) +{ + OneWireReset(); +#ifdef W1_PARASITE_POWER + + if (++ds18x20_sensor_curr >= ds18x20_sensors) + ds18x20_sensor_curr = 0; + OneWireSelect(ds18x20_sensor[ds18x20_sensor_curr].address); +#else + OneWireWrite(W1_SKIP_ROM); +#endif + OneWireWrite(W1_CONVERT_TEMP); + +} + +bool Ds18x20Read(uint8_t sensor) +{ + uint8_t data[9]; + int8_t sign = 1; + + uint8_t index = ds18x20_sensor[sensor].index; + if (ds18x20_sensor[index].valid) { ds18x20_sensor[index].valid--; } + for (uint32_t retry = 0; retry < 3; retry++) { + OneWireReset(); + OneWireSelect(ds18x20_sensor[index].address); + OneWireWrite(W1_READ_SCRATCHPAD); + for (uint32_t i = 0; i < 9; i++) { + data[i] = OneWireRead(); + } + if (OneWireCrc8(data)) { + switch(ds18x20_sensor[index].address[0]) { + case DS18S20_CHIPID: { + if (data[1] > 0x80) { + data[0] = (~data[0]) +1; + sign = -1; + } + float temp9 = (float)(data[0] >> 1) * sign; + ds18x20_sensor[index].temperature = ConvertTemp((temp9 - 0.25) + ((16.0 - data[6]) / 16.0)); + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; + } + case DS1822_CHIPID: + case DS18B20_CHIPID: { + if (data[4] != 0x7F) { + data[4] = 0x7F; + OneWireReset(); + OneWireSelect(ds18x20_sensor[index].address); + OneWireWrite(W1_WRITE_SCRATCHPAD); + OneWireWrite(data[2]); + OneWireWrite(data[3]); + OneWireWrite(data[4]); + OneWireSelect(ds18x20_sensor[index].address); + OneWireWrite(W1_WRITE_EEPROM); +#ifdef W1_PARASITE_POWER + w1_power_until = millis() + 10; +#endif + } + uint16_t temp12 = (data[1] << 8) + data[0]; + if (temp12 > 2047) { + temp12 = (~temp12) +1; + sign = -1; + } + ds18x20_sensor[index].temperature = ConvertTemp(sign * temp12 * 0.0625); + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; + } + case MAX31850_CHIPID: { + int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC); + ds18x20_sensor[index].temperature = ConvertTemp(temp14 * 0.0625); + ds18x20_sensor[index].valid = SENSOR_MAX_MISS; + return true; + } + } + } + } + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); + return false; +} + +void Ds18x20Name(uint8_t sensor) +{ + uint8_t index = sizeof(ds18x20_chipids); + while (index) { + if (ds18x20_sensor[ds18x20_sensor[sensor].index].address[0] == ds18x20_chipids[index]) { + break; + } + index--; + } + GetTextIndexed(ds18x20_types, sizeof(ds18x20_types), index, kDs18x20Types); + if (ds18x20_sensors > 1) { + snprintf_P(ds18x20_types, sizeof(ds18x20_types), PSTR("%s%c%d"), ds18x20_types, IndexSeparator(), sensor +1); + } +} + + + +void Ds18x20EverySecond(void) +{ +#ifdef W1_PARASITE_POWER + + unsigned long now = millis(); + if (now < w1_power_until) + return; +#endif + if (uptime & 1 +#ifdef W1_PARASITE_POWER + + || ds18x20_sensors >= 2 +#endif + ) { + + Ds18x20Convert(); + } else { + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + + if (!Ds18x20Read(i)) { + Ds18x20Name(i); + AddLogMissed(ds18x20_types, ds18x20_sensor[ds18x20_sensor[i].index].valid); +#ifdef USE_DS18x20_RECONFIGURE + if (!ds18x20_sensor[ds18x20_sensor[i].index].valid) { + memset(&ds18x20_sensor, 0, sizeof(ds18x20_sensor)); + Ds18x20Init(); + } +#endif + } + } + } +} + +void Ds18x20Show(bool json) +{ + for (uint32_t i = 0; i < ds18x20_sensors; i++) { + uint8_t index = ds18x20_sensor[i].index; + + if (ds18x20_sensor[index].valid) { + char temperature[33]; + dtostrfd(ds18x20_sensor[index].temperature, Settings.flag2.temperature_resolution, temperature); + + Ds18x20Name(i); + + if (json) { + char address[17]; + for (uint32_t j = 0; j < 6; j++) { + sprintf(address+2*j, "%02X", ds18x20_sensor[index].address[6-j]); + } + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), ds18x20_types, address, temperature); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzSensor(DZ_TEMP, temperature); + } +#endif +#ifdef USE_KNX + if ((0 == tele_period) && (0 == i)) { + KnxSensor(KNX_TEMPERATURE, ds18x20_sensor[index].temperature); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, ds18x20_types, temperature, TempUnit()); +#endif + } + } + } +} + + + + + +bool Xsns05(uint8_t function) +{ + bool result = false; + + if (pin[GPIO_DSB] < 99) { + switch (function) { + case FUNC_INIT: + Ds18x20Init(); + break; + case FUNC_EVERY_SECOND: + Ds18x20EverySecond(); + break; + case FUNC_JSON_APPEND: + Ds18x20Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ds18x20Show(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_old.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_old.ino" +#ifdef USE_DHT_OLD +# 29 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_old.ino" +#define XSNS_06 6 + +#define DHT_MAX_SENSORS 4 +#define DHT_MAX_RETRY 8 + +uint32_t dht_max_cycles; +uint8_t dht_data[5]; +uint8_t dht_sensors = 0; +uint8_t dht_pin_out = 0; +bool dht_active = true; +bool dht_dual_mode = false; + +struct DHTSTRUCT { + uint8_t pin; + uint8_t type; + char stype[12]; + uint32_t lastreadtime; + uint8_t lastresult; + float t = NAN; + float h = NAN; +} Dht[DHT_MAX_SENSORS]; + +void DhtReadPrep(void) +{ + for (uint32_t i = 0; i < dht_sensors; i++) { + if (!dht_dual_mode) { + digitalWrite(Dht[i].pin, HIGH); + } else { + digitalWrite(dht_pin_out, HIGH); + } + } +} + +int32_t DhtExpectPulse(uint8_t sensor, bool level) +{ + int32_t count = 0; + + while (digitalRead(Dht[sensor].pin) == level) { + if (count++ >= (int32_t)dht_max_cycles) { + return -1; + } + } + return count; +} + +bool DhtRead(uint8_t sensor) +{ + int32_t cycles[80]; + uint8_t error = 0; + + dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; + + + + + if (Dht[sensor].lastresult > DHT_MAX_RETRY) { + Dht[sensor].lastresult = 0; + if (!dht_dual_mode) { + digitalWrite(Dht[sensor].pin, HIGH); + } else { + digitalWrite(dht_pin_out, HIGH); + } + delay(250); + } + if (!dht_dual_mode) { + pinMode(Dht[sensor].pin, OUTPUT); + digitalWrite(Dht[sensor].pin, LOW); + } else { + digitalWrite(dht_pin_out, LOW); + } + + if (GPIO_SI7021 == Dht[sensor].type) { + delayMicroseconds(500); + } else { + delay(20); + } + + noInterrupts(); + if (!dht_dual_mode) { + digitalWrite(Dht[sensor].pin, HIGH); + delayMicroseconds(40); + pinMode(Dht[sensor].pin, INPUT_PULLUP); + } else { + digitalWrite(dht_pin_out, HIGH); + delayMicroseconds(40); + } + delayMicroseconds(10); + if (-1 == DhtExpectPulse(sensor, LOW)) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_LOW " " D_PULSE)); + error = 1; + } + else if (-1 == DhtExpectPulse(sensor, HIGH)) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_HIGH " " D_PULSE)); + error = 1; + } + else { + for (uint32_t i = 0; i < 80; i += 2) { + cycles[i] = DhtExpectPulse(sensor, LOW); + cycles[i+1] = DhtExpectPulse(sensor, HIGH); + } + } + interrupts(); + + if (error) { return false; } + + for (uint32_t i = 0; i < 40; ++i) { + int32_t lowCycles = cycles[2*i]; + int32_t highCycles = cycles[2*i+1]; + if ((-1 == lowCycles) || (-1 == highCycles)) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_PULSE)); + return false; + } + dht_data[i/8] <<= 1; + if (highCycles > lowCycles) { + dht_data[i / 8] |= 1; + } + } + + uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; + if (dht_data[4] != checksum) { + char hex_char[15]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), + ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); + return false; + } + + return true; +} + +void DhtReadTempHum(uint8_t sensor) +{ + if ((NAN == Dht[sensor].h) || (Dht[sensor].lastresult > DHT_MAX_RETRY)) { + Dht[sensor].t = NAN; + Dht[sensor].h = NAN; + } + if (DhtRead(sensor)) { + switch (Dht[sensor].type) { + case GPIO_DHT11: + Dht[sensor].h = dht_data[0]; + Dht[sensor].t = dht_data[2] + ((float)dht_data[3] * 0.1f); + break; + case GPIO_DHT22: + case GPIO_SI7021: + Dht[sensor].h = ((dht_data[0] << 8) | dht_data[1]) * 0.1; + Dht[sensor].t = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; + if (dht_data[2] & 0x80) { + Dht[sensor].t *= -1; + } + break; + } + Dht[sensor].t = ConvertTemp(Dht[sensor].t); + Dht[sensor].h = ConvertHumidity(Dht[sensor].h); + Dht[sensor].lastresult = 0; + } else { + Dht[sensor].lastresult++; + } +} + + + +bool DhtPinState() +{ + if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { + if (dht_sensors < DHT_MAX_SENSORS) { + Dht[dht_sensors].pin = XdrvMailbox.payload; + Dht[dht_sensors].type = XdrvMailbox.index; + dht_sensors++; + XdrvMailbox.index = GPIO_DHT11; + } else { + XdrvMailbox.index = 0; + } + return true; + } + return false; +} + +void DhtInit(void) +{ + if (dht_sensors) { + dht_max_cycles = microsecondsToClockCycles(1000); + + if (pin[GPIO_DHT11_OUT] < 99) { + dht_pin_out = pin[GPIO_DHT11_OUT]; + dht_dual_mode = true; + dht_sensors = 1; + pinMode(dht_pin_out, OUTPUT); + } + + for (uint32_t i = 0; i < dht_sensors; i++) { + pinMode(Dht[i].pin, INPUT_PULLUP); + Dht[i].lastreadtime = 0; + Dht[i].lastresult = 0; + GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); + if (dht_sensors > 1) { + snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_SENSORS_FOUND " %d"), dht_sensors); + } else { + dht_active = false; + } +} + +void DhtEverySecond(void) +{ + if (uptime &1) { + + DhtReadPrep(); + } else { + for (uint32_t i = 0; i < dht_sensors; i++) { + + DhtReadTempHum(i); + } + } +} + +void DhtShow(bool json) +{ + for (uint32_t i = 0; i < dht_sensors; i++) { + char temperature[33]; + dtostrfd(Dht[i].t, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(Dht[i].h, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, Dht[i].stype, temperature, humidity); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif +#ifdef USE_KNX + if ((0 == tele_period) && (0 == i)) { + KnxSensor(KNX_TEMPERATURE, Dht[i].t); + KnxSensor(KNX_HUMIDITY, Dht[i].h); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, Dht[i].stype, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, Dht[i].stype, humidity); +#endif + } + } +} + + + + + +bool Xsns06(uint8_t function) +{ + bool result = false; + + if (dht_active) { + switch (function) { + case FUNC_EVERY_SECOND: + DhtEverySecond(); + break; + case FUNC_JSON_APPEND: + DhtShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DhtShow(0); + break; +#endif + case FUNC_INIT: + DhtInit(); + break; + case FUNC_PIN_STATE: + result = DhtPinState(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v2.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v2.ino" +#ifdef USE_DHT_V2 +# 29 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v2.ino" +#define XSNS_06 6 + +#define DHT_MAX_SENSORS 4 +#define DHT_MAX_RETRY 8 + +uint32_t dht_max_cycles; +uint8_t dht_data[5]; +uint8_t dht_sensors = 0; +uint8_t dht_pin_out = 0; +bool dht_active = true; +bool dht_dual_mode = false; + +struct DHTSTRUCT { + uint8_t pin; + uint8_t type; + char stype[12]; + uint32_t lastreadtime; + uint8_t lastresult; + float t = NAN; + float h = NAN; +} Dht[DHT_MAX_SENSORS]; + +void DhtReadPrep(void) +{ + for (uint32_t i = 0; i < dht_sensors; i++) { + if (!dht_dual_mode) { + digitalWrite(Dht[i].pin, HIGH); + } else { + digitalWrite(dht_pin_out, HIGH); + } + } +} + +int32_t DhtExpectPulse(uint8_t sensor, bool level) +{ + int32_t count = 0; + + while (digitalRead(Dht[sensor].pin) == level) { + if (count++ >= (int32_t)dht_max_cycles) { + return -1; + } + } + return count; +} + +bool DhtRead(uint8_t sensor) +{ + int32_t cycles[80]; + uint8_t error = 0; + + dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; + + if (Dht[sensor].lastresult > DHT_MAX_RETRY) { + Dht[sensor].lastresult = 0; + if (!dht_dual_mode) { + digitalWrite(Dht[sensor].pin, HIGH); + } else { + digitalWrite(dht_pin_out, HIGH); + } + delay(250); + } + + + noInterrupts(); + if (!dht_dual_mode) { + pinMode(Dht[sensor].pin, OUTPUT); + digitalWrite(Dht[sensor].pin, LOW); + } else { + digitalWrite(dht_pin_out, LOW); + } + + switch (Dht[sensor].type) { + case GPIO_SI7021: +# 114 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v2.ino" + delayMicroseconds(500); + if (!dht_dual_mode) { + digitalWrite(Dht[sensor].pin, HIGH); + } else { + digitalWrite(dht_pin_out, HIGH); + } + delayMicroseconds(40); + break; + + case GPIO_DHT22: +# 133 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v2.ino" + delayMicroseconds(1100); + if (!dht_dual_mode) { + digitalWrite(Dht[sensor].pin, HIGH); + } else { + digitalWrite(dht_pin_out, HIGH); + } + delayMicroseconds(30); + break; + + case GPIO_DHT11: +# 151 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v2.ino" + default: + + delay(20); + if (!dht_dual_mode) { + digitalWrite(Dht[sensor].pin, HIGH); + } else { + digitalWrite(dht_pin_out, HIGH); + } + delayMicroseconds(30); + break; + } + + + pinMode(Dht[sensor].pin, INPUT_PULLUP); + + if (-1 == DhtExpectPulse(sensor, LOW)) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_LOW " " D_PULSE)); + error = 1; + } + else if (-1 == DhtExpectPulse(sensor, HIGH)) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_HIGH " " D_PULSE)); + error = 1; + } + else { + for (uint32_t i = 0; i < 80; i += 2) { + cycles[i] = DhtExpectPulse(sensor, LOW); + cycles[i+1] = DhtExpectPulse(sensor, HIGH); + } + } + interrupts(); + if (error) { return false; } + + + for (uint32_t i = 0; i < 40; ++i) { + int32_t lowCycles = cycles[2*i]; + int32_t highCycles = cycles[2*i+1]; + if ((-1 == lowCycles) || (-1 == highCycles)) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_PULSE)); + return false; + } + dht_data[i/8] <<= 1; + if (highCycles > lowCycles) { + dht_data[i / 8] |= 1; + } + } + + + uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; + if (dht_data[4] != checksum) { + char hex_char[15]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), + ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); + return false; + } + + return true; +} + +void DhtReadTempHum(uint8_t sensor) +{ + if ((NAN == Dht[sensor].h) || (Dht[sensor].lastresult > DHT_MAX_RETRY)) { + Dht[sensor].t = NAN; + Dht[sensor].h = NAN; + } + if (DhtRead(sensor)) { + switch (Dht[sensor].type) { + case GPIO_DHT11: + Dht[sensor].h = dht_data[0]; + Dht[sensor].t = dht_data[2] + ((float)dht_data[3] * 0.1f); + break; + case GPIO_DHT22: + case GPIO_SI7021: + Dht[sensor].h = ((dht_data[0] << 8) | dht_data[1]) * 0.1; + Dht[sensor].t = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; + if (dht_data[2] & 0x80) { + Dht[sensor].t *= -1; + } + break; + } + Dht[sensor].t = ConvertTemp(Dht[sensor].t); + Dht[sensor].h = ConvertHumidity(Dht[sensor].h); + Dht[sensor].lastresult = 0; + } else { + Dht[sensor].lastresult++; + } +} + + + +bool DhtPinState() +{ + if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { + if (dht_sensors < DHT_MAX_SENSORS) { + Dht[dht_sensors].pin = XdrvMailbox.payload; + Dht[dht_sensors].type = XdrvMailbox.index; + dht_sensors++; + XdrvMailbox.index = GPIO_DHT11; + } else { + XdrvMailbox.index = 0; + } + return true; + } + return false; +} + +void DhtInit(void) +{ + if (dht_sensors) { + dht_max_cycles = microsecondsToClockCycles(1000); + + if (pin[GPIO_DHT11_OUT] < 99) { + dht_pin_out = pin[GPIO_DHT11_OUT]; + dht_dual_mode = true; + dht_sensors = 1; + pinMode(dht_pin_out, OUTPUT); + } + + for (uint32_t i = 0; i < dht_sensors; i++) { + pinMode(Dht[i].pin, INPUT_PULLUP); + Dht[i].lastreadtime = 0; + Dht[i].lastresult = 0; + GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); + if (dht_sensors > 1) { + snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v2) " D_SENSORS_FOUND " %d"), dht_sensors); + } else { + dht_active = false; + } +} + +void DhtEverySecond(void) +{ + if (uptime &1) { + + DhtReadPrep(); + } else { + for (uint32_t i = 0; i < dht_sensors; i++) { + + DhtReadTempHum(i); + } + } +} + +void DhtShow(bool json) +{ + for (uint32_t i = 0; i < dht_sensors; i++) { + char temperature[33]; + dtostrfd(Dht[i].t, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(Dht[i].h, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, Dht[i].stype, temperature, humidity); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif +#ifdef USE_KNX + if ((0 == tele_period) && (0 == i)) { + KnxSensor(KNX_TEMPERATURE, Dht[i].t); + KnxSensor(KNX_HUMIDITY, Dht[i].h); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, Dht[i].stype, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, Dht[i].stype, humidity); +#endif + } + } +} + + + + + +bool Xsns06(uint8_t function) +{ + bool result = false; + + if (dht_active) { + switch (function) { + case FUNC_EVERY_SECOND: + DhtEverySecond(); + break; + case FUNC_JSON_APPEND: + DhtShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DhtShow(0); + break; +#endif + case FUNC_INIT: + DhtInit(); + break; + case FUNC_PIN_STATE: + result = DhtPinState(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v3.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v3.ino" +#ifdef USE_DHT_V3 +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v3.ino" +#define XSNS_06 6 + +#define DHT_MAX_SENSORS 4 +#define DHT_MAX_RETRY 8 + +uint8_t dht_data[5]; +uint8_t dht_sensors = 0; +uint8_t dht_pin_out = 0; +bool dht_active = true; +bool dht_dual_mode = false; + +struct DHTSTRUCT { + uint8_t pin; + uint8_t type; + char stype[12]; + uint32_t lastreadtime; + uint8_t lastresult; + float t = NAN; + float h = NAN; +} Dht[DHT_MAX_SENSORS]; + +bool DhtExpectPulse(uint8_t sensor, int level) +{ + unsigned long timeout = micros() + 100; + while (digitalRead(Dht[sensor].pin) != level) { + if (micros() > timeout) { return false; } + delayMicroseconds(1); + } + return true; +} + +int DhtReadDat(uint8_t sensor) +{ + uint8_t result = 0; + for (uint32_t i = 0; i < 8; i++) { + if (!DhtExpectPulse(sensor, HIGH)) { return -1; } + + delayMicroseconds(35); + if (digitalRead(Dht[sensor].pin)) { + result |= (1 << (7 - i)); + } + + if (!DhtExpectPulse(sensor, LOW)) { return -1; } + } + return result; +} + +bool DhtRead(uint8_t sensor) +{ + dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; + + if (!dht_dual_mode) { + pinMode(Dht[sensor].pin, OUTPUT); + digitalWrite(Dht[sensor].pin, LOW); + } else { + digitalWrite(dht_pin_out, LOW); + } + + switch (Dht[sensor].type) { + case GPIO_DHT11: + delay(19); + break; + case GPIO_DHT22: + delay(2); + break; + case GPIO_SI7021: + delayMicroseconds(500); + break; + } + + if (!dht_dual_mode) { + pinMode(Dht[sensor].pin, INPUT_PULLUP); + } else { + digitalWrite(dht_pin_out, HIGH); + } + + switch (Dht[sensor].type) { + case GPIO_DHT11: + case GPIO_DHT22: + delayMicroseconds(50); + break; + case GPIO_SI7021: + + delayMicroseconds(20); + break; + } + + noInterrupts(); + if (!DhtExpectPulse(sensor, LOW)) { + interrupts(); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_LOW " " D_PULSE)); + return false; + } + if (!DhtExpectPulse(sensor, HIGH)) { + interrupts(); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_HIGH " " D_PULSE)); + return false; + } + if (!DhtExpectPulse(sensor, LOW)) { + interrupts(); + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_LOW " " D_PULSE)); + return false; + } + + int data = 0; + for (uint32_t i = 0; i < 5; i++) { + data = DhtReadDat(sensor); + if (-1 == data) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_PULSE)); + break; + } + dht_data[i] = data; + } + interrupts(); + if (-1 == data) { return false; } + + uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; + if (dht_data[4] != checksum) { + char hex_char[15]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), + ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); + return false; + } + + return true; +} + +void DhtReadTempHum(uint8_t sensor) +{ + if ((NAN == Dht[sensor].h) || (Dht[sensor].lastresult > DHT_MAX_RETRY)) { + Dht[sensor].t = NAN; + Dht[sensor].h = NAN; + } + if (DhtRead(sensor)) { + switch (Dht[sensor].type) { + case GPIO_DHT11: + Dht[sensor].h = dht_data[0]; + Dht[sensor].t = dht_data[2] + ((float)dht_data[3] * 0.1f); + break; + case GPIO_DHT22: + case GPIO_SI7021: + Dht[sensor].h = ((dht_data[0] << 8) | dht_data[1]) * 0.1; + Dht[sensor].t = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; + if (dht_data[2] & 0x80) { + Dht[sensor].t *= -1; + } + break; + } + Dht[sensor].t = ConvertTemp(Dht[sensor].t); + Dht[sensor].h = ConvertHumidity(Dht[sensor].h); + Dht[sensor].lastresult = 0; + } else { + Dht[sensor].lastresult++; + } +} + + + +bool DhtPinState() +{ + if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { + if (dht_sensors < DHT_MAX_SENSORS) { + Dht[dht_sensors].pin = XdrvMailbox.payload; + Dht[dht_sensors].type = XdrvMailbox.index; + dht_sensors++; + XdrvMailbox.index = GPIO_DHT11; + } else { + XdrvMailbox.index = 0; + } + return true; + } + return false; +} + +void DhtInit(void) +{ + if (dht_sensors) { + if (pin[GPIO_DHT11_OUT] < 99) { + dht_pin_out = pin[GPIO_DHT11_OUT]; + dht_dual_mode = true; + dht_sensors = 1; + pinMode(dht_pin_out, OUTPUT); + } + + for (uint32_t i = 0; i < dht_sensors; i++) { + pinMode(Dht[i].pin, INPUT_PULLUP); + Dht[i].lastreadtime = 0; + Dht[i].lastresult = 0; + GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); + if (dht_sensors > 1) { + snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v3) " D_SENSORS_FOUND " %d"), dht_sensors); + } else { + dht_active = false; + } +} + +void DhtEverySecond(void) +{ + if (uptime &1) { + + + } else { + for (uint32_t i = 0; i < dht_sensors; i++) { + + DhtReadTempHum(i); + } + } +} + +void DhtShow(bool json) +{ + for (uint32_t i = 0; i < dht_sensors; i++) { + char temperature[33]; + dtostrfd(Dht[i].t, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(Dht[i].h, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, Dht[i].stype, temperature, humidity); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif +#ifdef USE_KNX + if ((0 == tele_period) && (0 == i)) { + KnxSensor(KNX_TEMPERATURE, Dht[i].t); + KnxSensor(KNX_HUMIDITY, Dht[i].h); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, Dht[i].stype, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, Dht[i].stype, humidity); +#endif + } + } +} + + + + + +bool Xsns06(uint8_t function) +{ + bool result = false; + + if (dht_active) { + switch (function) { + case FUNC_EVERY_SECOND: + DhtEverySecond(); + break; + case FUNC_JSON_APPEND: + DhtShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DhtShow(0); + break; +#endif + case FUNC_INIT: + DhtInit(); + break; + case FUNC_PIN_STATE: + result = DhtPinState(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v4.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v4.ino" +#ifdef USE_DHT_V4 +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v4.ino" +#define XSNS_06 6 + +#define DHT_MAX_SENSORS 4 +#define DHT_MAX_RETRY 8 + +uint8_t dht_data[5]; +uint8_t dht_sensors = 0; +uint8_t dht_pin_out = 0; +bool dht_active = true; +bool dht_dual_mode = false; + +struct DHTSTRUCT { + uint8_t pin; + uint8_t type; + char stype[12]; + uint32_t lastreadtime; + uint8_t lastresult; + float t = NAN; + float h = NAN; +} Dht[DHT_MAX_SENSORS]; + +bool DhtExpectPulse(uint32_t sensor, uint32_t level) +{ + unsigned long timeout = micros() + 100; + while (digitalRead(Dht[sensor].pin) != level) { + if (micros() > timeout) { return false; } + delayMicroseconds(1); + } + return true; +} + +bool DhtRead(uint32_t sensor) +{ + dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; + + if (!dht_dual_mode) { + pinMode(Dht[sensor].pin, OUTPUT); + digitalWrite(Dht[sensor].pin, LOW); + } else { + digitalWrite(dht_pin_out, LOW); + } + + switch (Dht[sensor].type) { + case GPIO_DHT11: + delay(19); + break; + case GPIO_DHT22: + delay(2); + break; + case GPIO_SI7021: + delayMicroseconds(500); + break; + } + + if (!dht_dual_mode) { + pinMode(Dht[sensor].pin, INPUT_PULLUP); + } else { + digitalWrite(dht_pin_out, HIGH); + } + + switch (Dht[sensor].type) { + case GPIO_DHT11: + case GPIO_DHT22: + delayMicroseconds(50); + break; + case GPIO_SI7021: + delayMicroseconds(20); + break; + } + + uint32_t level = 9; + noInterrupts(); + for (uint32_t i = 0; i < 3; i++) { + level = i &1; + if (!DhtExpectPulse(sensor, level)) { break; } + level = 9; + } + if (9 == level) { + int data = 0; + for (uint32_t i = 0; i < 5; i++) { + data = 0; + for (uint32_t j = 0; j < 8; j++) { + level = 1; + if (!DhtExpectPulse(sensor, level)) { break; } + + delayMicroseconds(35); + if (digitalRead(Dht[sensor].pin)) { + data |= (1 << (7 - j)); + } + + level = 0; + if (!DhtExpectPulse(sensor, level)) { break; } + level = 9; + } + if (level < 2) { break; } + + dht_data[i] = data; + } + } + interrupts(); + if (level < 2) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), (0 == level) ? D_START_SIGNAL_LOW : D_START_SIGNAL_HIGH); + return false; + } + + uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; + if (dht_data[4] != checksum) { + char hex_char[15]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), + ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); + return false; + } + + return true; +} + +void DhtReadTempHum(uint32_t sensor) +{ + if ((NAN == Dht[sensor].h) || (Dht[sensor].lastresult > DHT_MAX_RETRY)) { + Dht[sensor].t = NAN; + Dht[sensor].h = NAN; + } + if (DhtRead(sensor)) { + switch (Dht[sensor].type) { + case GPIO_DHT11: + Dht[sensor].h = dht_data[0]; + Dht[sensor].t = dht_data[2] + ((float)dht_data[3] * 0.1f); + break; + case GPIO_DHT22: + case GPIO_SI7021: + Dht[sensor].h = ((dht_data[0] << 8) | dht_data[1]) * 0.1; + Dht[sensor].t = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; + if (dht_data[2] & 0x80) { + Dht[sensor].t *= -1; + } + break; + } + Dht[sensor].t = ConvertTemp(Dht[sensor].t); + if (Dht[sensor].h > 100) { Dht[sensor].h = 100.0; } + if (Dht[sensor].h < 0) { Dht[sensor].h = 0.0; } + Dht[sensor].h = ConvertHumidity(Dht[sensor].h); + Dht[sensor].lastresult = 0; + } else { + Dht[sensor].lastresult++; + } +} + + + +bool DhtPinState() +{ + if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { + if (dht_sensors < DHT_MAX_SENSORS) { + Dht[dht_sensors].pin = XdrvMailbox.payload; + Dht[dht_sensors].type = XdrvMailbox.index; + dht_sensors++; + XdrvMailbox.index = GPIO_DHT11; + } else { + XdrvMailbox.index = 0; + } + return true; + } + return false; +} + +void DhtInit(void) +{ + if (dht_sensors) { + if (pin[GPIO_DHT11_OUT] < 99) { + dht_pin_out = pin[GPIO_DHT11_OUT]; + dht_dual_mode = true; + dht_sensors = 1; + pinMode(dht_pin_out, OUTPUT); + } + + for (uint32_t i = 0; i < dht_sensors; i++) { + pinMode(Dht[i].pin, INPUT_PULLUP); + Dht[i].lastreadtime = 0; + Dht[i].lastresult = 0; + GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); + if (dht_sensors > 1) { + snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v4) " D_SENSORS_FOUND " %d"), dht_sensors); + } else { + dht_active = false; + } +} + +void DhtEverySecond(void) +{ + if (uptime &1) { + } else { + for (uint32_t i = 0; i < dht_sensors; i++) { + + DhtReadTempHum(i); + } + } +} + +void DhtShow(bool json) +{ + for (uint32_t i = 0; i < dht_sensors; i++) { + char temperature[33]; + dtostrfd(Dht[i].t, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(Dht[i].h, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, Dht[i].stype, temperature, humidity); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif +#ifdef USE_KNX + if ((0 == tele_period) && (0 == i)) { + KnxSensor(KNX_TEMPERATURE, Dht[i].t); + KnxSensor(KNX_HUMIDITY, Dht[i].h); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, Dht[i].stype, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, Dht[i].stype, humidity); +#endif + } + } +} + + + + + +bool Xsns06(uint8_t function) +{ + bool result = false; + + if (dht_active) { + switch (function) { + case FUNC_EVERY_SECOND: + DhtEverySecond(); + break; + case FUNC_JSON_APPEND: + DhtShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DhtShow(0); + break; +#endif + case FUNC_INIT: + DhtInit(); + break; + case FUNC_PIN_STATE: + result = DhtPinState(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v5.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v5.ino" +#ifdef USE_DHT +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v5.ino" +#define XSNS_06 6 + +#define DHT_MAX_SENSORS 4 +#define DHT_MAX_RETRY 8 + +uint8_t dht_data[5]; +uint8_t dht_sensors = 0; +uint8_t dht_pin_out = 0; +bool dht_active = true; +bool dht_dual_mode = false; + +struct DHTSTRUCT { + uint8_t pin; + uint8_t type; + uint8_t lastresult; + char stype[12]; + float t = NAN; + float h = NAN; +} Dht[DHT_MAX_SENSORS]; + +bool DhtWaitState(uint32_t sensor, uint32_t level) +{ + unsigned long timeout = micros() + 100; + while (digitalRead(Dht[sensor].pin) != level) { + if (TimeReachedUsec(timeout)) { + PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), + (level) ? D_START_SIGNAL_HIGH : D_START_SIGNAL_LOW); + return false; + } + delayMicroseconds(1); + } + return true; +} + +bool DhtRead(uint32_t sensor) +{ + dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; + + if (!dht_dual_mode) { + pinMode(Dht[sensor].pin, OUTPUT); + digitalWrite(Dht[sensor].pin, LOW); + } else { + digitalWrite(dht_pin_out, LOW); + } + + switch (Dht[sensor].type) { + case GPIO_DHT11: + delay(19); + break; + case GPIO_DHT22: + delay(2); + break; + case GPIO_SI7021: + delayMicroseconds(500); + break; + } + + if (!dht_dual_mode) { + pinMode(Dht[sensor].pin, INPUT_PULLUP); + } else { + digitalWrite(dht_pin_out, HIGH); + } + + switch (Dht[sensor].type) { + case GPIO_DHT11: + case GPIO_DHT22: + delayMicroseconds(50); + break; + case GPIO_SI7021: + delayMicroseconds(20); + break; + } + + bool error = false; + noInterrupts(); + if (DhtWaitState(sensor, 0) && DhtWaitState(sensor, 1) && DhtWaitState(sensor, 0)) { + for (uint32_t i = 0; i < 5; i++) { + int data = 0; + for (uint32_t j = 0; j < 8; j++) { + if (!DhtWaitState(sensor, 1)) { + error = true; + break; + } + delayMicroseconds(35); + if (digitalRead(Dht[sensor].pin)) { + data |= (1 << (7 - j)); + } + if (!DhtWaitState(sensor, 0)) { + error = true; + break; + } + } + if (error) { break; } + dht_data[i] = data; + } + } else { + error = true; + } + interrupts(); + if (error) { return false; } + + uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; + if (dht_data[4] != checksum) { + char hex_char[15]; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), + ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); + return false; + } + + float temperature = NAN; + float humidity = NAN; + switch (Dht[sensor].type) { + case GPIO_DHT11: + humidity = dht_data[0]; + temperature = dht_data[2] + ((float)dht_data[3] * 0.1f); + break; + case GPIO_DHT22: + case GPIO_SI7021: + humidity = ((dht_data[0] << 8) | dht_data[1]) * 0.1; + temperature = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; + if (dht_data[2] & 0x80) { + temperature *= -1; + } + break; + } + if (isnan(temperature) || isnan(humidity)) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Invalid NAN reading")); + return false; + } + + if (humidity > 100) { humidity = 100.0; } + if (humidity < 0) { humidity = 0.1; } + Dht[sensor].h = ConvertHumidity(humidity); + Dht[sensor].t = ConvertTemp(temperature); + Dht[sensor].lastresult = 0; + + return true; +} + + + +bool DhtPinState() +{ + if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { + if (dht_sensors < DHT_MAX_SENSORS) { + Dht[dht_sensors].pin = XdrvMailbox.payload; + Dht[dht_sensors].type = XdrvMailbox.index; + dht_sensors++; + XdrvMailbox.index = GPIO_DHT11; + } else { + XdrvMailbox.index = 0; + } + return true; + } + return false; +} + +void DhtInit(void) +{ + if (dht_sensors) { + if (pin[GPIO_DHT11_OUT] < 99) { + dht_pin_out = pin[GPIO_DHT11_OUT]; + dht_dual_mode = true; + dht_sensors = 1; + pinMode(dht_pin_out, OUTPUT); + } + + for (uint32_t i = 0; i < dht_sensors; i++) { + pinMode(Dht[i].pin, INPUT_PULLUP); + Dht[i].lastresult = DHT_MAX_RETRY; + GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); + if (dht_sensors > 1) { + snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); + } + } + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v5) " D_SENSORS_FOUND " %d"), dht_sensors); + } else { + dht_active = false; + } +} + +void DhtEverySecond(void) +{ + if (uptime &1) { + for (uint32_t sensor = 0; sensor < dht_sensors; sensor++) { + + if (!DhtRead(sensor)) { + Dht[sensor].lastresult++; + if (Dht[sensor].lastresult > DHT_MAX_RETRY) { + Dht[sensor].t = NAN; + Dht[sensor].h = NAN; + } + } + } + } +} + +void DhtShow(bool json) +{ + for (uint32_t i = 0; i < dht_sensors; i++) { + char temperature[33]; + dtostrfd(Dht[i].t, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(Dht[i].h, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, Dht[i].stype, temperature, humidity); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif +#ifdef USE_KNX + if ((0 == tele_period) && (0 == i)) { + KnxSensor(KNX_TEMPERATURE, Dht[i].t); + KnxSensor(KNX_HUMIDITY, Dht[i].h); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, Dht[i].stype, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, Dht[i].stype, humidity); +#endif + } + } +} + + + + + +bool Xsns06(uint8_t function) +{ + bool result = false; + + if (dht_active) { + switch (function) { + case FUNC_EVERY_SECOND: + DhtEverySecond(); + break; + case FUNC_JSON_APPEND: + DhtShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DhtShow(0); + break; +#endif + case FUNC_INIT: + DhtInit(); + break; + case FUNC_PIN_STATE: + result = DhtPinState(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_07_sht1x.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_07_sht1x.ino" +#ifdef USE_I2C +#ifdef USE_SHT +# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_07_sht1x.ino" +#define XSNS_07 7 +#define XI2C_08 8 + +enum { + SHT1X_CMD_MEASURE_TEMP = B00000011, + SHT1X_CMD_MEASURE_RH = B00000101, + SHT1X_CMD_SOFT_RESET = B00011110 +}; + +uint8_t sht_sda_pin; +uint8_t sht_scl_pin; +uint8_t sht_type = 0; +char sht_types[] = "SHT1X"; +uint8_t sht_valid = 0; +float sht_temperature = 0; +float sht_humidity = 0; + +bool ShtReset(void) +{ + pinMode(sht_sda_pin, INPUT_PULLUP); + pinMode(sht_scl_pin, OUTPUT); + delay(11); + for (uint32_t i = 0; i < 9; i++) { + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_scl_pin, LOW); + } + bool success = ShtSendCommand(SHT1X_CMD_SOFT_RESET); + delay(11); + return success; +} + +bool ShtSendCommand(const uint8_t cmd) +{ + pinMode(sht_sda_pin, OUTPUT); + + digitalWrite(sht_sda_pin, HIGH); + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_sda_pin, LOW); + digitalWrite(sht_scl_pin, LOW); + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_sda_pin, HIGH); + digitalWrite(sht_scl_pin, LOW); + + shiftOut(sht_sda_pin, sht_scl_pin, MSBFIRST, cmd); + + bool ackerror = false; + digitalWrite(sht_scl_pin, HIGH); + pinMode(sht_sda_pin, INPUT_PULLUP); + if (digitalRead(sht_sda_pin) != LOW) { + ackerror = true; + } + digitalWrite(sht_scl_pin, LOW); + delayMicroseconds(1); + if (digitalRead(sht_sda_pin) != HIGH) { + ackerror = true; + } + if (ackerror) { + + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_DID_NOT_ACK_COMMAND)); + } + return (!ackerror); +} + +bool ShtAwaitResult(void) +{ + + for (uint32_t i = 0; i < 16; i++) { + if (LOW == digitalRead(sht_sda_pin)) { + return true; + } + delay(20); + } + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_BUSY)); + + return false; +} + +int ShtReadData(void) +{ + int val = 0; + + + val = shiftIn(sht_sda_pin, sht_scl_pin, 8); + val <<= 8; + + pinMode(sht_sda_pin, OUTPUT); + digitalWrite(sht_sda_pin, LOW); + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_scl_pin, LOW); + pinMode(sht_sda_pin, INPUT_PULLUP); + + val |= shiftIn(sht_sda_pin, sht_scl_pin, 8); + + digitalWrite(sht_scl_pin, HIGH); + digitalWrite(sht_scl_pin, LOW); + return val; +} + +bool ShtRead(void) +{ + if (sht_valid) { sht_valid--; } + if (!ShtReset()) { return false; } + if (!ShtSendCommand(SHT1X_CMD_MEASURE_TEMP)) { return false; } + if (!ShtAwaitResult()) { return false; } + float tempRaw = ShtReadData(); + if (!ShtSendCommand(SHT1X_CMD_MEASURE_RH)) { return false; } + if (!ShtAwaitResult()) { return false; } + float humRaw = ShtReadData(); + + + const float d1 = -39.7; + const float d2 = 0.01; + sht_temperature = d1 + (tempRaw * d2); + const float c1 = -2.0468; + const float c2 = 0.0367; + const float c3 = -1.5955E-6; + const float t1 = 0.01; + const float t2 = 0.00008; + float rhLinear = c1 + c2 * humRaw + c3 * humRaw * humRaw; + sht_humidity = (sht_temperature - 25) * (t1 + t2 * humRaw) + rhLinear; + sht_temperature = ConvertTemp(sht_temperature); + ConvertHumidity(sht_humidity); + + sht_valid = SENSOR_MAX_MISS; + return true; +} + + + +void ShtDetect(void) +{ + sht_sda_pin = pin[GPIO_I2C_SDA]; + sht_scl_pin = pin[GPIO_I2C_SCL]; + if (ShtRead()) { + sht_type = 1; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_SHT1X_FOUND)); + } else { + Wire.begin(sht_sda_pin, sht_scl_pin); + sht_type = 0; + } +} + +void ShtEverySecond(void) +{ + if (!(uptime %4)) { + + if (!ShtRead()) { + AddLogMissed(sht_types, sht_valid); + } + } +} + +void ShtShow(bool json) +{ + if (sht_valid) { + char temperature[33]; + dtostrfd(sht_temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(sht_humidity, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, sht_types, temperature, humidity); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, sht_temperature); + KnxSensor(KNX_HUMIDITY, sht_humidity); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, sht_types, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, sht_types, humidity); +#endif + } + } +} + + + + + +bool Xsns07(uint8_t function) +{ + if (!I2cEnabled(XI2C_08)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + ShtDetect(); + } + else if (sht_type) { + switch (function) { + case FUNC_EVERY_SECOND: + ShtEverySecond(); + break; + case FUNC_JSON_APPEND: + ShtShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + ShtShow(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_08_htu21.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_08_htu21.ino" +#ifdef USE_I2C +#ifdef USE_HTU +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_08_htu21.ino" +#define XSNS_08 8 +#define XI2C_09 9 + +#define HTU21_ADDR 0x40 + +#define SI7013_CHIPID 0x0D +#define SI7020_CHIPID 0x14 +#define SI7021_CHIPID 0x15 +#define HTU21_CHIPID 0x32 + +#define HTU21_READTEMP 0xE3 +#define HTU21_READHUM 0xE5 +#define HTU21_WRITEREG 0xE6 +#define HTU21_READREG 0xE7 +#define HTU21_RESET 0xFE +#define HTU21_HEATER_WRITE 0x51 +#define HTU21_HEATER_READ 0x11 +#define HTU21_SERIAL2_READ1 0xFC +#define HTU21_SERIAL2_READ2 0xC9 + +#define HTU21_HEATER_ON 0x04 +#define HTU21_HEATER_OFF 0xFB + +#define HTU21_RES_RH12_T14 0x00 +#define HTU21_RES_RH8_T12 0x01 +#define HTU21_RES_RH10_T13 0x80 +#define HTU21_RES_RH11_T11 0x81 + +#define HTU21_CRC8_POLYNOM 0x13100 + +const char kHtuTypes[] PROGMEM = "HTU21|SI7013|SI7020|SI7021|T/RH?"; + +uint8_t htu_address; +uint8_t htu_type = 0; +uint8_t htu_delay_temp; +uint8_t htu_delay_humidity = 50; +uint8_t htu_valid = 0; +float htu_temperature = 0; +float htu_humidity = 0; +char htu_types[7]; + +uint8_t HtuCheckCrc8(uint16_t data) +{ + for (uint32_t bit = 0; bit < 16; bit++) { + if (data & 0x8000) { + data = (data << 1) ^ HTU21_CRC8_POLYNOM; + } else { + data <<= 1; + } + } + return data >>= 8; +} + +uint8_t HtuReadDeviceId(void) +{ + uint16_t deviceID = 0; + uint8_t checksum = 0; + + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_SERIAL2_READ1); + Wire.write(HTU21_SERIAL2_READ2); + Wire.endTransmission(); + + Wire.requestFrom(HTU21_ADDR, 3); + deviceID = Wire.read() << 8; + deviceID |= Wire.read(); + checksum = Wire.read(); + if (HtuCheckCrc8(deviceID) == checksum) { + deviceID = deviceID >> 8; + } else { + deviceID = 0; + } + return (uint8_t)deviceID; +} + +void HtuSetResolution(uint8_t resolution) +{ + uint8_t current = I2cRead8(HTU21_ADDR, HTU21_READREG); + current &= 0x7E; + current |= resolution; + I2cWrite8(HTU21_ADDR, HTU21_WRITEREG, current); +} + +void HtuReset(void) +{ + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_RESET); + Wire.endTransmission(); + delay(15); +} + +void HtuHeater(uint8_t heater) +{ + uint8_t current = I2cRead8(HTU21_ADDR, HTU21_READREG); + + switch(heater) + { + case HTU21_HEATER_ON : current |= heater; + break; + case HTU21_HEATER_OFF : current &= heater; + break; + default : current &= heater; + break; + } + I2cWrite8(HTU21_ADDR, HTU21_WRITEREG, current); +} + +void HtuInit(void) +{ + HtuReset(); + HtuHeater(HTU21_HEATER_OFF); + HtuSetResolution(HTU21_RES_RH12_T14); +} + +bool HtuRead(void) +{ + uint8_t checksum = 0; + uint16_t sensorval = 0; + + if (htu_valid) { htu_valid--; } + + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_READTEMP); + if (Wire.endTransmission() != 0) { return false; } + delay(htu_delay_temp); + + Wire.requestFrom(HTU21_ADDR, 3); + if (3 == Wire.available()) { + sensorval = Wire.read() << 8; + sensorval |= Wire.read(); + checksum = Wire.read(); + } + if (HtuCheckCrc8(sensorval) != checksum) { return false; } + + htu_temperature = ConvertTemp(0.002681 * (float)sensorval - 46.85); + + Wire.beginTransmission(HTU21_ADDR); + Wire.write(HTU21_READHUM); + if (Wire.endTransmission() != 0) { return false; } + delay(htu_delay_humidity); + + Wire.requestFrom(HTU21_ADDR, 3); + if (3 <= Wire.available()) { + sensorval = Wire.read() << 8; + sensorval |= Wire.read(); + checksum = Wire.read(); + } + if (HtuCheckCrc8(sensorval) != checksum) { return false; } + + sensorval ^= 0x02; + htu_humidity = 0.001907 * (float)sensorval - 6; + if (htu_humidity > 100) { htu_humidity = 100.0; } + if (htu_humidity < 0) { htu_humidity = 0.01; } + + if ((0.00 == htu_humidity) && (0.00 == htu_temperature)) { + htu_humidity = 0.0; + } + if ((htu_temperature > 0.00) && (htu_temperature < 80.00)) { + htu_humidity = (-0.15) * (25 - htu_temperature) + htu_humidity; + } + ConvertHumidity(htu_humidity); + + htu_valid = SENSOR_MAX_MISS; + return true; +} + + + +void HtuDetect(void) +{ + htu_address = HTU21_ADDR; + if (I2cActive(htu_address)) { return; } + + htu_type = HtuReadDeviceId(); + if (htu_type) { + uint8_t index = 0; + HtuInit(); + switch (htu_type) { + case HTU21_CHIPID: + htu_delay_temp = 50; + htu_delay_humidity = 16; + break; + case SI7021_CHIPID: + index++; + case SI7020_CHIPID: + index++; + case SI7013_CHIPID: + index++; + htu_delay_temp = 12; + htu_delay_humidity = 23; + break; + default: + index = 4; + htu_delay_temp = 50; + htu_delay_humidity = 23; + } + GetTextIndexed(htu_types, sizeof(htu_types), index, kHtuTypes); + I2cSetActiveFound(htu_address, htu_types); + } +} + +void HtuEverySecond(void) +{ + if (uptime &1) { + + if (!HtuRead()) { + AddLogMissed(htu_types, htu_valid); + } + } +} + +void HtuShow(bool json) +{ + if (htu_valid) { + char temperature[33]; + dtostrfd(htu_temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(htu_humidity, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, htu_types, temperature, humidity); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, htu_temperature); + KnxSensor(KNX_HUMIDITY, htu_humidity); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, htu_types, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, htu_types, humidity); +#endif + } + } +} + + + + + +bool Xsns08(uint8_t function) +{ + if (!I2cEnabled(XI2C_09)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + HtuDetect(); + } + else if (htu_type) { + switch (function) { + case FUNC_EVERY_SECOND: + HtuEverySecond(); + break; + case FUNC_JSON_APPEND: + HtuShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HtuShow(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_09_bmp.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_09_bmp.ino" +#ifdef USE_I2C +#ifdef USE_BMP +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_09_bmp.ino" +#define XSNS_09 9 +#define XI2C_10 10 + +#define BMP_ADDR1 0x76 +#define BMP_ADDR2 0x77 + +#define BMP180_CHIPID 0x55 +#define BMP280_CHIPID 0x58 +#define BME280_CHIPID 0x60 +#define BME680_CHIPID 0x61 + +#define BMP_REGISTER_CHIPID 0xD0 + +#define BMP_REGISTER_RESET 0xE0 + +#define BMP_CMND_RESET 0xB6 + +#define BMP_MAX_SENSORS 2 + +const char kBmpTypes[] PROGMEM = "BMP180|BMP280|BME280|BME680"; + +typedef struct { + uint8_t bmp_address; + char bmp_name[7]; + uint8_t bmp_type; + uint8_t bmp_model; +#ifdef USE_BME680 + uint8_t bme680_state; + float bmp_gas_resistance; +#endif + float bmp_temperature; + float bmp_pressure; + float bmp_humidity; +} bmp_sensors_t; + +uint8_t bmp_addresses[] = { BMP_ADDR1, BMP_ADDR2 }; +uint8_t bmp_count = 0; +uint8_t bmp_once = 1; + +bmp_sensors_t *bmp_sensors = nullptr; + + + + + +#define BMP180_REG_CONTROL 0xF4 +#define BMP180_REG_RESULT 0xF6 +#define BMP180_TEMPERATURE 0x2E +#define BMP180_PRESSURE3 0xF4 + +#define BMP180_AC1 0xAA +#define BMP180_AC2 0xAC +#define BMP180_AC3 0xAE +#define BMP180_AC4 0xB0 +#define BMP180_AC5 0xB2 +#define BMP180_AC6 0xB4 +#define BMP180_VB1 0xB6 +#define BMP180_VB2 0xB8 +#define BMP180_MB 0xBA +#define BMP180_MC 0xBC +#define BMP180_MD 0xBE + +#define BMP180_OSS 3 + +typedef struct { + int16_t cal_ac1; + int16_t cal_ac2; + int16_t cal_ac3; + int16_t cal_b1; + int16_t cal_b2; + int16_t cal_mc; + int16_t cal_md; + uint16_t cal_ac4; + uint16_t cal_ac5; + uint16_t cal_ac6; +} bmp180_cal_data_t; + +bmp180_cal_data_t *bmp180_cal_data = nullptr; + +bool Bmp180Calibration(uint8_t bmp_idx) +{ + if (!bmp180_cal_data) { + bmp180_cal_data = (bmp180_cal_data_t*)malloc(BMP_MAX_SENSORS * sizeof(bmp180_cal_data_t)); + } + if (!bmp180_cal_data) { return false; } + + bmp180_cal_data[bmp_idx].cal_ac1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC1); + bmp180_cal_data[bmp_idx].cal_ac2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC2); + bmp180_cal_data[bmp_idx].cal_ac3 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC3); + bmp180_cal_data[bmp_idx].cal_ac4 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC4); + bmp180_cal_data[bmp_idx].cal_ac5 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC5); + bmp180_cal_data[bmp_idx].cal_ac6 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC6); + bmp180_cal_data[bmp_idx].cal_b1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_VB1); + bmp180_cal_data[bmp_idx].cal_b2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_VB2); + bmp180_cal_data[bmp_idx].cal_mc = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_MC); + bmp180_cal_data[bmp_idx].cal_md = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_MD); + + + if (!bmp180_cal_data[bmp_idx].cal_ac1 | + !bmp180_cal_data[bmp_idx].cal_ac2 | + !bmp180_cal_data[bmp_idx].cal_ac3 | + !bmp180_cal_data[bmp_idx].cal_ac4 | + !bmp180_cal_data[bmp_idx].cal_ac5 | + !bmp180_cal_data[bmp_idx].cal_ac6 | + !bmp180_cal_data[bmp_idx].cal_b1 | + !bmp180_cal_data[bmp_idx].cal_b2 | + !bmp180_cal_data[bmp_idx].cal_mc | + !bmp180_cal_data[bmp_idx].cal_md) { + return false; + } + + if ((bmp180_cal_data[bmp_idx].cal_ac1 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac2 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac3 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac4 == 0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac5 == 0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_ac6 == 0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_b1 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_b2 == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_mc == (int16_t)0xFFFF) | + (bmp180_cal_data[bmp_idx].cal_md == (int16_t)0xFFFF)) { + return false; + } + return true; +} + +void Bmp180Read(uint8_t bmp_idx) +{ + if (!bmp180_cal_data) { return; } + + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_TEMPERATURE); + delay(5); + int ut = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT); + int32_t xt1 = (ut - (int32_t)bmp180_cal_data[bmp_idx].cal_ac6) * ((int32_t)bmp180_cal_data[bmp_idx].cal_ac5) >> 15; + int32_t xt2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_mc << 11) / (xt1 + (int32_t)bmp180_cal_data[bmp_idx].cal_md); + int32_t bmp180_b5 = xt1 + xt2; + bmp_sensors[bmp_idx].bmp_temperature = ((bmp180_b5 + 8) >> 4) / 10.0; + + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_PRESSURE3); + delay(2 + (4 << BMP180_OSS)); + uint32_t up = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT); + up >>= (8 - BMP180_OSS); + + int32_t b6 = bmp180_b5 - 4000; + int32_t x1 = ((int32_t)bmp180_cal_data[bmp_idx].cal_b2 * ((b6 * b6) >> 12)) >> 11; + int32_t x2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_ac2 * b6) >> 11; + int32_t x3 = x1 + x2; + int32_t b3 = ((((int32_t)bmp180_cal_data[bmp_idx].cal_ac1 * 4 + x3) << BMP180_OSS) + 2) >> 2; + + x1 = ((int32_t)bmp180_cal_data[bmp_idx].cal_ac3 * b6) >> 13; + x2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_b1 * ((b6 * b6) >> 12)) >> 16; + x3 = ((x1 + x2) + 2) >> 2; + uint32_t b4 = ((uint32_t)bmp180_cal_data[bmp_idx].cal_ac4 * (uint32_t)(x3 + 32768)) >> 15; + uint32_t b7 = ((uint32_t)up - b3) * (uint32_t)(50000UL >> BMP180_OSS); + + int32_t p; + if (b7 < 0x80000000) { + p = (b7 * 2) / b4; + } + else { + p = (b7 / b4) * 2; + } + x1 = (p >> 8) * (p >> 8); + x1 = (x1 * 3038) >> 16; + x2 = (-7357 * p) >> 16; + p += ((x1 + x2 + (int32_t)3791) >> 4); + bmp_sensors[bmp_idx].bmp_pressure = (float)p / 100.0; +} + + + + + + + +#define BME280_REGISTER_CONTROLHUMID 0xF2 +#define BME280_REGISTER_CONTROL 0xF4 +#define BME280_REGISTER_CONFIG 0xF5 +#define BME280_REGISTER_PRESSUREDATA 0xF7 +#define BME280_REGISTER_TEMPDATA 0xFA +#define BME280_REGISTER_HUMIDDATA 0xFD + +#define BME280_REGISTER_DIG_T1 0x88 +#define BME280_REGISTER_DIG_T2 0x8A +#define BME280_REGISTER_DIG_T3 0x8C +#define BME280_REGISTER_DIG_P1 0x8E +#define BME280_REGISTER_DIG_P2 0x90 +#define BME280_REGISTER_DIG_P3 0x92 +#define BME280_REGISTER_DIG_P4 0x94 +#define BME280_REGISTER_DIG_P5 0x96 +#define BME280_REGISTER_DIG_P6 0x98 +#define BME280_REGISTER_DIG_P7 0x9A +#define BME280_REGISTER_DIG_P8 0x9C +#define BME280_REGISTER_DIG_P9 0x9E +#define BME280_REGISTER_DIG_H1 0xA1 +#define BME280_REGISTER_DIG_H2 0xE1 +#define BME280_REGISTER_DIG_H3 0xE3 +#define BME280_REGISTER_DIG_H4 0xE4 +#define BME280_REGISTER_DIG_H5 0xE5 +#define BME280_REGISTER_DIG_H6 0xE7 + +typedef struct { + uint16_t dig_T1; + int16_t dig_T2; + int16_t dig_T3; + uint16_t dig_P1; + int16_t dig_P2; + int16_t dig_P3; + int16_t dig_P4; + int16_t dig_P5; + int16_t dig_P6; + int16_t dig_P7; + int16_t dig_P8; + int16_t dig_P9; + int16_t dig_H2; + int16_t dig_H4; + int16_t dig_H5; + uint8_t dig_H1; + uint8_t dig_H3; + int8_t dig_H6; +} Bme280CalibrationData_t; + +Bme280CalibrationData_t *Bme280CalibrationData = nullptr; + +bool Bmx280Calibrate(uint8_t bmp_idx) +{ + + + if (!Bme280CalibrationData) { + Bme280CalibrationData = (Bme280CalibrationData_t*)malloc(BMP_MAX_SENSORS * sizeof(Bme280CalibrationData_t)); + } + if (!Bme280CalibrationData) { return false; } + + Bme280CalibrationData[bmp_idx].dig_T1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T1); + Bme280CalibrationData[bmp_idx].dig_T2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T2); + Bme280CalibrationData[bmp_idx].dig_T3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T3); + Bme280CalibrationData[bmp_idx].dig_P1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P1); + Bme280CalibrationData[bmp_idx].dig_P2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P2); + Bme280CalibrationData[bmp_idx].dig_P3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P3); + Bme280CalibrationData[bmp_idx].dig_P4 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P4); + Bme280CalibrationData[bmp_idx].dig_P5 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P5); + Bme280CalibrationData[bmp_idx].dig_P6 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P6); + Bme280CalibrationData[bmp_idx].dig_P7 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P7); + Bme280CalibrationData[bmp_idx].dig_P8 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P8); + Bme280CalibrationData[bmp_idx].dig_P9 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P9); + if (BME280_CHIPID == bmp_sensors[bmp_idx].bmp_type) { + Bme280CalibrationData[bmp_idx].dig_H1 = I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H1); + Bme280CalibrationData[bmp_idx].dig_H2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H2); + Bme280CalibrationData[bmp_idx].dig_H3 = I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H3); + Bme280CalibrationData[bmp_idx].dig_H4 = (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H4) << 4) | (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H4 + 1) & 0xF); + Bme280CalibrationData[bmp_idx].dig_H5 = (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H5 + 1) << 4) | (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H5) >> 4); + Bme280CalibrationData[bmp_idx].dig_H6 = (int8_t)I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H6); + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0x00); + + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROLHUMID, 0x01); + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONFIG, 0xA0); + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0x27); + } else { + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0xB7); + } + + return true; +} + +void Bme280Read(uint8_t bmp_idx) +{ + if (!Bme280CalibrationData) { return; } + + int32_t adc_T = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_TEMPDATA); + adc_T >>= 4; + + int32_t vart1 = ((((adc_T >> 3) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1 << 1))) * ((int32_t)Bme280CalibrationData[bmp_idx].dig_T2)) >> 11; + int32_t vart2 = (((((adc_T >> 4) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1)) * ((adc_T >> 4) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1))) >> 12) * + ((int32_t)Bme280CalibrationData[bmp_idx].dig_T3)) >> 14; + int32_t t_fine = vart1 + vart2; + float T = (t_fine * 5 + 128) >> 8; + bmp_sensors[bmp_idx].bmp_temperature = T / 100.0; + + int32_t adc_P = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_PRESSUREDATA); + adc_P >>= 4; + + int64_t var1 = ((int64_t)t_fine) - 128000; + int64_t var2 = var1 * var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P6; + var2 = var2 + ((var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P5) << 17); + var2 = var2 + (((int64_t)Bme280CalibrationData[bmp_idx].dig_P4) << 35); + var1 = ((var1 * var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P3) >> 8) + ((var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P2) << 12); + var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)Bme280CalibrationData[bmp_idx].dig_P1) >> 33; + if (0 == var1) { + return; + } + int64_t p = 1048576 - adc_P; + p = (((p << 31) - var2) * 3125) / var1; + var1 = (((int64_t)Bme280CalibrationData[bmp_idx].dig_P9) * (p >> 13) * (p >> 13)) >> 25; + var2 = (((int64_t)Bme280CalibrationData[bmp_idx].dig_P8) * p) >> 19; + p = ((p + var1 + var2) >> 8) + (((int64_t)Bme280CalibrationData[bmp_idx].dig_P7) << 4); + bmp_sensors[bmp_idx].bmp_pressure = (float)p / 25600.0; + + if (BMP280_CHIPID == bmp_sensors[bmp_idx].bmp_type) { return; } + + int32_t adc_H = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_HUMIDDATA); + + int32_t v_x1_u32r = (t_fine - ((int32_t)76800)); + v_x1_u32r = (((((adc_H << 14) - (((int32_t)Bme280CalibrationData[bmp_idx].dig_H4) << 20) - + (((int32_t)Bme280CalibrationData[bmp_idx].dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) * + (((((((v_x1_u32r * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H6)) >> 10) * + (((v_x1_u32r * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + + ((int32_t)2097152)) * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H2) + 8192) >> 14)); + v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * + ((int32_t)Bme280CalibrationData[bmp_idx].dig_H1)) >> 4)); + v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r; + v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r; + float h = (v_x1_u32r >> 12); + bmp_sensors[bmp_idx].bmp_humidity = h / 1024.0; +} + +#ifdef USE_BME680 + + + + +#include + +struct bme680_dev *gas_sensor = nullptr; + +static void BmeDelayMs(uint32_t ms) +{ + delay(ms); +} + +bool Bme680Init(uint8_t bmp_idx) +{ + if (!gas_sensor) { + gas_sensor = (bme680_dev*)malloc(BMP_MAX_SENSORS * sizeof(bme680_dev)); + } + if (!gas_sensor) { return false; } + + gas_sensor[bmp_idx].dev_id = bmp_sensors[bmp_idx].bmp_address; + gas_sensor[bmp_idx].intf = BME680_I2C_INTF; + gas_sensor[bmp_idx].read = &I2cReadBuffer; + gas_sensor[bmp_idx].write = &I2cWriteBuffer; + gas_sensor[bmp_idx].delay_ms = BmeDelayMs; + + + + gas_sensor[bmp_idx].amb_temp = 25; + + int8_t rslt = BME680_OK; + rslt = bme680_init(&gas_sensor[bmp_idx]); + if (rslt != BME680_OK) { return false; } + + + gas_sensor[bmp_idx].tph_sett.os_hum = BME680_OS_2X; + gas_sensor[bmp_idx].tph_sett.os_pres = BME680_OS_4X; + gas_sensor[bmp_idx].tph_sett.os_temp = BME680_OS_8X; + gas_sensor[bmp_idx].tph_sett.filter = BME680_FILTER_SIZE_3; + + + gas_sensor[bmp_idx].gas_sett.run_gas = BME680_ENABLE_GAS_MEAS; + + gas_sensor[bmp_idx].gas_sett.heatr_temp = 320; + gas_sensor[bmp_idx].gas_sett.heatr_dur = 150; + + + + gas_sensor[bmp_idx].power_mode = BME680_FORCED_MODE; + + + uint8_t set_required_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_FILTER_SEL | BME680_GAS_SENSOR_SEL; + + + rslt = bme680_set_sensor_settings(set_required_settings,&gas_sensor[bmp_idx]); + if (rslt != BME680_OK) { return false; } + + bmp_sensors[bmp_idx].bme680_state = 0; + + return true; +} + +void Bme680Read(uint8_t bmp_idx) +{ + if (!gas_sensor) { return; } + + int8_t rslt = BME680_OK; + + if (BME680_CHIPID == bmp_sensors[bmp_idx].bmp_type) { + if (0 == bmp_sensors[bmp_idx].bme680_state) { + + rslt = bme680_set_sensor_mode(&gas_sensor[bmp_idx]); + if (rslt != BME680_OK) { return; } + + + + + + + + bmp_sensors[bmp_idx].bme680_state = 1; + } else { + bmp_sensors[bmp_idx].bme680_state = 0; + + struct bme680_field_data data; + rslt = bme680_get_sensor_data(&data, &gas_sensor[bmp_idx]); + if (rslt != BME680_OK) { return; } + + bmp_sensors[bmp_idx].bmp_temperature = data.temperature / 100.0; + bmp_sensors[bmp_idx].bmp_humidity = data.humidity / 1000.0; + bmp_sensors[bmp_idx].bmp_pressure = data.pressure / 100.0; + + if (data.status & BME680_GASM_VALID_MSK) { + bmp_sensors[bmp_idx].bmp_gas_resistance = data.gas_resistance / 1000.0; + } else { + bmp_sensors[bmp_idx].bmp_gas_resistance = 0; + } + } + } + return; +} + +#endif + + + +void BmpDetect(void) +{ + int bmp_sensor_size = BMP_MAX_SENSORS * sizeof(bmp_sensors_t); + if (!bmp_sensors) { + bmp_sensors = (bmp_sensors_t*)malloc(bmp_sensor_size); + } + if (!bmp_sensors) { return; } + memset(bmp_sensors, 0, bmp_sensor_size); + + for (uint32_t i = 0; i < BMP_MAX_SENSORS; i++) { + if (I2cActive(bmp_addresses[i])) { continue; } + uint8_t bmp_type = I2cRead8(bmp_addresses[i], BMP_REGISTER_CHIPID); + if (bmp_type) { + bmp_sensors[bmp_count].bmp_address = bmp_addresses[i]; + bmp_sensors[bmp_count].bmp_type = bmp_type; + bmp_sensors[bmp_count].bmp_model = 0; + + bool success = false; + switch (bmp_type) { + case BMP180_CHIPID: + success = Bmp180Calibration(bmp_count); + break; + case BME280_CHIPID: + bmp_sensors[bmp_count].bmp_model++; + case BMP280_CHIPID: + bmp_sensors[bmp_count].bmp_model++; + success = Bmx280Calibrate(bmp_count); + break; +#ifdef USE_BME680 + case BME680_CHIPID: + bmp_sensors[bmp_count].bmp_model = 3; + success = Bme680Init(bmp_count); + break; +#endif + } + if (success) { + GetTextIndexed(bmp_sensors[bmp_count].bmp_name, sizeof(bmp_sensors[bmp_count].bmp_name), bmp_sensors[bmp_count].bmp_model, kBmpTypes); + I2cSetActiveFound(bmp_sensors[bmp_count].bmp_address, bmp_sensors[bmp_count].bmp_name); + bmp_count++; + } + } + } +} + +void BmpRead(void) +{ + for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { + switch (bmp_sensors[bmp_idx].bmp_type) { + case BMP180_CHIPID: + Bmp180Read(bmp_idx); + break; + case BMP280_CHIPID: + case BME280_CHIPID: + Bme280Read(bmp_idx); + break; +#ifdef USE_BME680 + case BME680_CHIPID: + Bme680Read(bmp_idx); + break; +#endif + } + } + ConvertTemp(bmp_sensors[0].bmp_temperature); + ConvertHumidity(bmp_sensors[0].bmp_humidity); +} + +void BmpShow(bool json) +{ + for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { + if (bmp_sensors[bmp_idx].bmp_type) { + float bmp_sealevel = 0.0; + if (bmp_sensors[bmp_idx].bmp_pressure != 0.0) { + bmp_sealevel = (bmp_sensors[bmp_idx].bmp_pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0), 5.255)) - 21.6; + bmp_sealevel = ConvertPressure(bmp_sealevel); + } + float bmp_temperature = ConvertTemp(bmp_sensors[bmp_idx].bmp_temperature); + float bmp_pressure = ConvertPressure(bmp_sensors[bmp_idx].bmp_pressure); + + char name[10]; + strlcpy(name, bmp_sensors[bmp_idx].bmp_name, sizeof(name)); + if (bmp_count > 1) { + snprintf_P(name, sizeof(name), PSTR("%s%c%02X"), name, IndexSeparator(), bmp_sensors[bmp_idx].bmp_address); + } + + char temperature[33]; + dtostrfd(bmp_temperature, Settings.flag2.temperature_resolution, temperature); + char pressure[33]; + dtostrfd(bmp_pressure, Settings.flag2.pressure_resolution, pressure); + char sea_pressure[33]; + dtostrfd(bmp_sealevel, Settings.flag2.pressure_resolution, sea_pressure); + char humidity[33]; + dtostrfd(bmp_sensors[bmp_idx].bmp_humidity, Settings.flag2.humidity_resolution, humidity); +#ifdef USE_BME680 + char gas_resistance[33]; + dtostrfd(bmp_sensors[bmp_idx].bmp_gas_resistance, 2, gas_resistance); +#endif + + if (json) { + char json_humidity[40]; + snprintf_P(json_humidity, sizeof(json_humidity), PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity); + char json_sealevel[40]; + snprintf_P(json_sealevel, sizeof(json_sealevel), PSTR(",\"" D_JSON_PRESSUREATSEALEVEL "\":%s"), sea_pressure); +#ifdef USE_BME680 + char json_gas[40]; + snprintf_P(json_gas, sizeof(json_gas), PSTR(",\"" D_JSON_GAS "\":%s"), gas_resistance); + + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s%s}"), + name, + temperature, + (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", + pressure, + (Settings.altitude != 0) ? json_sealevel : "", + (bmp_sensors[bmp_idx].bmp_model >= 3) ? json_gas : ""); +#else + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s}"), + name, temperature, (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", pressure, (Settings.altitude != 0) ? json_sealevel : ""); +#endif + +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == bmp_idx)) { + DomoticzTempHumPressureSensor(temperature, humidity, pressure); +#ifdef USE_BME680 + if (bmp_sensors[bmp_idx].bmp_model >= 3) { DomoticzSensor(DZ_AIRQUALITY, (uint32_t)bmp_sensors[bmp_idx].bmp_gas_resistance); } +#endif + } +#endif + +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, bmp_temperature); + KnxSensor(KNX_HUMIDITY, bmp_sensors[bmp_idx].bmp_humidity); + } +#endif + +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, name, temperature, TempUnit()); + if (bmp_sensors[bmp_idx].bmp_model >= 2) { + WSContentSend_PD(HTTP_SNS_HUM, name, humidity); + } + WSContentSend_PD(HTTP_SNS_PRESSURE, name, pressure, PressureUnit().c_str()); + if (Settings.altitude != 0) { + WSContentSend_PD(HTTP_SNS_SEAPRESSURE, name, sea_pressure, PressureUnit().c_str()); + } +#ifdef USE_BME680 + if (bmp_sensors[bmp_idx].bmp_model >= 3) { + WSContentSend_PD(PSTR("{s}%s " D_GAS "{m}%s " D_UNIT_KILOOHM "{e}"), name, gas_resistance); + } +#endif + +#endif + } + } + } +} + +#ifdef USE_DEEPSLEEP + +void BMP_EnterSleep(void) +{ + for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { + switch (bmp_sensors[bmp_idx].bmp_type) { + case BMP180_CHIPID: + case BMP280_CHIPID: + case BME280_CHIPID: + I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP_REGISTER_RESET, BMP_CMND_RESET); + break; + default: + break; + } + } +} + +#endif + + + + + +bool Xsns09(uint8_t function) +{ + if (!I2cEnabled(XI2C_10)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + BmpDetect(); + } + else if (bmp_count) { + switch (function) { + case FUNC_EVERY_SECOND: + BmpRead(); + break; + case FUNC_JSON_APPEND: + BmpShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + BmpShow(0); + break; +#endif +#ifdef USE_DEEPSLEEP + case FUNC_SAVE_BEFORE_RESTART: + BMP_EnterSleep(); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_10_bh1750.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_10_bh1750.ino" +#ifdef USE_I2C +#ifdef USE_BH1750 + + + + + + +#define XSNS_10 10 +#define XI2C_11 11 + +#define BH1750_ADDR1 0x23 +#define BH1750_ADDR2 0x5C + +#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 + +uint8_t bh1750_address; +uint8_t bh1750_addresses[] = { BH1750_ADDR1, BH1750_ADDR2 }; +uint8_t bh1750_type = 0; +uint8_t bh1750_valid = 0; +uint16_t bh1750_illuminance = 0; +char bh1750_types[] = "BH1750"; + +bool Bh1750Read(void) +{ + if (bh1750_valid) { bh1750_valid--; } + + if (2 != Wire.requestFrom(bh1750_address, (uint8_t)2)) { return false; } + uint8_t msb = Wire.read(); + uint8_t lsb = Wire.read(); + bh1750_illuminance = ((msb << 8) | lsb) / 1.2; + bh1750_valid = SENSOR_MAX_MISS; + return true; +} + + + +void Bh1750Detect(void) +{ + for (uint32_t i = 0; i < sizeof(bh1750_addresses); i++) { + bh1750_address = bh1750_addresses[i]; + if (I2cActive(bh1750_address)) { continue; } + Wire.beginTransmission(bh1750_address); + Wire.write(BH1750_CONTINUOUS_HIGH_RES_MODE); + if (!Wire.endTransmission()) { + I2cSetActiveFound(bh1750_address, bh1750_types); + bh1750_type = 1; + break; + } + } +} + +void Bh1750EverySecond(void) +{ + + if (!Bh1750Read()) { + AddLogMissed(bh1750_types, bh1750_valid); + } +} + +void Bh1750Show(bool json) +{ + if (bh1750_valid) { + if (json) { + ResponseAppend_P(JSON_SNS_ILLUMINANCE, bh1750_types, bh1750_illuminance); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_ILLUMINANCE, bh1750_illuminance); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, bh1750_types, bh1750_illuminance); +#endif + } + } +} + + + + + +bool Xsns10(uint8_t function) +{ + if (!I2cEnabled(XI2C_11)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Bh1750Detect(); + } + else if (bh1750_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Bh1750EverySecond(); + break; + case FUNC_JSON_APPEND: + Bh1750Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Bh1750Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_11_veml6070.ino" +# 89 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_11_veml6070.ino" +#ifdef USE_I2C +#ifdef USE_VEML6070 + + + + + + +#define XSNS_11 11 +#define XI2C_12 12 + +#define VEML6070_ADDR_H 0x39 +#define VEML6070_ADDR_L 0x38 +#define VEML6070_INTEGRATION_TIME 3 +#define VEML6070_ENABLE 1 +#define VEML6070_DISABLE 0 +#define VEML6070_RSET_DEFAULT 270000 +#define VEML6070_UV_MAX_INDEX 15 +#define VEML6070_UV_MAX_DEFAULT 11 +#define VEML6070_POWER_COEFFCIENT 0.025 +#define VEML6070_TABLE_COEFFCIENT 32.86270591 + + + + + +const char kVemlTypes[] PROGMEM = "VEML6070"; +double uv_risk_map[VEML6070_UV_MAX_INDEX] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +double uvrisk = 0; +double uvpower = 0; +uint16_t uvlevel = 0; +uint8_t veml6070_addr_low = VEML6070_ADDR_L; +uint8_t veml6070_addr_high = VEML6070_ADDR_H; +uint8_t itime = VEML6070_INTEGRATION_TIME; +uint8_t veml6070_type = 0; +char veml6070_name[9]; +char str_uvrisk_text[10]; + + + +void Veml6070Detect(void) +{ + if (I2cActive(VEML6070_ADDR_L)) { return; } + + + Wire.beginTransmission(VEML6070_ADDR_L); + Wire.write((itime << 2) | 0x02); + uint8_t status = Wire.endTransmission(); + + if (!status) { + veml6070_type = 1; + Veml6070UvTableInit(); + uint8_t veml_model = 0; + GetTextIndexed(veml6070_name, sizeof(veml6070_name), veml_model, kVemlTypes); + I2cSetActiveFound(VEML6070_ADDR_L, veml6070_name); + } +} + + + +void Veml6070UvTableInit(void) +{ + + for (uint32_t i = 0; i < VEML6070_UV_MAX_INDEX; i++) { +#ifdef USE_VEML6070_RSET + if ( (USE_VEML6070_RSET >= 220000) && (USE_VEML6070_RSET <= 1000000) ) { + uv_risk_map[i] = ( (USE_VEML6070_RSET / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); + } else { + uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor error %d"), USE_VEML6070_RSET); + } +#else + uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor default used %d"), VEML6070_RSET_DEFAULT); +#endif + } +} + + + +void Veml6070EverySecond(void) +{ + + Veml6070ModeCmd(1); + uvlevel = Veml6070ReadUv(); + uvrisk = Veml6070UvRiskLevel(uvlevel); + uvpower = Veml6070UvPower(uvrisk); + Veml6070ModeCmd(0); +} + + + +void Veml6070ModeCmd(bool mode_cmd) +{ + + + Wire.beginTransmission(VEML6070_ADDR_L); + Wire.write((mode_cmd << 0) | 0x02 | (itime << 2)); + uint8_t status = Wire.endTransmission(); + + if (!status) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 mode_cmd")); + } +} + + + +uint16_t Veml6070ReadUv(void) +{ + uint16_t uv_raw = 0; + + if (Wire.requestFrom(VEML6070_ADDR_H, 1) != 1) { + return -1; + } + uv_raw = Wire.read(); + uv_raw <<= 8; + + if (Wire.requestFrom(VEML6070_ADDR_L, 1) != 1) { + return -1; + } + uv_raw |= Wire.read(); + + return uv_raw; +} + + + +double Veml6070UvRiskLevel(uint16_t uv_level) +{ + double risk = 0; + if (uv_level < uv_risk_map[VEML6070_UV_MAX_INDEX-1]) { + risk = (double)uv_level / uv_risk_map[0]; + + if ( (risk >= 0) && (risk <= 2.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_1); } + else if ( (risk >= 3.0) && (risk <= 5.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_2); } + else if ( (risk >= 6.0) && (risk <= 7.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_3); } + else if ( (risk >= 8.0) && (risk <= 10.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_4); } + else if ( (risk >= 11.0) && (risk <= 12.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_5); } + else if ( (risk >= 13.0) && (risk <= 25.0) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_6); } + else { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); } + return risk; + } else { + + snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); + return ( risk = 99 ); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 out of range %d"), risk); + } +} + + + +double Veml6070UvPower(double uvrisk) +{ + + double power = 0; + return ( power = VEML6070_POWER_COEFFCIENT * uvrisk ); +} + + + + +#ifdef USE_WEBSERVER + +#ifdef USE_VEML6070_SHOW_RAW + const char HTTP_SNS_UV_LEVEL[] PROGMEM = "{s}VEML6070 " D_UV_LEVEL "{m}%s " D_UNIT_INCREMENTS "{e}"; +#endif + + const char HTTP_SNS_UV_INDEX[] PROGMEM = "{s}VEML6070 " D_UV_INDEX "{m}%s %s{e}"; + const char HTTP_SNS_UV_POWER[] PROGMEM = "{s}VEML6070 " D_UV_POWER "{m}%s " D_UNIT_WATT_METER_QUADRAT "{e}"; +#endif + + + +void Veml6070Show(bool json) +{ + + char str_uvlevel[33]; + dtostrfd((double)uvlevel, 0, str_uvlevel); + char str_uvrisk[33]; + dtostrfd(uvrisk, 2, str_uvrisk); + char str_uvpower[33]; + dtostrfd(uvpower, 3, str_uvpower); + if (json) { +#ifdef USE_VEML6070_SHOW_RAW + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_UV_LEVEL "\":%s,\"" D_JSON_UV_INDEX "\":%s,\"" D_JSON_UV_INDEX_TEXT "\":\"%s\",\"" D_JSON_UV_POWER "\":%s}"), + veml6070_name, str_uvlevel, str_uvrisk, str_uvrisk_text, str_uvpower); +#else + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_UV_INDEX "\":%s,\"" D_JSON_UV_INDEX_TEXT "\":\"%s\",\"" D_JSON_UV_POWER "\":%s}"), + veml6070_name, str_uvrisk, str_uvrisk_text, str_uvpower); +#endif +#ifdef USE_DOMOTICZ + if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, uvlevel); } +#endif +#ifdef USE_WEBSERVER + } else { +#ifdef USE_VEML6070_SHOW_RAW + WSContentSend_PD(HTTP_SNS_UV_LEVEL, str_uvlevel); +#endif + WSContentSend_PD(HTTP_SNS_UV_INDEX, str_uvrisk, str_uvrisk_text); + WSContentSend_PD(HTTP_SNS_UV_POWER, str_uvpower); +#endif + } +} + + + + + +bool Xsns11(uint8_t function) +{ + if (!I2cEnabled(XI2C_12)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Veml6070Detect(); + } + else if (veml6070_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Veml6070EverySecond(); + break; + case FUNC_JSON_APPEND: + Veml6070Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Veml6070Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_12_ads1115.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_12_ads1115.ino" +#ifdef USE_I2C +#ifdef USE_ADS1115 +# 43 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_12_ads1115.ino" +#define XSNS_12 12 +#define XI2C_13 13 + +#define ADS1115_ADDRESS_ADDR_GND 0x48 +#define ADS1115_ADDRESS_ADDR_VDD 0x49 +#define ADS1115_ADDRESS_ADDR_SDA 0x4A +#define ADS1115_ADDRESS_ADDR_SCL 0x4B + +#define ADS1115_CONVERSIONDELAY (8) + + + + +#define ADS1115_REG_POINTER_MASK (0x03) +#define ADS1115_REG_POINTER_CONVERT (0x00) +#define ADS1115_REG_POINTER_CONFIG (0x01) +#define ADS1115_REG_POINTER_LOWTHRESH (0x02) +#define ADS1115_REG_POINTER_HITHRESH (0x03) + + + + +#define ADS1115_REG_CONFIG_OS_MASK (0x8000) +#define ADS1115_REG_CONFIG_OS_SINGLE (0x8000) +#define ADS1115_REG_CONFIG_OS_BUSY (0x0000) +#define ADS1115_REG_CONFIG_OS_NOTBUSY (0x8000) + +#define ADS1115_REG_CONFIG_MUX_MASK (0x7000) +#define ADS1115_REG_CONFIG_MUX_DIFF_0_1 (0x0000) +#define ADS1115_REG_CONFIG_MUX_DIFF_0_3 (0x1000) +#define ADS1115_REG_CONFIG_MUX_DIFF_1_3 (0x2000) +#define ADS1115_REG_CONFIG_MUX_DIFF_2_3 (0x3000) +#define ADS1115_REG_CONFIG_MUX_SINGLE_0 (0x4000) +#define ADS1115_REG_CONFIG_MUX_SINGLE_1 (0x5000) +#define ADS1115_REG_CONFIG_MUX_SINGLE_2 (0x6000) +#define ADS1115_REG_CONFIG_MUX_SINGLE_3 (0x7000) + +#define ADS1115_REG_CONFIG_PGA_MASK (0x0E00) +#define ADS1115_REG_CONFIG_PGA_6_144V (0x0000) +#define ADS1115_REG_CONFIG_PGA_4_096V (0x0200) +#define ADS1115_REG_CONFIG_PGA_2_048V (0x0400) +#define ADS1115_REG_CONFIG_PGA_1_024V (0x0600) +#define ADS1115_REG_CONFIG_PGA_0_512V (0x0800) +#define ADS1115_REG_CONFIG_PGA_0_256V (0x0A00) + +#define ADS1115_REG_CONFIG_MODE_MASK (0x0100) +#define ADS1115_REG_CONFIG_MODE_CONTIN (0x0000) +#define ADS1115_REG_CONFIG_MODE_SINGLE (0x0100) + +#define ADS1115_REG_CONFIG_DR_MASK (0x00E0) +#define ADS1115_REG_CONFIG_DR_128SPS (0x0000) +#define ADS1115_REG_CONFIG_DR_250SPS (0x0020) +#define ADS1115_REG_CONFIG_DR_490SPS (0x0040) +#define ADS1115_REG_CONFIG_DR_920SPS (0x0060) +#define ADS1115_REG_CONFIG_DR_1600SPS (0x0080) +#define ADS1115_REG_CONFIG_DR_2400SPS (0x00A0) +#define ADS1115_REG_CONFIG_DR_3300SPS (0x00C0) +#define ADS1115_REG_CONFIG_DR_6000SPS (0x00E0) + +#define ADS1115_REG_CONFIG_CMODE_MASK (0x0010) +#define ADS1115_REG_CONFIG_CMODE_TRAD (0x0000) +#define ADS1115_REG_CONFIG_CMODE_WINDOW (0x0010) + +#define ADS1115_REG_CONFIG_CPOL_MASK (0x0008) +#define ADS1115_REG_CONFIG_CPOL_ACTVLOW (0x0000) +#define ADS1115_REG_CONFIG_CPOL_ACTVHI (0x0008) + +#define ADS1115_REG_CONFIG_CLAT_MASK (0x0004) +#define ADS1115_REG_CONFIG_CLAT_NONLAT (0x0000) +#define ADS1115_REG_CONFIG_CLAT_LATCH (0x0004) + +#define ADS1115_REG_CONFIG_CQUE_MASK (0x0003) +#define ADS1115_REG_CONFIG_CQUE_1CONV (0x0000) +#define ADS1115_REG_CONFIG_CQUE_2CONV (0x0001) +#define ADS1115_REG_CONFIG_CQUE_4CONV (0x0002) +#define ADS1115_REG_CONFIG_CQUE_NONE (0x0003) + +struct ADS1115 { + uint8_t count = 0; + uint8_t address; + uint8_t addresses[4] = { ADS1115_ADDRESS_ADDR_GND, ADS1115_ADDRESS_ADDR_VDD, ADS1115_ADDRESS_ADDR_SDA, ADS1115_ADDRESS_ADDR_SCL }; + uint8_t found[4] = {false,false,false,false}; +} Ads1115; + + + +void Ads1115StartComparator(uint8_t channel, uint16_t mode) +{ + + uint16_t config = mode | + ADS1115_REG_CONFIG_CQUE_NONE | + ADS1115_REG_CONFIG_CLAT_NONLAT | + ADS1115_REG_CONFIG_PGA_6_144V | + ADS1115_REG_CONFIG_CPOL_ACTVLOW | + ADS1115_REG_CONFIG_CMODE_TRAD | + ADS1115_REG_CONFIG_DR_6000SPS; + + + config |= (ADS1115_REG_CONFIG_MUX_SINGLE_0 + (0x1000 * channel)); + + + I2cWrite16(Ads1115.address, ADS1115_REG_POINTER_CONFIG, config); +} + +int16_t Ads1115GetConversion(uint8_t channel) +{ + Ads1115StartComparator(channel, ADS1115_REG_CONFIG_MODE_SINGLE); + + delay(ADS1115_CONVERSIONDELAY); + + I2cRead16(Ads1115.address, ADS1115_REG_POINTER_CONVERT); + + Ads1115StartComparator(channel, ADS1115_REG_CONFIG_MODE_CONTIN); + delay(ADS1115_CONVERSIONDELAY); + + uint16_t res = I2cRead16(Ads1115.address, ADS1115_REG_POINTER_CONVERT); + return (int16_t)res; +} + + + +void Ads1115Detect(void) +{ + for (uint32_t i = 0; i < sizeof(Ads1115.addresses); i++) { + if (!Ads1115.found[i]) { + Ads1115.address = Ads1115.addresses[i]; + if (I2cActive(Ads1115.address)) { continue; } + uint16_t buffer; + if (I2cValidRead16(&buffer, Ads1115.address, ADS1115_REG_POINTER_CONVERT) && + I2cValidRead16(&buffer, Ads1115.address, ADS1115_REG_POINTER_CONFIG)) { + Ads1115StartComparator(i, ADS1115_REG_CONFIG_MODE_CONTIN); + I2cSetActiveFound(Ads1115.address, "ADS1115"); + Ads1115.found[i] = 1; + Ads1115.count++; + } + } + } +} + +void Ads1115Show(bool json) +{ + int16_t values[4]; + + for (uint32_t t = 0; t < sizeof(Ads1115.addresses); t++) { + + if (Ads1115.found[t]) { + + uint8_t old_address = Ads1115.address; + Ads1115.address = Ads1115.addresses[t]; + for (uint32_t i = 0; i < 4; i++) { + values[i] = Ads1115GetConversion(i); + + } + Ads1115.address = old_address; + + char label[15]; + if (1 == Ads1115.count) { + + snprintf_P(label, sizeof(label), PSTR("ADS1115")); + } else { + + snprintf_P(label, sizeof(label), PSTR("ADS1115%c%02x"), IndexSeparator(), Ads1115.addresses[t]); + } + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{"), label); + for (uint32_t i = 0; i < 4; i++) { + ResponseAppend_P(PSTR("%s\"A%d\":%d"), (0 == i) ? "" : ",", i, values[i]); + } + ResponseJsonEnd(); + } +#ifdef USE_WEBSERVER + else { + for (uint32_t i = 0; i < 4; i++) { + WSContentSend_PD(HTTP_SNS_ANALOG, label, i, values[i]); + } + } +#endif + } + } +} + + + + + +bool Xsns12(uint8_t function) +{ + if (!I2cEnabled(XI2C_13)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Ads1115Detect(); + } + else if (Ads1115.count) { + switch (function) { + case FUNC_JSON_APPEND: + Ads1115Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ads1115Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_13_ina219.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_13_ina219.ino" +#ifdef USE_I2C +#ifdef USE_INA219 +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_13_ina219.ino" +#define XSNS_13 13 +#define XI2C_14 14 + +#define INA219_ADDRESS1 (0x40) +#define INA219_ADDRESS2 (0x41) +#define INA219_ADDRESS3 (0x44) +#define INA219_ADDRESS4 (0x45) + +#define INA219_READ (0x01) +#define INA219_REG_CONFIG (0x00) + +#define INA219_CONFIG_RESET (0x8000) + +#define INA219_CONFIG_BVOLTAGERANGE_MASK (0x2000) +#define INA219_CONFIG_BVOLTAGERANGE_16V (0x0000) +#define INA219_CONFIG_BVOLTAGERANGE_32V (0x2000) + +#define INA219_CONFIG_GAIN_MASK (0x1800) +#define INA219_CONFIG_GAIN_1_40MV (0x0000) +#define INA219_CONFIG_GAIN_2_80MV (0x0800) +#define INA219_CONFIG_GAIN_4_160MV (0x1000) +#define INA219_CONFIG_GAIN_8_320MV (0x1800) + +#define INA219_CONFIG_BADCRES_MASK (0x0780) +#define INA219_CONFIG_BADCRES_9BIT (0x0080) +#define INA219_CONFIG_BADCRES_10BIT (0x0100) +#define INA219_CONFIG_BADCRES_11BIT (0x0200) +#define INA219_CONFIG_BADCRES_12BIT (0x0400) + +#define INA219_CONFIG_SADCRES_MASK (0x0078) +#define INA219_CONFIG_SADCRES_9BIT_1S_84US (0x0000) +#define INA219_CONFIG_SADCRES_10BIT_1S_148US (0x0008) +#define INA219_CONFIG_SADCRES_11BIT_1S_276US (0x0010) +#define INA219_CONFIG_SADCRES_12BIT_1S_532US (0x0018) +#define INA219_CONFIG_SADCRES_12BIT_2S_1060US (0x0048) +#define INA219_CONFIG_SADCRES_12BIT_4S_2130US (0x0050) +#define INA219_CONFIG_SADCRES_12BIT_8S_4260US (0x0058) +#define INA219_CONFIG_SADCRES_12BIT_16S_8510US (0x0060) +#define INA219_CONFIG_SADCRES_12BIT_32S_17MS (0x0068) +#define INA219_CONFIG_SADCRES_12BIT_64S_34MS (0x0070) +#define INA219_CONFIG_SADCRES_12BIT_128S_69MS (0x0078) + +#define INA219_CONFIG_MODE_MASK (0x0007) +#define INA219_CONFIG_MODE_POWERDOWN (0x0000) +#define INA219_CONFIG_MODE_SVOLT_TRIGGERED (0x0001) +#define INA219_CONFIG_MODE_BVOLT_TRIGGERED (0x0002) +#define INA219_CONFIG_MODE_SANDBVOLT_TRIGGERED (0x0003) +#define INA219_CONFIG_MODE_ADCOFF (0x0004) +#define INA219_CONFIG_MODE_SVOLT_CONTINUOUS (0x0005) +#define INA219_CONFIG_MODE_BVOLT_CONTINUOUS (0x0006) +#define INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS (0x0007) + +#define INA219_REG_SHUNTVOLTAGE (0x01) +#define INA219_REG_BUSVOLTAGE (0x02) +#define INA219_REG_POWER (0x03) +#define INA219_REG_CURRENT (0x04) +#define INA219_REG_CALIBRATION (0x05) + +uint8_t ina219_type[4] = {0,0,0,0}; +uint8_t ina219_addresses[] = { INA219_ADDRESS1, INA219_ADDRESS2, INA219_ADDRESS3, INA219_ADDRESS4 }; + +uint32_t ina219_cal_value = 0; + +uint32_t ina219_current_divider_ma = 0; + +uint8_t ina219_valid[4] = {0,0,0,0}; +float ina219_voltage[4] = {0,0,0,0}; +float ina219_current[4] = {0,0,0,0}; +char ina219_types[] = "INA219"; +uint8_t ina219_count = 0; + +bool Ina219SetCalibration(uint8_t mode, uint16_t addr) +{ + uint16_t config = 0; + + switch (mode &3) { + case 0: + case 3: + ina219_cal_value = 4096; + ina219_current_divider_ma = 10; + config = INA219_CONFIG_BVOLTAGERANGE_32V | INA219_CONFIG_GAIN_8_320MV | INA219_CONFIG_BADCRES_12BIT | INA219_CONFIG_SADCRES_12BIT_1S_532US | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; + break; + case 1: + ina219_cal_value = 10240; + ina219_current_divider_ma = 25; + config |= INA219_CONFIG_BVOLTAGERANGE_32V | INA219_CONFIG_GAIN_8_320MV | INA219_CONFIG_BADCRES_12BIT | INA219_CONFIG_SADCRES_12BIT_1S_532US | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; + break; + case 2: + ina219_cal_value = 8192; + ina219_current_divider_ma = 20; + config |= INA219_CONFIG_BVOLTAGERANGE_16V | INA219_CONFIG_GAIN_1_40MV | INA219_CONFIG_BADCRES_12BIT | INA219_CONFIG_SADCRES_12BIT_1S_532US | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; + break; + } + + bool success = I2cWrite16(addr, INA219_REG_CALIBRATION, ina219_cal_value); + if (success) { + + I2cWrite16(addr, INA219_REG_CONFIG, config); + } + return success; +} + +float Ina219GetShuntVoltage_mV(uint16_t addr) +{ + + int16_t value = I2cReadS16(addr, INA219_REG_SHUNTVOLTAGE); + + return value * 0.01; +} + +float Ina219GetBusVoltage_V(uint16_t addr) +{ + + + int16_t value = (int16_t)(((uint16_t)I2cReadS16(addr, INA219_REG_BUSVOLTAGE) >> 3) * 4); + + return value * 0.001; +} + +float Ina219GetCurrent_mA(uint16_t addr) +{ + + + + I2cWrite16(addr, INA219_REG_CALIBRATION, ina219_cal_value); + + + float value = I2cReadS16(addr, INA219_REG_CURRENT); + value /= ina219_current_divider_ma; + + return value; +} + +bool Ina219Read(void) +{ + for (int i=0; i= 0) && (XdrvMailbox.payload <= 2)) { + Settings.ina219_mode = XdrvMailbox.payload; + restart_flag = 2; + } + Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_13, Settings.ina219_mode); + + return true; +} + + + +void Ina219Detect(void) +{ + for (uint32_t i = 0; i < sizeof(ina219_type); i++) { + uint16_t addr = ina219_addresses[i]; + if (I2cActive(addr)) { continue; } + if (Ina219SetCalibration(Settings.ina219_mode, addr)) { + I2cSetActiveFound(addr, ina219_types); + ina219_type[i] = 1; + ina219_count++; + } + } +} + +void Ina219EverySecond(void) +{ + + Ina219Read(); +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_INA219_DATA[] PROGMEM = + "{s}%s " D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}%s " D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}%s " D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; +#endif + +void Ina219Show(bool json) +{ + int num_found=0; + for (int i=0; i1) + snprintf_P(name, sizeof(name), PSTR("%s%c%d"), ina219_types, IndexSeparator(), sensor_num); + else + snprintf_P(name, sizeof(name), PSTR("%s"), ina219_types); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"Id\":%02x,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), + name, ina219_addresses[i], voltage, current, power); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, voltage); + DomoticzSensor(DZ_CURRENT, current); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_INA219_DATA, name, voltage, name, current, name, power); +#endif + } + } +} + + + + + +bool Xsns13(uint8_t function) +{ + if (!I2cEnabled(XI2C_14)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Ina219Detect(); + } + else if (ina219_count) { + switch (function) { + case FUNC_COMMAND_SENSOR: + if (XSNS_13 == XdrvMailbox.index) { + result = Ina219CommandSensor(); + } + break; + case FUNC_EVERY_SECOND: + Ina219EverySecond(); + break; + case FUNC_JSON_APPEND: + Ina219Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ina219Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_14_sht3x.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_14_sht3x.ino" +#ifdef USE_I2C +#ifdef USE_SHT3X + + + + + + +#define XSNS_14 14 +#define XI2C_15 15 + +#define SHT3X_ADDR_GND 0x44 +#define SHT3X_ADDR_VDD 0x45 +#define SHTC3_ADDR 0x70 + +#define SHT3X_MAX_SENSORS 3 + +const char kShtTypes[] PROGMEM = "SHT3X|SHT3X|SHTC3"; +uint8_t sht3x_addresses[] = { SHT3X_ADDR_GND, SHT3X_ADDR_VDD, SHTC3_ADDR }; + +uint8_t sht3x_count = 0; +struct SHT3XSTRUCT { + uint8_t address; + char types[6]; +} sht3x_sensors[SHT3X_MAX_SENSORS]; + +bool Sht3xRead(float &t, float &h, uint8_t sht3x_address) +{ + unsigned int data[6]; + + t = NAN; + h = NAN; + + Wire.beginTransmission(sht3x_address); + if (SHTC3_ADDR == sht3x_address) { + Wire.write(0x35); + Wire.write(0x17); + Wire.endTransmission(); + Wire.beginTransmission(sht3x_address); + Wire.write(0x78); + Wire.write(0x66); + } else { + Wire.write(0x2C); + Wire.write(0x06); + } + if (Wire.endTransmission() != 0) { + return false; + } + delay(30); + Wire.requestFrom(sht3x_address, (uint8_t)6); + for (uint32_t i = 0; i < 6; i++) { + data[i] = Wire.read(); + }; + t = ConvertTemp((float)((((data[0] << 8) | data[1]) * 175) / 65535.0) - 45); + h = ConvertHumidity((float)((((data[3] << 8) | data[4]) * 100) / 65535.0)); + return (!isnan(t) && !isnan(h) && (h != 0)); +} + + + +void Sht3xDetect(void) +{ + for (uint32_t i = 0; i < SHT3X_MAX_SENSORS; i++) { + if (I2cActive(sht3x_addresses[i])) { continue; } + float t; + float h; + if (Sht3xRead(t, h, sht3x_addresses[i])) { + sht3x_sensors[sht3x_count].address = sht3x_addresses[i]; + GetTextIndexed(sht3x_sensors[sht3x_count].types, sizeof(sht3x_sensors[sht3x_count].types), i, kShtTypes); + I2cSetActiveFound(sht3x_sensors[sht3x_count].address, sht3x_sensors[sht3x_count].types); + sht3x_count++; + } + } +} + +void Sht3xShow(bool json) +{ + for (uint32_t i = 0; i < sht3x_count; i++) { + float t; + float h; + if (Sht3xRead(t, h, sht3x_sensors[i].address)) { + char temperature[33]; + dtostrfd(t, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(h, Settings.flag2.humidity_resolution, humidity); + char types[11]; + snprintf_P(types, sizeof(types), PSTR("%s%c0x%02X"), sht3x_sensors[i].types, IndexSeparator(), sht3x_sensors[i].address); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, types, temperature, humidity); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && (0 == i)) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif + +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, t); + KnxSensor(KNX_HUMIDITY, h); + } +#endif + +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, types, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, types, humidity); +#endif + } + } + } +} + + + + + +bool Xsns14(uint8_t function) +{ + if (!I2cEnabled(XI2C_15)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Sht3xDetect(); + } + else if (sht3x_count) { + switch (function) { + case FUNC_JSON_APPEND: + Sht3xShow(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sht3xShow(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_15_mhz19.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_15_mhz19.ino" +#ifdef USE_MHZ19 +# 33 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_15_mhz19.ino" +#define XSNS_15 15 + +enum MhzFilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILTER_FAST, MHZ19_FILTER_MEDIUM, MHZ19_FILTER_SLOW}; + +#define MHZ19_FILTER_OPTION MHZ19_FILTER_FAST +# 58 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_15_mhz19.ino" +#include + +#ifndef CO2_LOW +#define CO2_LOW 800 +#endif +#ifndef CO2_HIGH +#define CO2_HIGH 1200 +#endif + +#define MHZ19_READ_TIMEOUT 400 +#define MHZ19_RETRY_COUNT 8 + +TasmotaSerial *MhzSerial; + +const char kMhzModels[] PROGMEM = "|B"; + +const char ABC_ENABLED[] = "ABC is Enabled"; +const char ABC_DISABLED[] = "ABC is Disabled"; + +enum MhzCommands { MHZ_CMND_READPPM, MHZ_CMND_ABCENABLE, MHZ_CMND_ABCDISABLE, MHZ_CMND_ZEROPOINT, MHZ_CMND_RESET, MHZ_CMND_RANGE_1000, MHZ_CMND_RANGE_2000, MHZ_CMND_RANGE_3000, MHZ_CMND_RANGE_5000 }; +const uint8_t kMhzCommands[][4] PROGMEM = { + + {0x86,0x00,0x00,0x00}, + {0x79,0xA0,0x00,0x00}, + {0x79,0x00,0x00,0x00}, + {0x87,0x00,0x00,0x00}, + {0x8D,0x00,0x00,0x00}, + {0x99,0x00,0x03,0xE8}, + {0x99,0x00,0x07,0xD0}, + {0x99,0x00,0x0B,0xB8}, + {0x99,0x00,0x13,0x88}}; + +uint8_t mhz_type = 1; +uint16_t mhz_last_ppm = 0; +uint8_t mhz_filter = MHZ19_FILTER_OPTION; +bool mhz_abc_must_apply = false; + +float mhz_temperature = 0; +uint8_t mhz_retry = MHZ19_RETRY_COUNT; +uint8_t mhz_received = 0; +uint8_t mhz_state = 0; + + + +uint8_t MhzCalculateChecksum(uint8_t *array) +{ + uint8_t checksum = 0; + for (uint32_t i = 1; i < 8; i++) { + checksum += array[i]; + } + checksum = 255 - checksum; + return (checksum +1); +} + +size_t MhzSendCmd(uint8_t command_id) +{ + uint8_t mhz_send[9] = { 0 }; + + mhz_send[0] = 0xFF; + mhz_send[1] = 0x01; + memcpy_P(&mhz_send[2], kMhzCommands[command_id], sizeof(uint16_t)); + + + + + memcpy_P(&mhz_send[6], kMhzCommands[command_id] + sizeof(uint16_t), sizeof(uint16_t)); + mhz_send[8] = MhzCalculateChecksum(mhz_send); + + + + return MhzSerial->write(mhz_send, sizeof(mhz_send)); +} + + + +bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s) +{ + if (1 == s) { + return false; + } + if (mhz_last_ppm < 400 || mhz_last_ppm > 5000) { + + + mhz_last_ppm = ppm; + return true; + } + int32_t difference = ppm - mhz_last_ppm; + if (s > 0 && s < 64 && mhz_filter != MHZ19_FILTER_OFF) { +# 154 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_15_mhz19.ino" + difference *= s; + difference /= 64; + } + if (MHZ19_FILTER_OFF == mhz_filter) { + if (s != 0 && s != 64) { + return false; + } + } else { + difference >>= (mhz_filter -1); + } + mhz_last_ppm = static_cast(mhz_last_ppm + difference); + return true; +} + +void MhzEverySecond(void) +{ + mhz_state++; + if (8 == mhz_state) { + mhz_state = 0; + + if (mhz_retry) { + mhz_retry--; + if (!mhz_retry) { + mhz_last_ppm = 0; + mhz_temperature = 0; + } + } + + MhzSerial->flush(); + MhzSendCmd(MHZ_CMND_READPPM); + mhz_received = 0; + } + + if ((mhz_state > 2) && !mhz_received) { + uint8_t mhz_response[9]; + + unsigned long start = millis(); + uint8_t counter = 0; + while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) { + if (MhzSerial->available() > 0) { + mhz_response[counter++] = MhzSerial->read(); + } else { + delay(5); + } + } + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, mhz_response, counter); + + if (counter < 9) { + + return; + } + + uint8_t crc = MhzCalculateChecksum(mhz_response); + if (mhz_response[8] != crc) { + + return; + } + if (0xFF != mhz_response[0] || 0x86 != mhz_response[1]) { + + return; + } + + mhz_received = 1; + + uint16_t u = (mhz_response[6] << 8) | mhz_response[7]; + if (15000 == u) { + if (Settings.SensorBits1.mhz19b_abc_disable) { + + + mhz_abc_must_apply = true; + } + } else { + uint16_t ppm = (mhz_response[2] << 8) | mhz_response[3]; + mhz_temperature = ConvertTemp((float)mhz_response[4] - 40); + uint8_t s = mhz_response[5]; + mhz_type = (s) ? 1 : 2; + if (MhzCheckAndApplyFilter(ppm, s)) { + mhz_retry = MHZ19_RETRY_COUNT; + LightSetSignal(CO2_LOW, CO2_HIGH, mhz_last_ppm); + + if (0 == s || 64 == s) { + if (mhz_abc_must_apply) { + mhz_abc_must_apply = false; + if (!Settings.SensorBits1.mhz19b_abc_disable) { + MhzSendCmd(MHZ_CMND_ABCENABLE); + } else { + MhzSendCmd(MHZ_CMND_ABCDISABLE); + } + } + } + + } + } + + } +} +# 266 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_15_mhz19.ino" +#define D_JSON_RANGE_1000 "1000 ppm range" +#define D_JSON_RANGE_2000 "2000 ppm range" +#define D_JSON_RANGE_3000 "3000 ppm range" +#define D_JSON_RANGE_5000 "5000 ppm range" + +bool MhzCommandSensor(void) +{ + bool serviced = true; + + switch (XdrvMailbox.payload) { + case 0: + Settings.SensorBits1.mhz19b_abc_disable = true; + MhzSendCmd(MHZ_CMND_ABCDISABLE); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_DISABLED); + break; + case 1: + Settings.SensorBits1.mhz19b_abc_disable = false; + MhzSendCmd(MHZ_CMND_ABCENABLE); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); + break; + case 2: + MhzSendCmd(MHZ_CMND_ZEROPOINT); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_ZERO_POINT_CALIBRATION); + break; + case 9: + MhzSendCmd(MHZ_CMND_RESET); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RESET); + break; + case 1000: + MhzSendCmd(MHZ_CMND_RANGE_1000); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_1000); + break; + case 2000: + MhzSendCmd(MHZ_CMND_RANGE_2000); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_2000); + break; + case 3000: + MhzSendCmd(MHZ_CMND_RANGE_3000); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_3000); + break; + case 5000: + MhzSendCmd(MHZ_CMND_RANGE_5000); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_5000); + break; + default: + if (!Settings.SensorBits1.mhz19b_abc_disable) { + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); + } else { + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_DISABLED); + } + } + + return serviced; +} + + + +void MhzInit(void) +{ + mhz_type = 0; + if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) { + MhzSerial = new TasmotaSerial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD], 1); + if (MhzSerial->begin(9600)) { + if (MhzSerial->hardwareSerial()) { ClaimSerial(); } + mhz_type = 1; + } + + } +} + +void MhzShow(bool json) +{ + char types[7] = "MHZ19B"; + char temperature[33]; + dtostrfd(mhz_temperature, Settings.flag2.temperature_resolution, temperature); + char model[3]; + GetTextIndexed(model, sizeof(model), mhz_type -1, kMhzModels); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_MODEL "\":\"%s\",\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s}"), types, model, mhz_last_ppm, temperature); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_AIRQUALITY, mhz_last_ppm); + DomoticzSensor(DZ_TEMP, temperature); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CO2, types, mhz_last_ppm); + WSContentSend_PD(HTTP_SNS_TEMP, types, temperature, TempUnit()); +#endif + } +} + + + + + +bool Xsns15(uint8_t function) +{ + bool result = false; + + if (mhz_type) { + switch (function) { + case FUNC_INIT: + MhzInit(); + break; + case FUNC_EVERY_SECOND: + MhzEverySecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_15 == XdrvMailbox.index) { + result = MhzCommandSensor(); + } + break; + case FUNC_JSON_APPEND: + MhzShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MhzShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_16_tsl2561.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_16_tsl2561.ino" +#ifdef USE_I2C +#ifdef USE_TSL2561 +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_16_tsl2561.ino" +#define XSNS_16 16 +#define XI2C_16 16 + +#include + +Tsl2561 Tsl(Wire); + +uint8_t tsl2561_type = 0; +uint8_t tsl2561_valid = 0; +uint32_t tsl2561_milliLux = 0; +char tsl2561_types[] = "TSL2561"; + +bool Tsl2561Read(void) +{ + if (tsl2561_valid) { tsl2561_valid--; } + + uint8_t id; + bool gain; + Tsl2561::exposure_t exposure; + uint16_t scaledFull, scaledIr; + uint32_t full, ir; + + if (Tsl.on()) { + if (Tsl.id(id) + && Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr) + && Tsl2561Util::normalizedLuminosity(gain, exposure, full = scaledFull, ir = scaledIr) + && Tsl2561Util::milliLux(full, ir, tsl2561_milliLux, Tsl2561::packageCS(id))) { + } else{ + tsl2561_milliLux = 0; + } + } + tsl2561_valid = SENSOR_MAX_MISS; + return true; +} + +void Tsl2561Detect(void) +{ + if (I2cSetDevice(0x29) || I2cSetDevice(0x39) || I2cSetDevice(0x49)) { + uint8_t id; + Tsl.begin(); + if (!Tsl.id(id)) return; + if (Tsl.on()) { + tsl2561_type = 1; + I2cSetActiveFound(Tsl.address(), tsl2561_types); + } + } +} + +void Tsl2561EverySecond(void) +{ + if (!(uptime %2)) { + + if (!Tsl2561Read()) { + AddLogMissed(tsl2561_types, tsl2561_valid); + } + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_TSL2561[] PROGMEM = + "{s}TSL2561 " D_ILLUMINANCE "{m}%u.%03u " D_UNIT_LUX "{e}"; +#endif + +void Tsl2561Show(bool json) +{ + if (tsl2561_valid) { + if (json) { + ResponseAppend_P(PSTR(",\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u}"), + tsl2561_milliLux / 1000, tsl2561_milliLux % 1000); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, (tsl2561_milliLux + 500) / 1000); } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TSL2561, tsl2561_milliLux / 1000, tsl2561_milliLux % 1000); +#endif + } + } +} + + + + + +bool Xsns16(uint8_t function) +{ + if (!I2cEnabled(XI2C_16)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Tsl2561Detect(); + } + else if (tsl2561_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Tsl2561EverySecond(); + break; + case FUNC_JSON_APPEND: + Tsl2561Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Tsl2561Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_17_senseair.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_17_senseair.ino" +#ifdef USE_SENSEAIR +# 29 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_17_senseair.ino" +#define XSNS_17 17 + +#define SENSEAIR_MODBUS_SPEED 9600 +#define SENSEAIR_DEVICE_ADDRESS 0xFE +#define SENSEAIR_READ_REGISTER 0x04 + +#ifndef CO2_LOW +#define CO2_LOW 800 +#endif +#ifndef CO2_HIGH +#define CO2_HIGH 1200 +#endif + +#include +TasmotaModbus *SenseairModbus; + +const char kSenseairTypes[] PROGMEM = "Kx0|S8"; + +uint8_t senseair_type = 1; +char senseair_types[7]; + +uint16_t senseair_co2 = 0; +float senseair_temperature = 0; +float senseair_humidity = 0; + + + +const uint8_t start_addresses[] { 0x1A, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x0A }; + +uint8_t senseair_read_state = 0; +uint8_t senseair_send_retry = 0; + +void Senseair250ms(void) +{ + + + + + uint16_t value = 0; + bool data_ready = SenseairModbus->ReceiveReady(); + + if (data_ready) { + uint8_t error = SenseairModbus->Receive16BitRegister(&value); + if (error) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir response error %d"), error); + } else { + switch(senseair_read_state) { + case 0: + senseair_type = 2; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir type id low %04X"), value); + break; + case 1: + if (value) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir error %04X"), value); + } + break; + case 2: + senseair_co2 = value; + LightSetSignal(CO2_LOW, CO2_HIGH, senseair_co2); + break; + case 3: + senseair_temperature = ConvertTemp((float)value / 100); + break; + case 4: + senseair_humidity = ConvertHumidity((float)value / 100); + break; + case 5: + { + bool relay_state = value >> 8 & 1; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir relay state %d"), relay_state); + break; + } + case 6: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir temp adjustment %d"), value); + break; + } + } + senseair_read_state++; + if (2 == senseair_type) { + if (3 == senseair_read_state) { + senseair_read_state = 1; + } + } else { + if (sizeof(start_addresses) == senseair_read_state) { + senseair_read_state = 1; + } + } + } + + if (0 == senseair_send_retry || data_ready) { + senseair_send_retry = 5; + SenseairModbus->Send(SENSEAIR_DEVICE_ADDRESS, SENSEAIR_READ_REGISTER, (uint16_t)start_addresses[senseair_read_state], 1); + } else { + senseair_send_retry--; + } + + +} + + + +void SenseairInit(void) +{ + senseair_type = 0; + if ((pin[GPIO_SAIR_RX] < 99) && (pin[GPIO_SAIR_TX] < 99)) { + SenseairModbus = new TasmotaModbus(pin[GPIO_SAIR_RX], pin[GPIO_SAIR_TX]); + uint8_t result = SenseairModbus->Begin(SENSEAIR_MODBUS_SPEED); + if (result) { + if (2 == result) { ClaimSerial(); } + senseair_type = 1; + } + } +} + +void SenseairShow(bool json) +{ + char temperature[33]; + dtostrfd(senseair_temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(senseair_humidity, Settings.flag2.temperature_resolution, humidity); + GetTextIndexed(senseair_types, sizeof(senseair_types), senseair_type -1, kSenseairTypes); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_CO2 "\":%d"), senseair_types, senseair_co2); + if (senseair_type != 2) { + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s"), temperature, humidity); + } + ResponseJsonEnd(); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, senseair_co2); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CO2, senseair_types, senseair_co2); + if (senseair_type != 2) { + WSContentSend_PD(HTTP_SNS_TEMP, senseair_types, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, senseair_types, humidity); + } +#endif + } +} + + + + + +bool Xsns17(uint8_t function) +{ + bool result = false; + + if (senseair_type) { + switch (function) { + case FUNC_INIT: + SenseairInit(); + break; + case FUNC_EVERY_250_MSECOND: + Senseair250ms(); + break; + case FUNC_JSON_APPEND: + SenseairShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + SenseairShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_18_pms5003.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_18_pms5003.ino" +#ifdef USE_PMS5003 +# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_18_pms5003.ino" +#define XSNS_18 18 + +#include + +TasmotaSerial *PmsSerial; + +uint8_t pms_type = 1; +uint8_t pms_valid = 0; + +struct pmsX003data { + uint16_t framelen; + uint16_t pm10_standard, pm25_standard, pm100_standard; + uint16_t pm10_env, pm25_env, pm100_env; +#ifdef PMS_MODEL_PMS3003 + uint16_t reserved1, reserved2, reserved3; +#else + uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um; + uint16_t unused; +#endif + uint16_t checksum; +} pms_data; + + + +bool PmsReadData(void) +{ + if (! PmsSerial->available()) { + return false; + } + while ((PmsSerial->peek() != 0x42) && PmsSerial->available()) { + PmsSerial->read(); + } +#ifdef PMS_MODEL_PMS3003 + if (PmsSerial->available() < 22) { +#else + if (PmsSerial->available() < 32) { +#endif + return false; + } + +#ifdef PMS_MODEL_PMS3003 + uint8_t buffer[22]; + PmsSerial->readBytes(buffer, 22); +#else + uint8_t buffer[32]; + PmsSerial->readBytes(buffer, 32); +#endif + uint16_t sum = 0; + PmsSerial->flush(); + +#ifdef PMS_MODEL_PMS3003 + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 22); +#else + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 32); +#endif + + +#ifdef PMS_MODEL_PMS3003 + for (uint32_t i = 0; i < 20; i++) { +#else + for (uint32_t i = 0; i < 30; i++) { +#endif + sum += buffer[i]; + } + +#ifdef PMS_MODEL_PMS3003 + uint16_t buffer_u16[10]; + for (uint32_t i = 0; i < 10; i++) { +#else + uint16_t buffer_u16[15]; + for (uint32_t i = 0; i < 15; i++) { +#endif + buffer_u16[i] = buffer[2 + i*2 + 1]; + buffer_u16[i] += (buffer[2 + i*2] << 8); + } +#ifdef PMS_MODEL_PMS3003 + if (sum != buffer_u16[9]) { +#else + if (sum != buffer_u16[14]) { +#endif + AddLog_P(LOG_LEVEL_DEBUG, PSTR("PMS: " D_CHECKSUM_FAILURE)); + return false; + } + +#ifdef PMS_MODEL_PMS3003 + memcpy((void *)&pms_data, (void *)buffer_u16, 20); +#else + memcpy((void *)&pms_data, (void *)buffer_u16, 30); +#endif + pms_valid = 10; + + return true; +} + + + +void PmsSecond(void) +{ + if (PmsReadData()) { + pms_valid = 10; + } else { + if (pms_valid) { + pms_valid--; + } + } +} + + + +void PmsInit(void) +{ + pms_type = 0; + if (pin[GPIO_PMS5003] < 99) { + PmsSerial = new TasmotaSerial(pin[GPIO_PMS5003], -1, 1); + if (PmsSerial->begin(9600)) { + if (PmsSerial->hardwareSerial()) { ClaimSerial(); } + pms_type = 1; + } + } +} + +#ifdef USE_WEBSERVER +#ifdef PMS_MODEL_PMS3003 +const char HTTP_PMS3003_SNS[] PROGMEM = + + + + "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; +#else +const char HTTP_PMS5003_SNS[] PROGMEM = + + + + "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 0.3 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 0.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" + "{s}PMS5003 " D_PARTICALS_BEYOND " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"; +#endif +#endif + +void PmsShow(bool json) +{ + if (pms_valid) { + if (json) { +#ifdef PMS_MODEL_PMS3003 + ResponseAppend_P(PSTR(",\"PMS3003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d}"), + pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env); +#else + ResponseAppend_P(PSTR(",\"PMS5003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d,\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d,\"PB5\":%d,\"PB10\":%d}"), + pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, + pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); +#endif +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_COUNT, pms_data.pm10_env); + DomoticzSensor(DZ_VOLTAGE, pms_data.pm25_env); + DomoticzSensor(DZ_CURRENT, pms_data.pm100_env); + } +#endif +#ifdef USE_WEBSERVER + } else { + +#ifdef PMS_MODEL_PMS3003 + WSContentSend_PD(HTTP_PMS3003_SNS, + + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env); +#else + WSContentSend_PD(HTTP_PMS5003_SNS, + + pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, + pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); +#endif +#endif + } + } +} + + + + + +bool Xsns18(uint8_t function) +{ + bool result = false; + + if (pms_type) { + switch (function) { + case FUNC_INIT: + PmsInit(); + break; + case FUNC_EVERY_SECOND: + PmsSecond(); + break; + case FUNC_JSON_APPEND: + PmsShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + PmsShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_19_mgs.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_19_mgs.ino" +#ifdef USE_I2C +#ifdef USE_MGS + + + + + + + +#define XSNS_19 19 +#define XI2C_17 17 + +#ifndef MGS_SENSOR_ADDR +#define MGS_SENSOR_ADDR 0x04 +#endif + +#include "MutichannelGasSensor.h" + +bool mgs_detected = false; + +void MGSInit(void) { + gas.begin(MGS_SENSOR_ADDR); +} + +void MGSPrepare(void) +{ + if (I2cActive(MGS_SENSOR_ADDR)) { return; } + + gas.begin(MGS_SENSOR_ADDR); + if (!gas.isError()) { + I2cSetActiveFound(MGS_SENSOR_ADDR, "MultiGas"); + mgs_detected = true; + } +} + +char* measure_gas(int gas_type, char* buffer) +{ + float f = gas.calcGas(gas_type); + dtostrfd(f, 2, buffer); + return buffer; +} + +#ifdef USE_WEBSERVER +const char HTTP_MGS_GAS[] PROGMEM = "{s}MGS %s{m}%s " D_UNIT_PARTS_PER_MILLION "{e}"; +#endif + +void MGSShow(bool json) +{ + char buffer[33]; + if (json) { + ResponseAppend_P(PSTR(",\"MGS\":{\"NH3\":%s"), measure_gas(NH3, buffer)); + ResponseAppend_P(PSTR(",\"CO\":%s"), measure_gas(CO, buffer)); + ResponseAppend_P(PSTR(",\"NO2\":%s"), measure_gas(NO2, buffer)); + ResponseAppend_P(PSTR(",\"C3H8\":%s"), measure_gas(C3H8, buffer)); + ResponseAppend_P(PSTR(",\"C4H10\":%s"), measure_gas(C4H10, buffer)); + ResponseAppend_P(PSTR(",\"CH4\":%s"), measure_gas(GAS_CH4, buffer)); + ResponseAppend_P(PSTR(",\"H2\":%s"), measure_gas(H2, buffer)); + ResponseAppend_P(PSTR(",\"C2H5OH\":%s}"), measure_gas(C2H5OH, buffer)); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_MGS_GAS, "NH3", measure_gas(NH3, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "CO", measure_gas(CO, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "NO2", measure_gas(NO2, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "C3H8", measure_gas(C3H8, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "C4H10", measure_gas(C4H10, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "CH4", measure_gas(GAS_CH4, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "H2", measure_gas(H2, buffer)); + WSContentSend_PD(HTTP_MGS_GAS, "C2H5OH", measure_gas(C2H5OH, buffer)); +#endif + } +} + + + + + +bool Xsns19(uint8_t function) +{ + if (!I2cEnabled(XI2C_17)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + MGSPrepare(); + } + else if (mgs_detected) { + switch (function) { + case FUNC_JSON_APPEND: + MGSShow(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MGSShow(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_20_novasds.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_20_novasds.ino" +#ifdef USE_NOVA_SDS +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_20_novasds.ino" +#define XSNS_20 20 + +#include + +#ifndef STARTING_OFFSET +#define STARTING_OFFSET 30 +#endif +#if STARTING_OFFSET < 10 +#error "Please set STARTING_OFFSET >= 10" +#endif +#ifndef NOVA_SDS_RECDATA_TIMEOUT +#define NOVA_SDS_RECDATA_TIMEOUT 150 +#endif +#ifndef NOVA_SDS_DEVICE_ID +#define NOVA_SDS_DEVICE_ID 0xFFFF +#endif + +TasmotaSerial *NovaSdsSerial; + +uint8_t novasds_type = 1; +uint8_t novasds_valid = 0; +uint8_t cont_mode = 1; + +struct sds011data { + uint16_t pm100; + uint16_t pm25; +} novasds_data; +uint16_t pm100_sum; +uint16_t pm25_sum; + + +#define NOVA_SDS_REPORTING_MODE 2 +#define NOVA_SDS_QUERY_DATA 4 +#define NOVA_SDS_SET_DEVICE_ID 5 +#define NOVA_SDS_SLEEP_AND_WORK 6 +#define NOVA_SDS_WORKING_PERIOD 8 +#define NOVA_SDS_CHECK_FIRMWARE_VER 7 + #define NOVA_SDS_QUERY_MODE 0 + #define NOVA_SDS_SET_MODE 1 + #define NOVA_SDS_REPORT_ACTIVE 0 + #define NOVA_SDS_REPORT_QUERY 1 + #define NOVA_SDS_SLEEP 0 + #define NOVA_SDS_WORK 1 + + +bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensorid, uint8_t *buffer) +{ + uint8_t novasds_cmnd[19] = {0xAA, 0xB4, byte1, byte2, byte3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (uint8_t)(sensorid & 0xFF), (uint8_t)((sensorid>>8) & 0xFF), 0x00, 0xAB}; + + + for (uint32_t i = 2; i < 17; i++) { + novasds_cmnd[17] += novasds_cmnd[i]; + } + + + + + + NovaSdsSerial->write(novasds_cmnd, sizeof(novasds_cmnd)); + NovaSdsSerial->flush(); + + + unsigned long cmndtime = millis(); + while ( (TimePassedSince(cmndtime) < NOVA_SDS_RECDATA_TIMEOUT) && ( ! NovaSdsSerial->available() ) ); + if ( ! NovaSdsSerial->available() ) { + + return false; + } + uint8_t recbuf[10]; + memset(recbuf, 0, sizeof(recbuf)); + + while ( (TimePassedSince(cmndtime) < NOVA_SDS_RECDATA_TIMEOUT) && ( NovaSdsSerial->available() > 0) && (0xAA != (recbuf[0] = NovaSdsSerial->read())) ); + if ( 0xAA != recbuf[0] ) { + + return false; + } + + + NovaSdsSerial->readBytes(&recbuf[1], 9); + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, recbuf, sizeof(recbuf)); + + if ( nullptr != buffer ) { + + memcpy(buffer, recbuf, sizeof(recbuf)); + } + + + if ((0xAB != recbuf[9] ) || (recbuf[8] != ((recbuf[2] + recbuf[3] + recbuf[4] + recbuf[5] + recbuf[6] + recbuf[7]) & 0xFF))) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("SDS: " D_CHECKSUM_FAILURE)); + return false; + } + + return true; +} + +void NovaSdsSetWorkPeriod(void) +{ + + NovaSdsCommand(NOVA_SDS_WORKING_PERIOD, NOVA_SDS_SET_MODE, 0, NOVA_SDS_DEVICE_ID, nullptr); + + NovaSdsCommand(NOVA_SDS_REPORTING_MODE, NOVA_SDS_SET_MODE, NOVA_SDS_REPORT_QUERY, NOVA_SDS_DEVICE_ID, nullptr); +} + +bool NovaSdsReadData(void) +{ + uint8_t d[10]; + if ( ! NovaSdsCommand(NOVA_SDS_QUERY_DATA, 0, 0, NOVA_SDS_DEVICE_ID, d) ) { + return false; + } + novasds_data.pm25 = (d[2] + 256 * d[3]); + novasds_data.pm100 = (d[4] + 256 * d[5]); + + return true; +} + + + +void NovaSdsSecond(void) +{ + if (!novasds_valid) + { + NovaSdsSetWorkPeriod(); + novasds_valid=1; + } + if((Settings.tele_period - Settings.novasds_startingoffset <= 0)) + { + if(!cont_mode) + { + cont_mode = 1; + NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_WORK, NOVA_SDS_DEVICE_ID, nullptr); + } + } + else + cont_mode = 0; + + if(tele_period == Settings.tele_period - Settings.novasds_startingoffset && !cont_mode) + { + NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_WORK, NOVA_SDS_DEVICE_ID, nullptr); + } + if(tele_period >= Settings.tele_period-5 && tele_period <= Settings.tele_period-2) + { + if(!(NovaSdsReadData())) novasds_valid=0; + pm100_sum += novasds_data.pm100; + pm25_sum += novasds_data.pm25; + } + if(tele_period == Settings.tele_period-1) + { + novasds_data.pm100 = pm100_sum >> 2; + novasds_data.pm25 = pm25_sum >> 2; + if(!cont_mode) + NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_SLEEP, NOVA_SDS_DEVICE_ID, nullptr); + pm100_sum = pm25_sum = 0; + } +} + + + + + + + +bool NovaSdsCommandSensor(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 256)) { + if( XdrvMailbox.payload < 10 ) Settings.novasds_startingoffset = 10; + else Settings.novasds_startingoffset = XdrvMailbox.payload; + } + Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_20, Settings.novasds_startingoffset); + + return true; +} + +void NovaSdsInit(void) +{ + novasds_type = 0; + if (pin[GPIO_SDS0X1_RX] < 99 && pin[GPIO_SDS0X1_TX] < 99) { + NovaSdsSerial = new TasmotaSerial(pin[GPIO_SDS0X1_RX], pin[GPIO_SDS0X1_TX], 1); + if (NovaSdsSerial->begin(9600)) { + if (NovaSdsSerial->hardwareSerial()) { + ClaimSerial(); + } + novasds_type = 1; + NovaSdsSetWorkPeriod(); + } + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SDS0X1_SNS[] PROGMEM = + "{s}SDS0X1 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}SDS0X1 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; +#endif + +void NovaSdsShow(bool json) +{ + if (novasds_valid) { + float pm10f = (float)(novasds_data.pm100) / 10.0f; + float pm2_5f = (float)(novasds_data.pm25) / 10.0f; + char pm10[33]; + dtostrfd(pm10f, 1, pm10); + char pm2_5[33]; + dtostrfd(pm2_5f, 1, pm2_5); + if (json) { + ResponseAppend_P(PSTR(",\"SDS0X1\":{\"PM2.5\":%s,\"PM10\":%s}"), pm2_5, pm10); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, pm2_5); + DomoticzSensor(DZ_CURRENT, pm10); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SDS0X1_SNS, pm2_5, pm10); +#endif + } + } +} + + + + + +bool Xsns20(uint8_t function) +{ + bool result = false; + + if (novasds_type) { + switch (function) { + case FUNC_INIT: + NovaSdsInit(); + break; + case FUNC_EVERY_SECOND: + NovaSdsSecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_20 == XdrvMailbox.index) { + result = NovaSdsCommandSensor(); + } + break; + case FUNC_JSON_APPEND: + NovaSdsShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + NovaSdsShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_21_sgp30.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_21_sgp30.ino" +#ifdef USE_I2C +#ifdef USE_SGP30 +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_21_sgp30.ino" +#define XSNS_21 21 +#define XI2C_18 18 + +#define SGP30_ADDRESS 0x58 + +#include "Adafruit_SGP30.h" +Adafruit_SGP30 sgp; + +bool sgp30_type = false; +bool sgp30_ready = false; +float sgp30_abshum; + + + +void sgp30_Init(void) +{ + if (I2cActive(SGP30_ADDRESS)) { return; } + + if (sgp.begin()) { + sgp30_type = true; + + I2cSetActiveFound(SGP30_ADDRESS, "SGP30"); + } +} + + +#define POW_FUNC FastPrecisePow + +float sgp30_AbsoluteHumidity(float temperature, float humidity,char tempUnit) { + + + + + + float temp = NAN; + const float mw = 18.01534; + const float r = 8.31447215; + + if (isnan(temperature) || isnan(humidity) ) { + return NAN; + } + + if (tempUnit != 'C') { + temperature = (temperature - 32.0) * (5.0 / 9.0); + } + + temp = POW_FUNC(2.718281828, (17.67 * temperature) / (temperature + 243.5)); + + + return (6.112 * temp * humidity * mw) / ((273.15 + temperature) * r); +} + +#define SAVE_PERIOD 30 + +void Sgp30Update(void) +{ + sgp30_ready = false; + if (!sgp.IAQmeasure()) { + return; + } + if (global_update && (global_humidity > 0) && (global_temperature != 9999)) { + + sgp30_abshum=sgp30_AbsoluteHumidity(global_temperature,global_humidity,TempUnit()); + sgp.setHumidity(sgp30_abshum*1000); + } + sgp30_ready = true; + + + if (!(uptime%SAVE_PERIOD)) { + + uint16_t TVOC_base; + uint16_t eCO2_base; + + if (!sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) return; + + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SGP30[] PROGMEM = + "{s}SGP30 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" + "{s}SGP30 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; +const char HTTP_SNS_AHUM[] PROGMEM = "{s}SGP30 Abs Humidity{m}%s g/m3{e}"; +#endif + +#define D_JSON_AHUM "aHumidity" + +void Sgp30Show(bool json) +{ + if (sgp30_ready) { + char abs_hum[33]; + + if (json) { + ResponseAppend_P(PSTR(",\"SGP30\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d"), sgp.eCO2, sgp.TVOC); + if (global_update && global_humidity>0 && global_temperature!=9999) { + + dtostrfd(sgp30_abshum,4,abs_hum); + ResponseAppend_P(PSTR(",\"" D_JSON_AHUM "\":%s"),abs_hum); + } + ResponseJsonEnd(); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, sgp.eCO2); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_SGP30, sgp.eCO2, sgp.TVOC); + if (global_update) { + WSContentSend_PD(HTTP_SNS_AHUM, abs_hum); + } +#endif + } + } +} + + + + + +bool Xsns21(uint8_t function) +{ + if (!I2cEnabled(XI2C_18)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + sgp30_Init(); + } + else if (sgp30_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Sgp30Update(); + break; + case FUNC_JSON_APPEND: + Sgp30Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sgp30Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_22_sr04.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_22_sr04.ino" +#ifdef USE_SR04 + +#include +#include +# 32 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_22_sr04.ino" +#define XSNS_22 22 + +uint8_t sr04_type = 1; +int sr04_echo_pin = 0; +int sr04_trig_pin = 0; +real64_t distance; + +NewPing* sonar = nullptr; +TasmotaSerial* sonar_serial = nullptr; + + + +uint8_t Sr04TModeDetect(void) +{ + sr04_type = 0; + if (pin[GPIO_SR04_ECHO]>=99) return sr04_type; + + sr04_echo_pin = pin[GPIO_SR04_ECHO]; + sr04_trig_pin = (pin[GPIO_SR04_TRIG] < 99) ? pin[GPIO_SR04_TRIG] : -1; + sonar_serial = new TasmotaSerial(sr04_echo_pin, sr04_trig_pin, 1); + + if (sonar_serial->begin(9600,1)) { + DEBUG_SENSOR_LOG(PSTR("SR04: Detect mode")); + + if (sr04_trig_pin!=-1) { + sr04_type = (Sr04TMiddleValue(Sr04TMode3Distance(),Sr04TMode3Distance(),Sr04TMode3Distance())!=NO_ECHO)?3:1; + } else { + sr04_type = 2; + } + } else { + sr04_type = 1; + } + + if (sr04_type < 2) { + delete sonar_serial; + sonar_serial = nullptr; + sonar = new NewPing(sr04_trig_pin, sr04_echo_pin, 300); + } else { + if (sonar_serial->hardwareSerial()) { + ClaimSerial(); + } + } + + AddLog_P2(LOG_LEVEL_INFO,PSTR("SR04: Mode %d"), sr04_type); + return sr04_type; +} + +uint16_t Sr04TMiddleValue(uint16_t first, uint16_t second, uint16_t third) +{ + uint16_t ret = first; + if (first > second) { + first = second; + second = ret; + } + + if (third < first) { + return first; + } else if (third > second) { + return second; + } else { + return third; + } +} + +uint16_t Sr04TMode3Distance() { + + sonar_serial->write(0x55); + sonar_serial->flush(); + + return Sr04TMode2Distance(); +} + +uint16_t Sr04TMode2Distance(void) +{ + sonar_serial->setTimeout(300); + const char startByte = 0xff; + + if (!sonar_serial->find(startByte)) { + + return NO_ECHO; + } + + delay(5); + + uint8_t crc = sonar_serial->read(); + + uint16_t distance = ((uint16_t)crc) << 8; + + + distance += sonar_serial->read(); + crc += distance & 0x00ff; + crc += 0x00FF; + + + if (crc != sonar_serial->read()) { + AddLog_P2(LOG_LEVEL_ERROR,PSTR("SR04: Reading CRC error.")); + return NO_ECHO; + } + + return distance; +} + +void Sr04TReading(void) { + + if (sonar_serial==nullptr && sonar==nullptr) { + Sr04TModeDetect(); + } + + switch (sr04_type) { + case 3: + distance = (real64_t)(Sr04TMiddleValue(Sr04TMode3Distance(),Sr04TMode3Distance(),Sr04TMode3Distance()))/ 10; + break; + case 2: + + while(sonar_serial->available()) sonar_serial->read(); + distance = (real64_t)(Sr04TMiddleValue(Sr04TMode2Distance(),Sr04TMode2Distance(),Sr04TMode2Distance()))/10; + break; + case 1: + distance = (real64_t)(sonar->ping_median(5))/ US_ROUNDTRIP_CM; + break; + default: + distance = NO_ECHO; + } + + return; +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_DISTANCE[] PROGMEM = + "{s}SR04 " D_DISTANCE "{m}%s" D_UNIT_CENTIMETER "{e}"; +#endif + +void Sr04Show(bool json) +{ + + if (distance != 0) { + char distance_chr[33]; + dtostrfd(distance, 3, distance_chr); + + if(json) { + ResponseAppend_P(PSTR(",\"SR04\":{\"" D_JSON_DISTANCE "\":%s}"), distance_chr); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_COUNT, distance_chr); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_DISTANCE, distance_chr); +#endif + } + } +} + + + + + +bool Xsns22(uint8_t function) +{ + bool result = false; + + if (sr04_type) { + switch (function) { + case FUNC_INIT: + result = (pin[GPIO_SR04_ECHO]<99); + break; + case FUNC_EVERY_SECOND: + Sr04TReading(); + result = true; + break; + case FUNC_JSON_APPEND: + Sr04Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Sr04Show(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_24_si1145.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_24_si1145.ino" +#ifdef USE_I2C +#ifdef USE_SI1145 +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_24_si1145.ino" +#define XSNS_24 24 +#define XI2C_19 19 + +#define SI114X_ADDR 0X60 + + + +#define SI114X_QUERY 0X80 +#define SI114X_SET 0XA0 +#define SI114X_NOP 0X00 +#define SI114X_RESET 0X01 +#define SI114X_BUSADDR 0X02 +#define SI114X_PS_FORCE 0X05 +#define SI114X_GET_CAL 0X12 +#define SI114X_ALS_FORCE 0X06 +#define SI114X_PSALS_FORCE 0X07 +#define SI114X_PS_PAUSE 0X09 +#define SI114X_ALS_PAUSE 0X0A +#define SI114X_PSALS_PAUSE 0X0B +#define SI114X_PS_AUTO 0X0D +#define SI114X_ALS_AUTO 0X0E +#define SI114X_PSALS_AUTO 0X0F + + + +#define SI114X_PART_ID 0X00 +#define SI114X_REV_ID 0X01 +#define SI114X_SEQ_ID 0X02 +#define SI114X_INT_CFG 0X03 +#define SI114X_IRQ_ENABLE 0X04 +#define SI114X_IRQ_MODE1 0x05 +#define SI114X_IRQ_MODE2 0x06 +#define SI114X_HW_KEY 0X07 +#define SI114X_MEAS_RATE0 0X08 +#define SI114X_MEAS_RATE1 0X09 +#define SI114X_PS_RATE 0X0A +#define SI114X_PS_LED21 0X0F +#define SI114X_PS_LED3 0X10 +#define SI114X_UCOEFF0 0X13 +#define SI114X_UCOEFF1 0X14 +#define SI114X_UCOEFF2 0X15 +#define SI114X_UCOEFF3 0X16 +#define SI114X_WR 0X17 +#define SI114X_COMMAND 0X18 +#define SI114X_RESPONSE 0X20 +#define SI114X_IRQ_STATUS 0X21 +#define SI114X_ALS_VIS_DATA0 0X22 +#define SI114X_ALS_VIS_DATA1 0X23 +#define SI114X_ALS_IR_DATA0 0X24 +#define SI114X_ALS_IR_DATA1 0X25 +#define SI114X_PS1_DATA0 0X26 +#define SI114X_PS1_DATA1 0X27 +#define SI114X_PS2_DATA0 0X28 +#define SI114X_PS2_DATA1 0X29 +#define SI114X_PS3_DATA0 0X2A +#define SI114X_PS3_DATA1 0X2B +#define SI114X_AUX_DATA0_UVINDEX0 0X2C +#define SI114X_AUX_DATA1_UVINDEX1 0X2D +#define SI114X_RD 0X2E +#define SI114X_CHIP_STAT 0X30 + + + +#define SI114X_CHLIST 0X01 +#define SI114X_CHLIST_ENUV 0x80 +#define SI114X_CHLIST_ENAUX 0x40 +#define SI114X_CHLIST_ENALSIR 0x20 +#define SI114X_CHLIST_ENALSVIS 0x10 +#define SI114X_CHLIST_ENPS1 0x01 +#define SI114X_CHLIST_ENPS2 0x02 +#define SI114X_CHLIST_ENPS3 0x04 + +#define SI114X_PSLED12_SELECT 0X02 +#define SI114X_PSLED3_SELECT 0X03 + +#define SI114X_PS_ENCODE 0X05 +#define SI114X_ALS_ENCODE 0X06 + +#define SI114X_PS1_ADCMUX 0X07 +#define SI114X_PS2_ADCMUX 0X08 +#define SI114X_PS3_ADCMUX 0X09 + +#define SI114X_PS_ADC_COUNTER 0X0A +#define SI114X_PS_ADC_GAIN 0X0B +#define SI114X_PS_ADC_MISC 0X0C + +#define SI114X_ALS_IR_ADC_MUX 0X0E +#define SI114X_AUX_ADC_MUX 0X0F + +#define SI114X_ALS_VIS_ADC_COUNTER 0X10 +#define SI114X_ALS_VIS_ADC_GAIN 0X11 +#define SI114X_ALS_VIS_ADC_MISC 0X12 + +#define SI114X_LED_REC 0X1C + +#define SI114X_ALS_IR_ADC_COUNTER 0X1D +#define SI114X_ALS_IR_ADC_GAIN 0X1E +#define SI114X_ALS_IR_ADC_MISC 0X1F + + + + +#define SI114X_ADCMUX_SMALL_IR 0x00 +#define SI114X_ADCMUX_VISIABLE 0x02 +#define SI114X_ADCMUX_LARGE_IR 0x03 +#define SI114X_ADCMUX_NO 0x06 +#define SI114X_ADCMUX_GND 0x25 +#define SI114X_ADCMUX_TEMPERATURE 0x65 +#define SI114X_ADCMUX_VDD 0x75 + +#define SI114X_PSLED12_SELECT_PS1_NONE 0x00 +#define SI114X_PSLED12_SELECT_PS1_LED1 0x01 +#define SI114X_PSLED12_SELECT_PS1_LED2 0x02 +#define SI114X_PSLED12_SELECT_PS1_LED3 0x04 +#define SI114X_PSLED12_SELECT_PS2_NONE 0x00 +#define SI114X_PSLED12_SELECT_PS2_LED1 0x10 +#define SI114X_PSLED12_SELECT_PS2_LED2 0x20 +#define SI114X_PSLED12_SELECT_PS2_LED3 0x40 +#define SI114X_PSLED3_SELECT_PS2_NONE 0x00 +#define SI114X_PSLED3_SELECT_PS2_LED1 0x10 +#define SI114X_PSLED3_SELECT_PS2_LED2 0x20 +#define SI114X_PSLED3_SELECT_PS2_LED3 0x40 + +#define SI114X_ADC_GAIN_DIV1 0X00 +#define SI114X_ADC_GAIN_DIV2 0X01 +#define SI114X_ADC_GAIN_DIV4 0X02 +#define SI114X_ADC_GAIN_DIV8 0X03 +#define SI114X_ADC_GAIN_DIV16 0X04 +#define SI114X_ADC_GAIN_DIV32 0X05 + +#define SI114X_LED_CURRENT_5MA 0X01 +#define SI114X_LED_CURRENT_11MA 0X02 +#define SI114X_LED_CURRENT_22MA 0X03 +#define SI114X_LED_CURRENT_45MA 0X04 + +#define SI114X_ADC_COUNTER_1ADCCLK 0X00 +#define SI114X_ADC_COUNTER_7ADCCLK 0X01 +#define SI114X_ADC_COUNTER_15ADCCLK 0X02 +#define SI114X_ADC_COUNTER_31ADCCLK 0X03 +#define SI114X_ADC_COUNTER_63ADCCLK 0X04 +#define SI114X_ADC_COUNTER_127ADCCLK 0X05 +#define SI114X_ADC_COUNTER_255ADCCLK 0X06 +#define SI114X_ADC_COUNTER_511ADCCLK 0X07 + +#define SI114X_ADC_MISC_LOWRANGE 0X00 +#define SI114X_ADC_MISC_HIGHRANGE 0X20 +#define SI114X_ADC_MISC_ADC_NORMALPROXIMITY 0X00 +#define SI114X_ADC_MISC_ADC_RAWADC 0X04 + +#define SI114X_INT_CFG_INTOE 0X01 + +#define SI114X_IRQEN_ALS 0x01 +#define SI114X_IRQEN_PS1 0x04 +#define SI114X_IRQEN_PS2 0x08 +#define SI114X_IRQEN_PS3 0x10 + +uint16_t si1145_visible; +uint16_t si1145_infrared; +uint16_t si1145_uvindex; + +bool si1145_type = false; +uint8_t si1145_valid = 0; + + + +uint8_t Si1145ReadByte(uint8_t reg) +{ + return I2cRead8(SI114X_ADDR, reg); +} + +uint16_t Si1145ReadHalfWord(uint8_t reg) +{ + return I2cRead16LE(SI114X_ADDR, reg); +} + +bool Si1145WriteByte(uint8_t reg, uint16_t val) +{ + I2cWrite8(SI114X_ADDR, reg, val); +} + +uint8_t Si1145WriteParamData(uint8_t p, uint8_t v) +{ + Si1145WriteByte(SI114X_WR, v); + Si1145WriteByte(SI114X_COMMAND, p | SI114X_SET); + return Si1145ReadByte(SI114X_RD); +} + + + +bool Si1145Present(void) +{ + return (Si1145ReadByte(SI114X_PART_ID) == 0X45); +} + +void Si1145Reset(void) +{ + Si1145WriteByte(SI114X_MEAS_RATE0, 0); + Si1145WriteByte(SI114X_MEAS_RATE1, 0); + Si1145WriteByte(SI114X_IRQ_ENABLE, 0); + Si1145WriteByte(SI114X_IRQ_MODE1, 0); + Si1145WriteByte(SI114X_IRQ_MODE2, 0); + Si1145WriteByte(SI114X_INT_CFG, 0); + Si1145WriteByte(SI114X_IRQ_STATUS, 0xFF); + + Si1145WriteByte(SI114X_COMMAND, SI114X_RESET); + delay(10); + Si1145WriteByte(SI114X_HW_KEY, 0x17); + delay(10); +} + +void Si1145DeInit(void) +{ + + + Si1145WriteByte(SI114X_UCOEFF0, 0x29); + Si1145WriteByte(SI114X_UCOEFF1, 0x89); + Si1145WriteByte(SI114X_UCOEFF2, 0x02); + Si1145WriteByte(SI114X_UCOEFF3, 0x00); + Si1145WriteParamData(SI114X_CHLIST, SI114X_CHLIST_ENUV | SI114X_CHLIST_ENALSIR | SI114X_CHLIST_ENALSVIS | SI114X_CHLIST_ENPS1); + + + + Si1145WriteParamData(SI114X_PS1_ADCMUX, SI114X_ADCMUX_LARGE_IR); + Si1145WriteByte(SI114X_PS_LED21, SI114X_LED_CURRENT_22MA); + Si1145WriteParamData(SI114X_PSLED12_SELECT, SI114X_PSLED12_SELECT_PS1_LED1); + + + + Si1145WriteParamData(SI114X_PS_ADC_GAIN, SI114X_ADC_GAIN_DIV1); + Si1145WriteParamData(SI114X_PS_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); + Si1145WriteParamData(SI114X_PS_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE | SI114X_ADC_MISC_ADC_RAWADC); + + + + Si1145WriteParamData(SI114X_ALS_VIS_ADC_GAIN, SI114X_ADC_GAIN_DIV1); + Si1145WriteParamData(SI114X_ALS_VIS_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); + Si1145WriteParamData(SI114X_ALS_VIS_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE); + + + + Si1145WriteParamData(SI114X_ALS_IR_ADC_GAIN, SI114X_ADC_GAIN_DIV1); + Si1145WriteParamData(SI114X_ALS_IR_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); + Si1145WriteParamData(SI114X_ALS_IR_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE); + + + + Si1145WriteByte(SI114X_INT_CFG, SI114X_INT_CFG_INTOE); + Si1145WriteByte(SI114X_IRQ_ENABLE, SI114X_IRQEN_ALS); + + + + Si1145WriteByte(SI114X_MEAS_RATE0, 0xFF); + Si1145WriteByte(SI114X_COMMAND, SI114X_PSALS_AUTO); +} + +bool Si1145Begin(void) +{ + if (!Si1145Present()) { return false; } + + Si1145Reset(); + Si1145DeInit(); + return true; +} + + +uint16_t Si1145ReadUV(void) +{ + return Si1145ReadHalfWord(SI114X_AUX_DATA0_UVINDEX0); +} + + +uint16_t Si1145ReadVisible(void) +{ + return Si1145ReadHalfWord(SI114X_ALS_VIS_DATA0); +} + + +uint16_t Si1145ReadIR(void) +{ + return Si1145ReadHalfWord(SI114X_ALS_IR_DATA0); +} + + + +bool Si1145Read(void) +{ + if (si1145_valid) { si1145_valid--; } + + if (!Si1145Present()) { return false; } + + si1145_visible = Si1145ReadVisible(); + si1145_infrared = Si1145ReadIR(); + si1145_uvindex = Si1145ReadUV(); + si1145_valid = SENSOR_MAX_MISS; + return true; +} + +void Si1145Detect(void) +{ + if (I2cActive(SI114X_ADDR)) { return; } + + if (Si1145Begin()) { + si1145_type = true; + I2cSetActiveFound(SI114X_ADDR, "SI1145"); + } +} + +void Si1145Update(void) +{ + if (!Si1145Read()) { + AddLogMissed("SI1145", si1145_valid); + } +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SI1145[] PROGMEM = + "{s}SI1145 " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}" + "{s}SI1145 " D_INFRARED "{m}%d " D_UNIT_LUX "{e}" + "{s}SI1145 " D_UV_INDEX "{m}%d.%d{e}"; +#endif + +void Si1145Show(bool json) +{ + if (si1145_valid) { + if (json) { + ResponseAppend_P(PSTR(",\"SI1145\":{\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_INFRARED "\":%d,\"" D_JSON_UV_INDEX "\":%d.%d}"), + si1145_visible, si1145_infrared, si1145_uvindex /100, si1145_uvindex %100); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, si1145_visible); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_SI1145, si1145_visible, si1145_infrared, si1145_uvindex /100, si1145_uvindex %100); +#endif + } + } +} + + + + + +bool Xsns24(uint8_t function) +{ + if (!I2cEnabled(XI2C_19)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Si1145Detect(); + } + else if (si1145_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Si1145Update(); + break; + case FUNC_JSON_APPEND: + Si1145Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Si1145Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_26_lm75ad.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_26_lm75ad.ino" +#ifdef USE_I2C +#ifdef USE_LM75AD +# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_26_lm75ad.ino" +#define XSNS_26 26 +#define XI2C_20 20 + +#define LM75AD_ADDRESS1 0x48 +#define LM75AD_ADDRESS2 0x49 +#define LM75AD_ADDRESS3 0x4A +#define LM75AD_ADDRESS4 0x4B +#define LM75AD_ADDRESS5 0x4C +#define LM75AD_ADDRESS6 0x4D +#define LM75AD_ADDRESS7 0x4E +#define LM75AD_ADDRESS8 0x4F + +#define LM75_TEMP_REGISTER 0x00 +#define LM75_CONF_REGISTER 0x01 +#define LM75_THYST_REGISTER 0x02 +#define LM75_TOS_REGISTER 0x03 + +bool lm75ad_type = false; +uint8_t lm75ad_address; +uint8_t lm75ad_addresses[] = { LM75AD_ADDRESS1, LM75AD_ADDRESS2, LM75AD_ADDRESS3, LM75AD_ADDRESS4, LM75AD_ADDRESS5, LM75AD_ADDRESS6, LM75AD_ADDRESS7, LM75AD_ADDRESS8 }; + +void LM75ADDetect(void) +{ + for (uint32_t i = 0; i < sizeof(lm75ad_addresses); i++) { + lm75ad_address = lm75ad_addresses[i]; + if (I2cActive(lm75ad_address)) { + continue; } + if (!I2cSetDevice(lm75ad_address)) { + break; + } + uint16_t buffer; + if (I2cValidRead16(&buffer, lm75ad_address, LM75_THYST_REGISTER)) { + if (buffer == 0x4B00) { + lm75ad_type = true; + I2cSetActiveFound(lm75ad_address, "LM75AD"); + break; + } + } + } +} + +float LM75ADGetTemp(void) +{ + int16_t sign = 1; + + uint16_t t = I2cRead16(lm75ad_address, LM75_TEMP_REGISTER); + if (t & 0x8000) { + t = (~t) +0x20; + sign = -1; + } + t = t >> 5; + return ConvertTemp(sign * t * 0.125); +} + +void LM75ADShow(bool json) +{ + float t = LM75ADGetTemp(); + char temperature[33]; + dtostrfd(t, Settings.flag2.temperature_resolution, temperature); + + if (json) { + ResponseAppend_P(PSTR(",\"LM75AD\":{\"" D_JSON_TEMPERATURE "\":%s}"), temperature); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_TEMP, temperature); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, "LM75AD", temperature, TempUnit()); +#endif + } +} + + + + + +bool Xsns26(uint8_t function) +{ + if (!I2cEnabled(XI2C_20)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + LM75ADDetect(); + } + else if (lm75ad_type) { + switch (function) { + case FUNC_JSON_APPEND: + LM75ADShow(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + LM75ADShow(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" +# 28 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" +#ifdef USE_I2C +#ifdef USE_APDS9960 +# 39 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" +#define XSNS_27 27 +#define XI2C_21 21 + +#if defined(USE_SHT) || defined(USE_VEML6070) || defined(USE_TSL2561) + #warning **** Turned off conflicting drivers SHT and VEML6070 **** + #ifdef USE_SHT + #undef USE_SHT + #endif + #ifdef USE_VEML6070 + #undef USE_VEML6070 + #endif + #ifdef USE_TSL2561 + #undef USE_TSL2561 + #endif +#endif + +#define APDS9960_I2C_ADDR 0x39 + +#define APDS9960_CHIPID_1 0xAB +#define APDS9960_CHIPID_2 0x9C + +#define APDS9930_CHIPID_1 0x12 +#define APDS9930_CHIPID_2 0x39 + + +#define GESTURE_THRESHOLD_OUT 10 +#define GESTURE_SENSITIVITY_1 50 +#define GESTURE_SENSITIVITY_2 20 + +uint8_t APDS9960addr; +uint8_t APDS9960type = 0; +char APDS9960stype[] = "APDS9960"; +char currentGesture[6]; +uint8_t gesture_mode = 1; + + +volatile uint8_t recovery_loop_counter = 0; +#define APDS9960_LONG_RECOVERY 50 +#define APDS9960_MAX_GESTURE_CYCLES 50 +bool APDS9960_overload = false; + +#ifdef USE_WEBSERVER +const char HTTP_APDS_9960_SNS[] PROGMEM = + "{s}" "Red" "{m}%s{e}" + "{s}" "Green" "{m}%s{e}" + "{s}" "Blue" "{m}%s{e}" + "{s}" "Ambient" "{m}%s " D_UNIT_LUX "{e}" + "{s}" "CCT" "{m}%s " "K" "{e}" + "{s}" "Proximity" "{m}%s{e}"; +#endif +# 97 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" +#define FIFO_PAUSE_TIME 30 + + +#define APDS9960_ENABLE 0x80 +#define APDS9960_ATIME 0x81 +#define APDS9960_WTIME 0x83 +#define APDS9960_AILTL 0x84 +#define APDS9960_AILTH 0x85 +#define APDS9960_AIHTL 0x86 +#define APDS9960_AIHTH 0x87 +#define APDS9960_PILT 0x89 +#define APDS9960_PIHT 0x8B +#define APDS9960_PERS 0x8C +#define APDS9960_CONFIG1 0x8D +#define APDS9960_PPULSE 0x8E +#define APDS9960_CONTROL 0x8F +#define APDS9960_CONFIG2 0x90 +#define APDS9960_ID 0x92 +#define APDS9960_STATUS 0x93 +#define APDS9960_CDATAL 0x94 +#define APDS9960_CDATAH 0x95 +#define APDS9960_RDATAL 0x96 +#define APDS9960_RDATAH 0x97 +#define APDS9960_GDATAL 0x98 +#define APDS9960_GDATAH 0x99 +#define APDS9960_BDATAL 0x9A +#define APDS9960_BDATAH 0x9B +#define APDS9960_PDATA 0x9C +#define APDS9960_POFFSET_UR 0x9D +#define APDS9960_POFFSET_DL 0x9E +#define APDS9960_CONFIG3 0x9F +#define APDS9960_GPENTH 0xA0 +#define APDS9960_GEXTH 0xA1 +#define APDS9960_GCONF1 0xA2 +#define APDS9960_GCONF2 0xA3 +#define APDS9960_GOFFSET_U 0xA4 +#define APDS9960_GOFFSET_D 0xA5 +#define APDS9960_GOFFSET_L 0xA7 +#define APDS9960_GOFFSET_R 0xA9 +#define APDS9960_GPULSE 0xA6 +#define APDS9960_GCONF3 0xAA +#define APDS9960_GCONF4 0xAB +#define APDS9960_GFLVL 0xAE +#define APDS9960_GSTATUS 0xAF +#define APDS9960_IFORCE 0xE4 +#define APDS9960_PICLEAR 0xE5 +#define APDS9960_CICLEAR 0xE6 +#define APDS9960_AICLEAR 0xE7 +#define APDS9960_GFIFO_U 0xFC +#define APDS9960_GFIFO_D 0xFD +#define APDS9960_GFIFO_L 0xFE +#define APDS9960_GFIFO_R 0xFF + + +#define APDS9960_PON 0b00000001 +#define APDS9960_AEN 0b00000010 +#define APDS9960_PEN 0b00000100 +#define APDS9960_WEN 0b00001000 +#define APSD9960_AIEN 0b00010000 +#define APDS9960_PIEN 0b00100000 +#define APDS9960_GEN 0b01000000 +#define APDS9960_GVALID 0b00000001 + + +#define OFF 0 +#define ON 1 + + +#define POWER 0 +#define AMBIENT_LIGHT 1 +#define PROXIMITY 2 +#define WAIT 3 +#define AMBIENT_LIGHT_INT 4 +#define PROXIMITY_INT 5 +#define GESTURE 6 +#define ALL 7 + + +#define LED_DRIVE_100MA 0 +#define LED_DRIVE_50MA 1 +#define LED_DRIVE_25MA 2 +#define LED_DRIVE_12_5MA 3 + + +#define PGAIN_1X 0 +#define PGAIN_2X 1 +#define PGAIN_4X 2 +#define PGAIN_8X 3 + + +#define AGAIN_1X 0 +#define AGAIN_4X 1 +#define AGAIN_16X 2 +#define AGAIN_64X 3 + + +#define GGAIN_1X 0 +#define GGAIN_2X 1 +#define GGAIN_4X 2 +#define GGAIN_8X 3 + + +#define LED_BOOST_100 0 +#define LED_BOOST_150 1 +#define LED_BOOST_200 2 +#define LED_BOOST_300 3 + + +#define GWTIME_0MS 0 +#define GWTIME_2_8MS 1 +#define GWTIME_5_6MS 2 +#define GWTIME_8_4MS 3 +#define GWTIME_14_0MS 4 +#define GWTIME_22_4MS 5 +#define GWTIME_30_8MS 6 +#define GWTIME_39_2MS 7 + + +#define DEFAULT_ATIME 0xdb +#define DEFAULT_WTIME 246 +#define DEFAULT_PROX_PPULSE 0x87 +#define DEFAULT_GESTURE_PPULSE 0x89 +#define DEFAULT_POFFSET_UR 0 +#define DEFAULT_POFFSET_DL 0 +#define DEFAULT_CONFIG1 0x60 +#define DEFAULT_LDRIVE LED_DRIVE_100MA +#define DEFAULT_PGAIN PGAIN_4X +#define DEFAULT_AGAIN AGAIN_4X +#define DEFAULT_PILT 0 +#define DEFAULT_PIHT 50 +#define DEFAULT_AILT 0xFFFF +#define DEFAULT_AIHT 0 +#define DEFAULT_PERS 0x11 +#define DEFAULT_CONFIG2 0x01 +#define DEFAULT_CONFIG3 0 +#define DEFAULT_GPENTH 40 +#define DEFAULT_GEXTH 30 +#define DEFAULT_GCONF1 0x40 +#define DEFAULT_GGAIN GGAIN_4X +#define DEFAULT_GLDRIVE LED_DRIVE_100MA +#define DEFAULT_GWTIME GWTIME_2_8MS +#define DEFAULT_GOFFSET 0 +#define DEFAULT_GPULSE 0xC9 +#define DEFAULT_GCONF3 0 +#define DEFAULT_GIEN 0 + +#define ERROR 0xFF + + +enum { + DIR_NONE, + DIR_LEFT, + DIR_RIGHT, + DIR_UP, + DIR_DOWN, + DIR_ALL +}; + + +enum { + APDS9960_NA_STATE, + APDS9960_ALL_STATE +}; + + +typedef struct gesture_data_type { + uint8_t u_data[32]; + uint8_t d_data[32]; + uint8_t l_data[32]; + uint8_t r_data[32]; + uint8_t index; + uint8_t total_gestures; + uint8_t in_threshold; + uint8_t out_threshold; +} gesture_data_type; + + + gesture_data_type gesture_data_; + int16_t gesture_ud_delta_ = 0; + int16_t gesture_lr_delta_ = 0; + int16_t gesture_ud_count_ = 0; + int16_t gesture_lr_count_ = 0; + int16_t gesture_state_ = 0; + int16_t gesture_motion_ = DIR_NONE; + + typedef struct color_data_type { + uint16_t a; + uint16_t r; + uint16_t g; + uint16_t b; + uint8_t p; + uint16_t cct; + uint16_t lux; + } color_data_type; + + color_data_type color_data; + uint8_t APDS9960_aTime = DEFAULT_ATIME; +# 306 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + bool wireWriteByte(uint8_t val) + { + Wire.beginTransmission(APDS9960_I2C_ADDR); + Wire.write(val); + if( Wire.endTransmission() != 0 ) { + return false; + } + + return true; + } +# 325 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" +int8_t wireReadDataBlock( uint8_t reg, + uint8_t *val, + uint16_t len) +{ + unsigned char i = 0; + + + if (!wireWriteByte(reg)) { + return -1; + } + + + Wire.requestFrom(APDS9960_I2C_ADDR, len); + while (Wire.available()) { + if (i >= len) { + return -1; + } + val[i] = Wire.read(); + i++; + } + + return i; +} + + + + + + + +void calculateColorTemperature(void) +{ + float X, Y, Z; + float xc, yc; + float n; + float cct; + + + + + + X = (-0.14282F * color_data.r) + (1.54924F * color_data.g) + (-0.95641F * color_data.b); + Y = (-0.32466F * color_data.r) + (1.57837F * color_data.g) + (-0.73191F * color_data.b); + Z = (-0.68202F * color_data.r) + (0.77073F * color_data.g) + ( 0.56332F * color_data.b); + + + xc = (X) / (X + Y + Z); + yc = (Y) / (X + Y + Z); + + + n = (xc - 0.3320F) / (0.1858F - yc); + + + color_data.cct = (449.0F * FastPrecisePowf(n, 3)) + (3525.0F * FastPrecisePowf(n, 2)) + (6823.3F * n) + 5520.33F; + + return; +} +# 392 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getProxIntLowThresh(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT) ; + return val; + } + + + + + + + void setProxIntLowThresh(uint8_t threshold) + { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold); + } + + + + + + + uint8_t getProxIntHighThresh(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT) ; + return val; + } + + + + + + + + void setProxIntHighThresh(uint8_t threshold) + { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold); + } +# 448 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getLEDDrive(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL) ; + + val = (val >> 6) & 0b00000011; + + return val; + } +# 471 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + void setLEDDrive(uint8_t drive) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + + drive &= 0b00000011; + drive = drive << 6; + val &= 0b00111111; + val |= drive; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); + } +# 500 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getProximityGain(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL) ; + + val = (val >> 2) & 0b00000011; + + return val; + } +# 523 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + void setProximityGain(uint8_t drive) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + + drive &= 0b00000011; + drive = drive << 2; + val &= 0b11110011; + val |= drive; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); + } +# 564 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + void setAmbientLightGain(uint8_t drive) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); + + + drive &= 0b00000011; + val &= 0b11111100; + val |= drive; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); + } +# 591 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getLEDBoost(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2) ; + + + val = (val >> 4) & 0b00000011; + + return val; + } +# 615 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + void setLEDBoost(uint8_t boost) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2) ; + + boost &= 0b00000011; + boost = boost << 4; + val &= 0b11001111; + val |= boost; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, val) ; + } + + + + + + + uint8_t getProxGainCompEnable(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; + + + val = (val >> 5) & 0b00000001; + + return val; + } + + + + + + + void setProxGainCompEnable(uint8_t enable) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; + + + enable &= 0b00000001; + enable = enable << 5; + val &= 0b11011111; + val |= enable; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val) ; + } +# 683 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getProxPhotoMask(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; + + + val &= 0b00001111; + + return val; + } +# 708 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + void setProxPhotoMask(uint8_t mask) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; + + + mask &= 0b00001111; + val &= 0b11110000; + val |= mask; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val) ; + } + + + + + + + uint8_t getGestureEnterThresh(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GPENTH) ; + + return val; + } + + + + + + + void setGestureEnterThresh(uint8_t threshold) + { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPENTH, threshold) ; + + } + + + + + + + uint8_t getGestureExitThresh(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GEXTH) ; + + return val; + } + + + + + + + void setGestureExitThresh(uint8_t threshold) + { + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GEXTH, threshold) ; + } +# 786 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getGestureGain(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; + + + val = (val >> 5) & 0b00000011; + + return val; + } +# 810 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + void setGestureGain(uint8_t gain) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; + + + gain &= 0b00000011; + gain = gain << 5; + val &= 0b10011111; + val |= gain; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; + } +# 838 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getGestureLEDDrive(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; + + + val = (val >> 3) & 0b00000011; + + return val; + } +# 862 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + void setGestureLEDDrive(uint8_t drive) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; + + + drive &= 0b00000011; + drive = drive << 3; + val &= 0b11100111; + val |= drive; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; + } +# 894 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + uint8_t getGestureWaitTime(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; + + + val &= 0b00000111; + + return val; + } +# 922 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + void setGestureWaitTime(uint8_t time) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; + + + time &= 0b00000111; + val &= 0b11111000; + val |= time; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; + } + + + + + + + void getLightIntLowThreshold(uint16_t &threshold) + { + uint8_t val_byte; + threshold = 0; + + + val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AILTL) ; + threshold = val_byte; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_byte) ; + threshold = threshold + ((uint16_t)val_byte << 8); + } + + + + + + + + void setLightIntLowThreshold(uint16_t threshold) + { + uint8_t val_low; + uint8_t val_high; + + + val_low = threshold & 0x00FF; + val_high = (threshold & 0xFF00) >> 8; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTL, val_low) ; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_high) ; + + } + + + + + + + + void getLightIntHighThreshold(uint16_t &threshold) + { + uint8_t val_byte; + threshold = 0; + + + val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AIHTL); + threshold = val_byte; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_byte) ; + threshold = threshold + ((uint16_t)val_byte << 8); + } + + + + + + + void setLightIntHighThreshold(uint16_t threshold) + { + uint8_t val_low; + uint8_t val_high; + + + val_low = threshold & 0x00FF; + val_high = (threshold & 0xFF00) >> 8; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTL, val_low); + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_high) ; + } + + + + + + + + void getProximityIntLowThreshold(uint8_t &threshold) + { + threshold = 0; + + + threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT); + + } + + + + + + + void setProximityIntLowThreshold(uint8_t threshold) + { + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold) ; + } +# 1055 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" + void getProximityIntHighThreshold(uint8_t &threshold) + { + threshold = 0; + + + threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT) ; + + } + + + + + + + void setProximityIntHighThreshold(uint8_t threshold) + { + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold) ; + } + + + + + + + uint8_t getAmbientLightIntEnable(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; + + + val = (val >> 4) & 0b00000001; + + return val; + } + + + + + + + void setAmbientLightIntEnable(uint8_t enable) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); + + + enable &= 0b00000001; + enable = enable << 4; + val &= 0b11101111; + val |= enable; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val) ; + } + + + + + + + uint8_t getProximityIntEnable(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; + + + val = (val >> 5) & 0b00000001; + + return val; + } + + + + + + + void setProximityIntEnable(uint8_t enable) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; + + + enable &= 0b00000001; + enable = enable << 5; + val &= 0b11011111; + val |= enable; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val) ; + } + + + + + + + uint8_t getGestureIntEnable(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; + + + val = (val >> 1) & 0b00000001; + + return val; + } + + + + + + + void setGestureIntEnable(uint8_t enable) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; + + + enable &= 0b00000001; + enable = enable << 1; + val &= 0b11111101; + val |= enable; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val) ; + } + + + + + + void clearAmbientLightInt(void) + { + uint8_t throwaway; + throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AICLEAR); + } + + + + + + void clearProximityInt(void) + { + uint8_t throwaway; + throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PICLEAR) ; + + } + + + + + + + uint8_t getGestureMode(void) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; + + + val &= 0b00000001; + + return val; + } + + + + + + + void setGestureMode(uint8_t mode) + { + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; + + + mode &= 0b00000001; + val &= 0b11111110; + val |= mode; + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val) ; + } + + +bool APDS9960_init(void) +{ + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, DEFAULT_ATIME) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, DEFAULT_WTIME) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_PROX_PPULSE) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_UR, DEFAULT_POFFSET_UR) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_DL, DEFAULT_POFFSET_DL) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG1, DEFAULT_CONFIG1) ; + + setLEDDrive(DEFAULT_LDRIVE); + + setProximityGain(DEFAULT_PGAIN); + + setAmbientLightGain(DEFAULT_AGAIN); + + setProxIntLowThresh(DEFAULT_PILT) ; + + setProxIntHighThresh(DEFAULT_PIHT); + + setLightIntLowThreshold(DEFAULT_AILT) ; + + setLightIntHighThreshold(DEFAULT_AIHT) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PERS, DEFAULT_PERS) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, DEFAULT_CONFIG2) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, DEFAULT_CONFIG3) ; + + + setGestureEnterThresh(DEFAULT_GPENTH); + + setGestureExitThresh(DEFAULT_GEXTH) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF1, DEFAULT_GCONF1) ; + + setGestureGain(DEFAULT_GGAIN) ; + + setGestureLEDDrive(DEFAULT_GLDRIVE) ; + + setGestureWaitTime(DEFAULT_GWTIME) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_U, DEFAULT_GOFFSET) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_D, DEFAULT_GOFFSET) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_L, DEFAULT_GOFFSET) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_R, DEFAULT_GOFFSET) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPULSE, DEFAULT_GPULSE) ; + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF3, DEFAULT_GCONF3) ; + + setGestureIntEnable(DEFAULT_GIEN); + + disablePower(); + + return true; +} +# 1333 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" +uint8_t getMode(void) +{ + uint8_t enable_value; + + + enable_value = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; + + return enable_value; +} + + + + + + + +void setMode(uint8_t mode, uint8_t enable) +{ + uint8_t reg_val; + + + reg_val = getMode(); + + + + enable = enable & 0x01; + if( mode >= 0 && mode <= 6 ) { + if (enable) { + reg_val |= (1 << mode); + } else { + reg_val &= ~(1 << mode); + } + } else if( mode == ALL ) { + if (enable) { + reg_val = 0x7F; + } else { + reg_val = 0x00; + } + } + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, reg_val) ; +} + + + + + + +void enableLightSensor(void) +{ + + setAmbientLightGain(DEFAULT_AGAIN); + setAmbientLightIntEnable(0); + enablePower() ; + setMode(AMBIENT_LIGHT, 1) ; +} + + + + + +void disableLightSensor(void) +{ + setAmbientLightIntEnable(0) ; + setMode(AMBIENT_LIGHT, 0) ; +} + + + + + + +void enableProximitySensor(void) +{ + + setProximityGain(DEFAULT_PGAIN); + setLEDDrive(DEFAULT_LDRIVE) ; + setProximityIntEnable(0) ; + enablePower(); + setMode(PROXIMITY, 1) ; +} + + + + + +void disableProximitySensor(void) +{ + setProximityIntEnable(0) ; + setMode(PROXIMITY, 0) ; +} + + + + + + +void enableGestureSensor(void) +{ + + + + + + + + resetGestureParameters(); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, 0xFF) ; + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_GESTURE_PPULSE) ; + setLEDBoost(LED_BOOST_100); + setGestureIntEnable(0) ; + setGestureMode(1); + enablePower() ; + setMode(WAIT, 1) ; + setMode(PROXIMITY, 1) ; + setMode(GESTURE, 1); +} + + + + + +void disableGestureSensor(void) +{ + resetGestureParameters(); + setGestureIntEnable(0) ; + setGestureMode(0) ; + setMode(GESTURE, 0) ; +} + + + + + + +bool isGestureAvailable(void) +{ + uint8_t val; + + + val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS) ; + + + val &= APDS9960_GVALID; + + + if( val == 1) { + return true; + } else { + return false; + } +} + + + + + + +int16_t readGesture(void) +{ + uint8_t fifo_level = 0; + uint8_t bytes_read = 0; + uint8_t fifo_data[128]; + uint8_t gstatus; + uint16_t motion; + uint16_t i; + uint8_t gesture_loop_counter = 0; + + + if( !isGestureAvailable() || !(getMode() & 0b01000001) ) { + return DIR_NONE; + } + + + while(1) { + if (gesture_loop_counter == APDS9960_MAX_GESTURE_CYCLES){ + disableGestureSensor(); + APDS9960_overload = true; + AddLog_P(LOG_LEVEL_DEBUG, PSTR("Sensor overload")); + } + gesture_loop_counter += 1; + + delay(FIFO_PAUSE_TIME); + + + gstatus = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS); + + + if( (gstatus & APDS9960_GVALID) == APDS9960_GVALID ) { + + + fifo_level = I2cRead8(APDS9960_I2C_ADDR,APDS9960_GFLVL) ; + + + if( fifo_level > 0) { + bytes_read = wireReadDataBlock( APDS9960_GFIFO_U, + (uint8_t*)fifo_data, + (fifo_level * 4) ); + if( bytes_read == -1 ) { + return ERROR; + } + + + if( bytes_read >= 4 ) { + for( i = 0; i < bytes_read; i += 4 ) { + gesture_data_.u_data[gesture_data_.index] = \ + fifo_data[i + 0]; + gesture_data_.d_data[gesture_data_.index] = \ + fifo_data[i + 1]; + gesture_data_.l_data[gesture_data_.index] = \ + fifo_data[i + 2]; + gesture_data_.r_data[gesture_data_.index] = \ + fifo_data[i + 3]; + gesture_data_.index++; + gesture_data_.total_gestures++; + } + + if( processGestureData() ) { + if( decodeGesture() ) { + + } + } + + gesture_data_.index = 0; + gesture_data_.total_gestures = 0; + } + } + } else { + + + delay(FIFO_PAUSE_TIME); + decodeGesture(); + motion = gesture_motion_; + resetGestureParameters(); + return motion; + } + } +} + + + + + +void enablePower(void) +{ + setMode(POWER, 1) ; +} + + + + + +void disablePower(void) +{ + setMode(POWER, 0) ; +} +# 1600 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" +void readAllColorAndProximityData(void) +{ + if (I2cReadBuffer(APDS9960_I2C_ADDR, APDS9960_CDATAL, (uint8_t *) &color_data, (uint16_t)9)) + { + + + } +} +# 1616 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" +void resetGestureParameters(void) +{ + gesture_data_.index = 0; + gesture_data_.total_gestures = 0; + + gesture_ud_delta_ = 0; + gesture_lr_delta_ = 0; + + gesture_ud_count_ = 0; + gesture_lr_count_ = 0; + + gesture_state_ = 0; + gesture_motion_ = DIR_NONE; +} + + + + + + +bool processGestureData(void) +{ + uint8_t u_first = 0; + uint8_t d_first = 0; + uint8_t l_first = 0; + uint8_t r_first = 0; + uint8_t u_last = 0; + uint8_t d_last = 0; + uint8_t l_last = 0; + uint8_t r_last = 0; + uint16_t ud_ratio_first; + uint16_t lr_ratio_first; + uint16_t ud_ratio_last; + uint16_t lr_ratio_last; + uint16_t ud_delta; + uint16_t lr_delta; + uint16_t i; + + + if( gesture_data_.total_gestures <= 4 ) { + return false; + } + + + if( (gesture_data_.total_gestures <= 32) && \ + (gesture_data_.total_gestures > 0) ) { + + + for( i = 0; i < gesture_data_.total_gestures; i++ ) { + if( (gesture_data_.u_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data_.d_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data_.l_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data_.r_data[i] > GESTURE_THRESHOLD_OUT) ) { + + u_first = gesture_data_.u_data[i]; + d_first = gesture_data_.d_data[i]; + l_first = gesture_data_.l_data[i]; + r_first = gesture_data_.r_data[i]; + break; + } + } + + + if( (u_first == 0) || (d_first == 0) || \ + (l_first == 0) || (r_first == 0) ) { + + return false; + } + + for( i = gesture_data_.total_gestures - 1; i >= 0; i-- ) { + + if( (gesture_data_.u_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data_.d_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data_.l_data[i] > GESTURE_THRESHOLD_OUT) && + (gesture_data_.r_data[i] > GESTURE_THRESHOLD_OUT) ) { + + u_last = gesture_data_.u_data[i]; + d_last = gesture_data_.d_data[i]; + l_last = gesture_data_.l_data[i]; + r_last = gesture_data_.r_data[i]; + break; + } + } + } + + + ud_ratio_first = ((u_first - d_first) * 100) / (u_first + d_first); + lr_ratio_first = ((l_first - r_first) * 100) / (l_first + r_first); + ud_ratio_last = ((u_last - d_last) * 100) / (u_last + d_last); + lr_ratio_last = ((l_last - r_last) * 100) / (l_last + r_last); + + + ud_delta = ud_ratio_last - ud_ratio_first; + lr_delta = lr_ratio_last - lr_ratio_first; + + + gesture_ud_delta_ += ud_delta; + gesture_lr_delta_ += lr_delta; + + + if( gesture_ud_delta_ >= GESTURE_SENSITIVITY_1 ) { + gesture_ud_count_ = 1; + } else if( gesture_ud_delta_ <= -GESTURE_SENSITIVITY_1 ) { + gesture_ud_count_ = -1; + } else { + gesture_ud_count_ = 0; + } + + + if( gesture_lr_delta_ >= GESTURE_SENSITIVITY_1 ) { + gesture_lr_count_ = 1; + } else if( gesture_lr_delta_ <= -GESTURE_SENSITIVITY_1 ) { + gesture_lr_count_ = -1; + } else { + gesture_lr_count_ = 0; + } + return false; +} + + + + + + +bool decodeGesture(void) +{ + + + if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == 0) ) { + gesture_motion_ = DIR_UP; + } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == 0) ) { + gesture_motion_ = DIR_DOWN; + } else if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == 1) ) { + gesture_motion_ = DIR_RIGHT; + } else if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == -1) ) { + gesture_motion_ = DIR_LEFT; + } else if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == 1) ) { + if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { + gesture_motion_ = DIR_UP; + } else { + gesture_motion_ = DIR_RIGHT; + } + } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == -1) ) { + if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { + gesture_motion_ = DIR_DOWN; + } else { + gesture_motion_ = DIR_LEFT; + } + } else if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == -1) ) { + if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { + gesture_motion_ = DIR_UP; + } else { + gesture_motion_ = DIR_LEFT; + } + } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == 1) ) { + if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { + gesture_motion_ = DIR_DOWN; + } else { + gesture_motion_ = DIR_RIGHT; + } + } else { + return false; + } + + return true; +} + +void handleGesture(void) { + if (isGestureAvailable() ) { + switch (readGesture()) { + case DIR_UP: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("UP")); + snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Up")); + break; + case DIR_DOWN: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("DOWN")); + snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Down")); + break; + case DIR_LEFT: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("LEFT")); + snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Left")); + break; + case DIR_RIGHT: + AddLog_P(LOG_LEVEL_DEBUG, PSTR("RIGHT")); + snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Right")); + break; + default: + if(APDS9960_overload) + { + AddLog_P(LOG_LEVEL_DEBUG, PSTR("LONG")); + snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Long")); + } + else{ + AddLog_P(LOG_LEVEL_DEBUG, PSTR("NONE")); + snprintf_P(currentGesture, sizeof(currentGesture), PSTR("None")); + } + } + MqttPublishSensor(); + } +} + +void APDS9960_adjustATime(void) +{ + + I2cValidRead16LE(&color_data.a, APDS9960_I2C_ADDR, APDS9960_CDATAL); + + + if (color_data.a < (uint16_t)20){ + APDS9960_aTime = 0x40; + } + else if (color_data.a < (uint16_t)40){ + APDS9960_aTime = 0x80; + } + else if (color_data.a < (uint16_t)50){ + APDS9960_aTime = DEFAULT_ATIME; + } + else if (color_data.a < (uint16_t)70){ + APDS9960_aTime = 0xc0; + } + if (color_data.a < 200){ + APDS9960_aTime = 0xe9; + } + + + + else{ + APDS9960_aTime = 0xff; + } + + + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, APDS9960_aTime); + enablePower(); + enableLightSensor(); + delay(20); +} + + +void APDS9960_loop(void) +{ + if (recovery_loop_counter > 0){ + recovery_loop_counter -= 1; + } + if (recovery_loop_counter == 1 && APDS9960_overload){ + enableGestureSensor(); + APDS9960_overload = false; + Response_P(PSTR("{\"Gesture\":\"On\"}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); + gesture_mode = 1; + } + + if (gesture_mode) { + if (recovery_loop_counter == 0){ + handleGesture(); + + if (APDS9960_overload) + { + disableGestureSensor(); + recovery_loop_counter = APDS9960_LONG_RECOVERY; + Response_P(PSTR("{\"Gesture\":\"Off\"}")); + MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); + gesture_mode = 0; + } + } + } +} + +void APDS9960_detect(void) +{ + if (APDS9960type || I2cActive(APDS9960_I2C_ADDR)) { return; } + + APDS9960type = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ID); + if (APDS9960type == APDS9960_CHIPID_1 || APDS9960type == APDS9960_CHIPID_2) { + if (APDS9960_init()) { + I2cSetActiveFound(APDS9960_I2C_ADDR, APDS9960stype); + + enableProximitySensor(); + enableGestureSensor(); + } else { + APDS9960type = 0; + } + } else { + APDS9960type = 0; + } + currentGesture[0] = '\0'; +} + + + + + +void APDS9960_show(bool json) +{ + if (!APDS9960type) { return; } + + if (!gesture_mode && !APDS9960_overload) { + char red_chr[10]; + char green_chr[10]; + char blue_chr[10]; + char ambient_chr[10]; + char cct_chr[10]; + char prox_chr[10]; + + readAllColorAndProximityData(); + + sprintf (ambient_chr, "%u", color_data.a/4); + sprintf (red_chr, "%u", color_data.r); + sprintf (green_chr, "%u", color_data.g); + sprintf (blue_chr, "%u", color_data.b ); + sprintf (prox_chr, "%u", color_data.p ); + + + + + + calculateColorTemperature(); + sprintf (cct_chr, "%u", color_data.cct); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"Red\":%s,\"Green\":%s,\"Blue\":%s,\"Ambient\":%s,\"CCT\":%s,\"Proximity\":%s}"), + APDS9960stype, red_chr, green_chr, blue_chr, ambient_chr, cct_chr, prox_chr); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_APDS_9960_SNS, red_chr, green_chr, blue_chr, ambient_chr, cct_chr, prox_chr ); +#endif + } + } + else { + if (json && (currentGesture[0] != '\0' )) { + ResponseAppend_P(PSTR(",\"%s\":{\"%s\":1}"), APDS9960stype, currentGesture); + currentGesture[0] = '\0'; + } + } +} +# 1961 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" +bool APDS9960CommandSensor(void) +{ + bool serviced = true; + + switch (XdrvMailbox.payload) { + case 0: + disableGestureSensor(); + gesture_mode = 0; + enableLightSensor(); + APDS9960_overload = false; + break; + case 1: + if (APDS9960type) { + setGestureGain(DEFAULT_GGAIN); + setProximityGain(DEFAULT_PGAIN); + disableLightSensor(); + enableGestureSensor(); + gesture_mode = 1; + } + break; + case 2: + if (APDS9960type) { + setGestureGain(GGAIN_2X); + setProximityGain(PGAIN_2X); + disableLightSensor(); + enableGestureSensor(); + gesture_mode = 1; + } + break; + default: + int temp_aTime = (uint8_t)XdrvMailbox.payload; + if (temp_aTime > 2 && temp_aTime < 256){ + disablePower(); + I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, temp_aTime); + enablePower(); + enableLightSensor(); + } + break; + } + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_27, GetStateText(gesture_mode)); + + return serviced; +} + + + + + +bool Xsns27(uint8_t function) +{ + if (!I2cEnabled(XI2C_21)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + APDS9960_detect(); + } + else if (APDS9960type) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + APDS9960_loop(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_27 == XdrvMailbox.index) { + result = APDS9960CommandSensor(); + } + break; + case FUNC_JSON_APPEND: + APDS9960_show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + APDS9960_show(0); + break; +#endif + } + } + return result; +} +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_28_tm1638.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_28_tm1638.ino" +#ifdef USE_TM1638 + + + + + + +#define XSNS_28 28 + +#define TM1638_COLOR_NONE 0 +#define TM1638_COLOR_RED 1 +#define TM1638_COLOR_GREEN 2 + +#define TM1638_CLOCK_DELAY 1 + +uint8_t tm1638_type = 1; +uint8_t tm1638_clock_pin = 0; +uint8_t tm1638_data_pin = 0; +uint8_t tm1638_strobe_pin = 0; +uint8_t tm1638_displays = 8; +uint8_t tm1638_active_display = 1; +uint8_t tm1638_intensity = 0; +uint8_t tm1638_state = 0; + + + + + + +void Tm16XXSend(uint8_t data) +{ + for (uint32_t i = 0; i < 8; i++) { + digitalWrite(tm1638_data_pin, !!(data & (1 << i))); + digitalWrite(tm1638_clock_pin, LOW); + delayMicroseconds(TM1638_CLOCK_DELAY); + digitalWrite(tm1638_clock_pin, HIGH); + } +} + +void Tm16XXSendCommand(uint8_t cmd) +{ + digitalWrite(tm1638_strobe_pin, LOW); + Tm16XXSend(cmd); + digitalWrite(tm1638_strobe_pin, HIGH); +} + +void TM16XXSendData(uint8_t address, uint8_t data) +{ + Tm16XXSendCommand(0x44); + digitalWrite(tm1638_strobe_pin, LOW); + Tm16XXSend(0xC0 | address); + Tm16XXSend(data); + digitalWrite(tm1638_strobe_pin, HIGH); +} + +uint8_t Tm16XXReceive(void) +{ + uint8_t temp = 0; + + + pinMode(tm1638_data_pin, INPUT); + digitalWrite(tm1638_data_pin, HIGH); + + for (uint32_t i = 0; i < 8; ++i) { + digitalWrite(tm1638_clock_pin, LOW); + delayMicroseconds(TM1638_CLOCK_DELAY); + temp |= digitalRead(tm1638_data_pin) << i; + digitalWrite(tm1638_clock_pin, HIGH); + } + + + pinMode(tm1638_data_pin, OUTPUT); + digitalWrite(tm1638_data_pin, LOW); + + return temp; +} + + + +void Tm16XXClearDisplay(void) +{ + for (uint32_t i = 0; i < tm1638_displays; i++) { + TM16XXSendData(i << 1, 0); + } +} + +void Tm1638SetLED(uint8_t color, uint8_t pos) +{ + TM16XXSendData((pos << 1) + 1, color); +} + +void Tm1638SetLEDs(word leds) +{ + for (uint32_t i = 0; i < tm1638_displays; i++) { + uint8_t color = 0; + + if ((leds & (1 << i)) != 0) { + color |= TM1638_COLOR_RED; + } + + if ((leds & (1 << (i + 8))) != 0) { + color |= TM1638_COLOR_GREEN; + } + + Tm1638SetLED(color, i); + } +} + +uint8_t Tm1638GetButtons(void) +{ + uint8_t keys = 0; + + digitalWrite(tm1638_strobe_pin, LOW); + Tm16XXSend(0x42); + for (uint32_t i = 0; i < 4; i++) { + keys |= Tm16XXReceive() << i; + } + digitalWrite(tm1638_strobe_pin, HIGH); + + return keys; +} + + + +void TmInit(void) +{ + tm1638_type = 0; + if ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99)) { + tm1638_clock_pin = pin[GPIO_TM16CLK]; + tm1638_data_pin = pin[GPIO_TM16DIO]; + tm1638_strobe_pin = pin[GPIO_TM16STB]; + + pinMode(tm1638_data_pin, OUTPUT); + pinMode(tm1638_clock_pin, OUTPUT); + pinMode(tm1638_strobe_pin, OUTPUT); + + digitalWrite(tm1638_strobe_pin, HIGH); + digitalWrite(tm1638_clock_pin, HIGH); + + Tm16XXSendCommand(0x40); + Tm16XXSendCommand(0x80 | (tm1638_active_display ? 8 : 0) | tmin(7, tm1638_intensity)); + + digitalWrite(tm1638_strobe_pin, LOW); + Tm16XXSend(0xC0); + for (uint32_t i = 0; i < 16; i++) { + Tm16XXSend(0x00); + } + digitalWrite(tm1638_strobe_pin, HIGH); + + tm1638_type = 1; + tm1638_state = 1; + } +} + +void TmLoop(void) +{ + if (tm1638_state) { + uint8_t buttons = Tm1638GetButtons(); + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + SwitchSetVirtual(i, (buttons &1) ^1); + uint8_t color = (SwitchGetVirtual(i)) ? TM1638_COLOR_NONE : TM1638_COLOR_RED; + Tm1638SetLED(color, i); + buttons >>= 1; + } + SwitchHandler(1); + } +} +# 201 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_28_tm1638.ino" +bool Xsns28(uint8_t function) +{ + bool result = false; + + if (tm1638_type) { + switch (function) { + case FUNC_INIT: + TmInit(); + break; + case FUNC_EVERY_50_MSECOND: + TmLoop(); + break; +# 223 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_28_tm1638.ino" + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_29_mcp230xx.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_29_mcp230xx.ino" +#ifdef USE_I2C +#ifdef USE_MCP230xx +# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_29_mcp230xx.ino" +#define XSNS_29 29 +#define XI2C_22 22 + + + + + +uint8_t MCP230xx_IODIR = 0x00; +uint8_t MCP230xx_GPINTEN = 0x02; +uint8_t MCP230xx_IOCON = 0x05; +uint8_t MCP230xx_GPPU = 0x06; +uint8_t MCP230xx_INTF = 0x07; +uint8_t MCP230xx_INTCAP = 0x08; +uint8_t MCP230xx_GPIO = 0x09; + +uint8_t mcp230xx_type = 0; +uint8_t mcp230xx_pincount = 0; +uint8_t mcp230xx_int_en = 0; +uint8_t mcp230xx_int_prio_counter = 0; +uint8_t mcp230xx_int_counter_en = 0; +uint8_t mcp230xx_int_retainer_en = 0; +uint8_t mcp230xx_int_sec_counter = 0; + +uint8_t mcp230xx_int_report_defer_counter[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +uint16_t mcp230xx_int_counter[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +uint8_t mcp230xx_int_retainer[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +unsigned long int_millis[16]; + +const char MCP230XX_SENSOR_RESPONSE[] PROGMEM = "{\"Sensor29_D%i\":{\"MODE\":%i,\"PULL_UP\":\"%s\",\"INT_MODE\":\"%s\",\"STATE\":\"%s\"}}"; + +const char MCP230XX_INTCFG_RESPONSE[] PROGMEM = "{\"MCP230xx_INT%s\":{\"D_%i\":%i}}"; + +#ifdef USE_MCP230xx_OUTPUT +const char MCP230XX_CMND_RESPONSE[] PROGMEM = "{\"S29cmnd_D%i\":{\"COMMAND\":\"%s\",\"STATE\":\"%s\"}}"; +#endif + +void MCP230xx_CheckForIntCounter(void) { + uint8_t en = 0; + for (uint32_t ca=0;ca<16;ca++) { + if (Settings.mcp230xx_config[ca].int_count_en) { + en=1; + } + } + if (!Settings.mcp230xx_int_timer) en=0; + mcp230xx_int_counter_en=en; + if (!mcp230xx_int_counter_en) { + for (uint32_t ca=0;ca<16;ca++) { + mcp230xx_int_counter[ca] = 0; + } + } +} + +void MCP230xx_CheckForIntRetainer(void) { + uint8_t en = 0; + for (uint32_t ca=0;ca<16;ca++) { + if (Settings.mcp230xx_config[ca].int_retain_flag) { + en=1; + } + } + mcp230xx_int_retainer_en=en; + if (!mcp230xx_int_retainer_en) { + for (uint32_t ca=0;ca<16;ca++) { + mcp230xx_int_retainer[ca] = 0; + } + } +} + +const char* ConvertNumTxt(uint8_t statu, uint8_t pinmod=0) { +#ifdef USE_MCP230xx_OUTPUT +if ((6 == pinmod) && (statu < 2)) { statu = abs(statu-1); } +#endif + switch (statu) { + case 0: + return "OFF"; + break; + case 1: + return "ON"; + break; +#ifdef USE_MCP230xx_OUTPUT + case 2: + return "TOGGLE"; + break; +#endif + } + return ""; +} + +const char* IntModeTxt(uint8_t intmo) { + switch (intmo) { + case 0: + return "ALL"; + break; + case 1: + return "EVENT"; + break; + case 2: + return "TELE"; + break; + case 3: + return "DISABLED"; + break; + } + return ""; +} + +uint8_t MCP230xx_readGPIO(uint8_t port) { + return I2cRead8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port); +} + +void MCP230xx_ApplySettings(void) +{ + uint8_t int_en = 0; + for (uint32_t mcp230xx_port = 0; mcp230xx_port < mcp230xx_type; mcp230xx_port++) { + uint8_t reg_gppu = 0; + uint8_t reg_gpinten = 0; + uint8_t reg_iodir = 0xFF; +#ifdef USE_MCP230xx_OUTPUT + uint8_t reg_portpins = 0x00; +#endif + for (uint32_t idx = 0; idx < 8; idx++) { + switch (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pinmode) { + case 0 ... 1: + reg_iodir |= (1 << idx); + break; + case 2 ... 4: + reg_iodir |= (1 << idx); + reg_gpinten |= (1 << idx); + int_en = 1; + break; +#ifdef USE_MCP230xx_OUTPUT + case 5 ... 6: + reg_iodir &= ~(1 << idx); + if (Settings.flag.save_state) { + reg_portpins |= (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].saved_state << idx); + } else { + if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) { + reg_portpins |= (1 << idx); + } + } + break; +#endif + default: + break; + } +#ifdef USE_MCP230xx_OUTPUT + if ((Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) && (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pinmode < 5)) { + reg_gppu |= (1 << idx); + } +#else + if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) { + reg_gppu |= (1 << idx); + } +#endif + } + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPPU+mcp230xx_port, reg_gppu); + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten); + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IODIR+mcp230xx_port, reg_iodir); +#ifdef USE_MCP230xx_OUTPUT + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins); +#endif + } + for (uint32_t idx=0;idx 0) { + if (I2cValidRead8(&mcp230xx_intcap, USE_MCP230xx_ADDR, MCP230xx_INTCAP+mcp230xx_port)) { + for (uint32_t intp = 0; intp < 8; intp++) { + if ((intf >> intp) & 0x01) { + report_int = 0; + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].pinmode > 1) { + switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].pinmode) { + case 2: + report_int = 1; + break; + case 3: + if (((mcp230xx_intcap >> intp) & 0x01) == 0) report_int = 1; + break; + case 4: + if (((mcp230xx_intcap >> intp) & 0x01) == 1) report_int = 1; + break; + default: + break; + } + + if ((mcp230xx_int_counter_en) && (report_int)) { + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_count_en) { + mcp230xx_int_counter[intp+(mcp230xx_port*8)]++; + } + } + + if (report_int) { + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_defer) { + mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)]++; + if (mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)] >= Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_defer) { + mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)]=0; + } else { + report_int = 0; + } + } + } + + if (report_int) { + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_retain_flag) { + mcp230xx_int_retainer[intp+(mcp230xx_port*8)] = 1; + report_int = 0; + } + } + if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_count_en) { + report_int = 0; + } + if (report_int) { + bool int_tele = false; + bool int_event = false; + unsigned long millis_now = millis(); + unsigned long millis_since_last_int = millis_now - int_millis[intp+(mcp230xx_port*8)]; + int_millis[intp+(mcp230xx_port*8)]=millis_now; + switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_mode) { + case 0: + int_tele=true; + int_event=true; + break; + case 1: + int_event=true; + break; + case 2: + int_tele=true; + break; + } + if (int_tele) { + ResponseTime_P(PSTR(",\"MCP230XX_INT\":{\"D%i\":%i,\"MS\":%lu}}"), + intp+(mcp230xx_port*8), ((mcp230xx_intcap >> intp) & 0x01),millis_since_last_int); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("MCP230XX_INT")); + } + if (int_event) { + char command[19]; + sprintf(command,"event MCPINT_D%i=%i",intp+(mcp230xx_port*8),((mcp230xx_intcap >> intp) & 0x01)); + ExecuteCommand(command, SRC_RULE); + } + } + } + } + } + } + } + } + } +} + +void MCP230xx_Show(bool json) +{ + if (json) { + uint8_t gpio = MCP230xx_readGPIO(0); + ResponseAppend_P(PSTR(",\"MCP230XX\":{\"D0\":%i,\"D1\":%i,\"D2\":%i,\"D3\":%i,\"D4\":%i,\"D5\":%i,\"D6\":%i,\"D7\":%i"), + (gpio>>0)&1,(gpio>>1)&1,(gpio>>2)&1,(gpio>>3)&1,(gpio>>4)&1,(gpio>>5)&1,(gpio>>6)&1,(gpio>>7)&1); + if (2 == mcp230xx_type) { + gpio = MCP230xx_readGPIO(1); + ResponseAppend_P(PSTR(",\"D8\":%i,\"D9\":%i,\"D10\":%i,\"D11\":%i,\"D12\":%i,\"D13\":%i,\"D14\":%i,\"D15\":%i"), + (gpio>>0)&1,(gpio>>1)&1,(gpio>>2)&1,(gpio>>3)&1,(gpio>>4)&1,(gpio>>5)&1,(gpio>>6)&1,(gpio>>7)&1); + } + ResponseJsonEnd(); + } +} + +#ifdef USE_MCP230xx_OUTPUT + +void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate) { + uint8_t portpins; + uint8_t port = 0; + uint8_t pinmo = Settings.mcp230xx_config[pin].pinmode; + uint8_t interlock = Settings.flag.interlock; + int pinadd = (pin % 2)+1-(3*(pin % 2)); + char cmnd[7], stt[4]; + if (pin > 7) { port = 1; } + portpins = MCP230xx_readGPIO(port); + if (interlock && (pinmo == Settings.mcp230xx_config[pin+pinadd].pinmode)) { + if (pinstate < 2) { + if (6 == pinmo) { + if (pinstate) portpins |= (1 << (pin-(port*8))); else portpins |= (1 << (pin+pinadd-(port*8))),portpins &= ~(1 << (pin-(port*8))); + } else { + if (pinstate) portpins &= ~(1 << (pin+pinadd-(port*8))),portpins |= (1 << (pin-(port*8))); else portpins &= ~(1 << (pin-(port*8))); + } + } else { + if (6 == pinmo) { + portpins |= (1 << (pin+pinadd-(port*8))),portpins ^= (1 << (pin-(port*8))); + } else { + portpins &= ~(1 << (pin+pinadd-(port*8))),portpins ^= (1 << (pin-(port*8))); + } + } + } else { + if (pinstate < 2) { + if (pinstate) portpins |= (1 << (pin-(port*8))); else portpins &= ~(1 << (pin-(port*8))); + } else { + portpins ^= (1 << (pin-(port*8))); + } + } + I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port, portpins); + if (Settings.flag.save_state) { + Settings.mcp230xx_config[pin].saved_state=portpins>>(pin-(port*8))&1; + Settings.mcp230xx_config[pin+pinadd].saved_state=portpins>>(pin+pinadd-(port*8))&1; + } + sprintf(cmnd,ConvertNumTxt(pinstate, pinmo)); + sprintf(stt,ConvertNumTxt((portpins >> (pin-(port*8))&1), pinmo)); + if (interlock && (pinmo == Settings.mcp230xx_config[pin+pinadd].pinmode)) { + char stt1[4]; + sprintf(stt1,ConvertNumTxt((portpins >> (pin+pinadd-(port*8))&1), pinmo)); + Response_P(PSTR("{\"S29cmnd_D%i\":{\"COMMAND\":\"%s\",\"STATE\":\"%s\"},\"S29cmnd_D%i\":{\"STATE\":\"%s\"}}"),pin, cmnd, stt, pin+pinadd, stt1); + } else { + Response_P(MCP230XX_CMND_RESPONSE, pin, cmnd, stt); + } +} + +#endif + +void MCP230xx_Reset(uint8_t pinmode) { + uint8_t pullup = 0; + if ((pinmode > 1) && (pinmode < 5)) { pullup=1; } + for (uint32_t pinx=0;pinx<16;pinx++) { + Settings.mcp230xx_config[pinx].pinmode=pinmode; + Settings.mcp230xx_config[pinx].pullup=pullup; + Settings.mcp230xx_config[pinx].saved_state=0; + if ((pinmode > 1) && (pinmode < 5)) { + Settings.mcp230xx_config[pinx].int_report_mode=0; + } else { + Settings.mcp230xx_config[pinx].int_report_mode=3; + } + Settings.mcp230xx_config[pinx].int_report_defer=0; + Settings.mcp230xx_config[pinx].int_count_en=0; + Settings.mcp230xx_config[pinx].int_retain_flag=0; + Settings.mcp230xx_config[pinx].spare13=0; + Settings.mcp230xx_config[pinx].spare14=0; + Settings.mcp230xx_config[pinx].spare15=0; + } + Settings.mcp230xx_int_prio = 0; + Settings.mcp230xx_int_timer = 0; + MCP230xx_ApplySettings(); + char pulluptxt[7]; + char intmodetxt[9]; + sprintf(pulluptxt,ConvertNumTxt(pullup)); + uint8_t intmode = 3; + if ((pinmode > 1) && (pinmode < 5)) { intmode = 0; } + sprintf(intmodetxt,IntModeTxt(intmode)); + Response_P(MCP230XX_SENSOR_RESPONSE,99,pinmode,pulluptxt,intmodetxt,""); +} + +bool MCP230xx_Command(void) +{ + bool serviced = true; + bool validpin = false; + uint8_t paramcount = 0; + if (XdrvMailbox.data_len > 0) { + paramcount=1; + } else { + serviced = false; + return serviced; + } + char sub_string[XdrvMailbox.data_len]; + for (uint32_t ca=0;ca 1) { + uint8_t intpri = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if ((intpri >= 0) && (intpri <= 20)) { + Settings.mcp230xx_int_prio = intpri; + Response_P(MCP230XX_INTCFG_RESPONSE,"PRI",99,Settings.mcp230xx_int_prio); + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"PRI",99,Settings.mcp230xx_int_prio); + return serviced; + } + } + + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTTIMER")) { + if (paramcount > 1) { + uint8_t inttim = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if ((inttim >= 0) && (inttim <= 3600)) { + Settings.mcp230xx_int_timer = inttim; + MCP230xx_CheckForIntCounter(); + Response_P(MCP230XX_INTCFG_RESPONSE,"TIMER",99,Settings.mcp230xx_int_timer); + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"TIMER",99,Settings.mcp230xx_int_timer); + return serviced; + } + } + + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTDEF")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (pin < mcp230xx_pincount) { + if (pin == 0) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; + } else { + validpin = true; + } + } + if (validpin) { + if (paramcount > 2) { + uint8_t intdef = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((intdef >= 0) && (intdef <= 15)) { + Settings.mcp230xx_config[pin].int_report_defer=intdef; + if (Settings.mcp230xx_config[pin].int_count_en) { + Settings.mcp230xx_config[pin].int_count_en=0; + MCP230xx_CheckForIntCounter(); + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTCNT for pin D%i"),pin); + } + Response_P(MCP230XX_INTCFG_RESPONSE,"DEF",pin,Settings.mcp230xx_config[pin].int_report_defer); + return serviced; + } else { + serviced=false; + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"DEF",pin,Settings.mcp230xx_config[pin].int_report_defer); + return serviced; + } + } + serviced = false; + return serviced; + } else { + serviced = false; + return serviced; + } + } + + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTCNT")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (pin < mcp230xx_pincount) { + if (pin == 0) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; + } else { + validpin = true; + } + } + if (validpin) { + if (paramcount > 2) { + uint8_t intcnt = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((intcnt >= 0) && (intcnt <= 1)) { + Settings.mcp230xx_config[pin].int_count_en=intcnt; + if (Settings.mcp230xx_config[pin].int_report_defer) { + Settings.mcp230xx_config[pin].int_report_defer=0; + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTDEF for pin D%i"),pin); + } + if (Settings.mcp230xx_config[pin].int_report_mode < 3) { + Settings.mcp230xx_config[pin].int_report_mode=3; + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled immediate interrupt/telemetry reporting for pin D%i"),pin); + } + if ((Settings.mcp230xx_config[pin].int_count_en) && (!Settings.mcp230xx_int_timer)) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - INTCNT enabled for pin D%i but global INTTIMER is disabled!"),pin); + } + MCP230xx_CheckForIntCounter(); + Response_P(MCP230XX_INTCFG_RESPONSE,"CNT",pin,Settings.mcp230xx_config[pin].int_count_en); + return serviced; + } else { + serviced=false; + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"CNT",pin,Settings.mcp230xx_config[pin].int_count_en); + return serviced; + } + } + serviced = false; + return serviced; + } else { + serviced = false; + return serviced; + } + } + + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTRETAIN")) { + if (paramcount > 1) { + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + if (pin < mcp230xx_pincount) { + if (pin == 0) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; + } else { + validpin = true; + } + } + if (validpin) { + if (paramcount > 2) { + uint8_t int_retain = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + if ((int_retain >= 0) && (int_retain <= 1)) { + Settings.mcp230xx_config[pin].int_retain_flag=int_retain; + Response_P(MCP230XX_INTCFG_RESPONSE,"INT_RETAIN",pin,Settings.mcp230xx_config[pin].int_retain_flag); + MCP230xx_CheckForIntRetainer(); + return serviced; + } else { + serviced=false; + return serviced; + } + } else { + Response_P(MCP230XX_INTCFG_RESPONSE,"INT_RETAIN",pin,Settings.mcp230xx_config[pin].int_retain_flag); + return serviced; + } + } + serviced = false; + return serviced; + } else { + serviced = false; + return serviced; + } + } + + uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); + + if (pin < mcp230xx_pincount) { + if (0 == pin) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1), "0")) validpin=true; + } else { + validpin=true; + } + } + if (validpin && (paramcount > 1)) { + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "?")) { + uint8_t port = 0; + if (pin > 7) { port = 1; } + uint8_t portdata = MCP230xx_readGPIO(port); + char pulluptxtr[7],pinstatustxtr[7]; + char intmodetxt[9]; + sprintf(intmodetxt,IntModeTxt(Settings.mcp230xx_config[pin].int_report_mode)); + sprintf(pulluptxtr,ConvertNumTxt(Settings.mcp230xx_config[pin].pullup)); +#ifdef USE_MCP230xx_OUTPUT + uint8_t pinmod = Settings.mcp230xx_config[pin].pinmode; + sprintf(pinstatustxtr,ConvertNumTxt(portdata>>(pin-(port*8))&1,pinmod)); + Response_P(MCP230XX_SENSOR_RESPONSE,pin,pinmod,pulluptxtr,intmodetxt,pinstatustxtr); +#else + sprintf(pinstatustxtr,ConvertNumTxt(portdata>>(pin-(port*8))&1)); + Response_P(MCP230XX_SENSOR_RESPONSE,pin,Settings.mcp230xx_config[pin].pinmode,pulluptxtr,intmodetxt,pinstatustxtr); +#endif + return serviced; + } +#ifdef USE_MCP230xx_OUTPUT + if (Settings.mcp230xx_config[pin].pinmode >= 5) { + uint8_t pincmd = Settings.mcp230xx_config[pin].pinmode - 5; + if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "ON")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "1"))) { + MCP230xx_SetOutPin(pin,abs(pincmd-1)); + return serviced; + } + if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "OFF")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0"))) { + MCP230xx_SetOutPin(pin,pincmd); + return serviced; + } + if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "T")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "2"))) { + MCP230xx_SetOutPin(pin,2); + return serviced; + } + } +#endif + uint8_t pinmode = 0; + uint8_t pullup = 0; + uint8_t intmode = 0; + if (paramcount > 1) { + pinmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); + } + if (paramcount > 2) { + pullup = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); + } + if (paramcount > 3) { + intmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); + } +#ifdef USE_MCP230xx_OUTPUT + if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 7) && (pullup < 2) && (paramcount > 2)) { +#else + if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 5) && (pullup < 2) && (paramcount > 2)) { +#endif + Settings.mcp230xx_config[pin].pinmode=pinmode; + Settings.mcp230xx_config[pin].pullup=pullup; + if ((pinmode > 1) && (pinmode < 5)) { + if ((intmode >= 0) && (intmode <= 3)) { + Settings.mcp230xx_config[pin].int_report_mode=intmode; + } + } else { + Settings.mcp230xx_config[pin].int_report_mode=3; + } + MCP230xx_ApplySettings(); + uint8_t port = 0; + if (pin > 7) { port = 1; } + uint8_t portdata = MCP230xx_readGPIO(port); + char pulluptxtc[7], pinstatustxtc[7]; + char intmodetxt[9]; + sprintf(pulluptxtc,ConvertNumTxt(pullup)); + sprintf(intmodetxt,IntModeTxt(Settings.mcp230xx_config[pin].int_report_mode)); +#ifdef USE_MCP230xx_OUTPUT + sprintf(pinstatustxtc,ConvertNumTxt(portdata>>(pin-(port*8))&1,Settings.mcp230xx_config[pin].pinmode)); +#else + sprintf(pinstatustxtc,ConvertNumTxt(portdata>>(pin-(port*8))&1)); +#endif + Response_P(MCP230XX_SENSOR_RESPONSE,pin,pinmode,pulluptxtc,intmodetxt,pinstatustxtc); + return serviced; + } + } else { + serviced=false; + return serviced; + } + return serviced; +} + +#ifdef USE_MCP230xx_DISPLAYOUTPUT + +const char HTTP_SNS_MCP230xx_OUTPUT[] PROGMEM = "{s}MCP230XX D%d{m}%s{e}"; + +void MCP230xx_UpdateWebData(void) +{ + uint8_t gpio1 = MCP230xx_readGPIO(0); + uint8_t gpio2 = 0; + if (2 == mcp230xx_type) { + gpio2 = MCP230xx_readGPIO(1); + } + uint16_t gpio = (gpio2 << 8) + gpio1; + for (uint32_t pin = 0; pin < mcp230xx_pincount; pin++) { + if (Settings.mcp230xx_config[pin].pinmode >= 5) { + char stt[7]; + sprintf(stt,ConvertNumTxt((gpio>>pin)&1,Settings.mcp230xx_config[pin].pinmode)); + WSContentSend_PD(HTTP_SNS_MCP230xx_OUTPUT, pin, stt); + } + } +} + +#endif + +#ifdef USE_MCP230xx_OUTPUT + +void MCP230xx_OutputTelemetry(void) +{ + uint8_t outputcount = 0; + uint16_t gpiototal = 0; + uint8_t gpioa = 0; + uint8_t gpiob = 0; + gpioa=MCP230xx_readGPIO(0); + if (2 == mcp230xx_type) { gpiob=MCP230xx_readGPIO(1); } + gpiototal=((uint16_t)gpiob << 8) | gpioa; + for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { + if (Settings.mcp230xx_config[pinx].pinmode >= 5) outputcount++; + } + if (outputcount) { + char stt[7]; + ResponseTime_P(PSTR(",\"MCP230_OUT\":{")); + for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { + if (Settings.mcp230xx_config[pinx].pinmode >= 5) { + sprintf(stt,ConvertNumTxt(((gpiototal>>pinx)&1),Settings.mcp230xx_config[pinx].pinmode)); + ResponseAppend_P(PSTR("\"OUT_D%i\":\"%s\","),pinx,stt); + } + } + ResponseAppend_P(PSTR("\"END\":1}}")); + MqttPublishTeleSensor(); + } +} + +#endif + +void MCP230xx_Interrupt_Counter_Report(void) { + ResponseTime_P(PSTR(",\"MCP230_INTTIMER\":{")); + for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { + if (Settings.mcp230xx_config[pinx].int_count_en) { + ResponseAppend_P(PSTR("\"INTCNT_D%i\":%i,"),pinx,mcp230xx_int_counter[pinx]); + mcp230xx_int_counter[pinx]=0; + } + } + ResponseAppend_P(PSTR("\"END\":1}}")); + MqttPublishTeleSensor(); + mcp230xx_int_sec_counter = 0; +} + +void MCP230xx_Interrupt_Retain_Report(void) { + uint16_t retainresult = 0; + ResponseTime_P(PSTR(",\"MCP_INTRETAIN\":{")); + for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { + if (Settings.mcp230xx_config[pinx].int_retain_flag) { + ResponseAppend_P(PSTR("\"D%i\":%i,"),pinx,mcp230xx_int_retainer[pinx]); + retainresult |= (((mcp230xx_int_retainer[pinx])&1) << pinx); + mcp230xx_int_retainer[pinx]=0; + } + } + ResponseAppend_P(PSTR("\"Value\":%u}}"),retainresult); + MqttPublishTeleSensor(); +} + + + + + +bool Xsns29(uint8_t function) +{ + if (!I2cEnabled(XI2C_22)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + MCP230xx_Detect(); + } + else if (mcp230xx_type) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + if (mcp230xx_int_en) { + mcp230xx_int_prio_counter++; + if ((mcp230xx_int_prio_counter) >= (Settings.mcp230xx_int_prio)) { + MCP230xx_CheckForInterrupt(); + mcp230xx_int_prio_counter=0; + } + } + break; + case FUNC_EVERY_SECOND: + if (mcp230xx_int_counter_en) { + mcp230xx_int_sec_counter++; + if (mcp230xx_int_sec_counter >= Settings.mcp230xx_int_timer) { + MCP230xx_Interrupt_Counter_Report(); + } + } + if (tele_period == 0) { + if (mcp230xx_int_retainer_en) { + MCP230xx_Interrupt_Retain_Report(); + } +#ifdef USE_MCP230xx_OUTPUT + MCP230xx_OutputTelemetry(); +#endif + } + break; + case FUNC_JSON_APPEND: + MCP230xx_Show(1); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_29 == XdrvMailbox.index) { + result = MCP230xx_Command(); + } + break; +#ifdef USE_WEBSERVER +#ifdef USE_MCP230xx_OUTPUT +#ifdef USE_MCP230xx_DISPLAYOUTPUT + case FUNC_WEB_SENSOR: + MCP230xx_UpdateWebData(); + break; +#endif +#endif +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_30_mpr121.ino" +# 46 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_30_mpr121.ino" +#ifdef USE_I2C +#ifdef USE_MPR121 + + + + + +#define XSNS_30 30 +#define XI2C_23 23 + + + + + + + +#define MPR121_ELEX_REG 0x00 + + +#define MPR121_MHDR_REG 0x2B + + +#define MPR121_MHDR_VAL 0x01 + + +#define MPR121_NHDR_REG 0x2C + + +#define MPR121_NHDR_VAL 0x01 + + +#define MPR121_NCLR_REG 0x2D + + +#define MPR121_NCLR_VAL 0x0E + + +#define MPR121_MHDF_REG 0x2F + + +#define MPR121_MHDF_VAL 0x01 + + +#define MPR121_NHDF_REG 0x30 + + +#define MPR121_NHDF_VAL 0x05 + + +#define MPR121_NCLF_REG 0x31 + + +#define MPR121_NCLF_VAL 0x01 + + +#define MPR121_MHDPROXR_REG 0x36 + + +#define MPR121_MHDPROXR_VAL 0x3F + + +#define MPR121_NHDPROXR_REG 0x37 + + +#define MPR121_NHDPROXR_VAL 0x5F + + +#define MPR121_NCLPROXR_REG 0x38 + + +#define MPR121_NCLPROXR_VAL 0x04 + + +#define MPR121_FDLPROXR_REG 0x39 + + +#define MPR121_FDLPROXR_VAL 0x00 + + +#define MPR121_MHDPROXF_REG 0x3A + + +#define MPR121_MHDPROXF_VAL 0x01 + + +#define MPR121_NHDPROXF_REG 0x3B + + +#define MPR121_NHDPROXF_VAL 0x01 + + +#define MPR121_NCLPROXF_REG 0x3C + + +#define MPR121_NCLPROXF_VAL 0x1F + + +#define MPR121_FDLPROXF_REG 0x3D + + +#define MPR121_FDLPROXF_VAL 0x04 + + +#define MPR121_E0TTH_REG 0x41 + + +#define MPR121_E0TTH_VAL 12 + + +#define MPR121_E0RTH_REG 0x42 + + +#define MPR121_E0RTH_VAL 6 + + +#define MPR121_CDT_REG 0x5D + + +#define MPR121_CDT_VAL 0x20 + + +#define MPR121_ECR_REG 0x5E + + +#define MPR121_ECR_VAL 0x8F + + + +#define MPR121_SRST_REG 0x80 + + +#define MPR121_SRST_VAL 0x63 + + +#define BITC(sensor,position) ((pS->current[sensor] >> position) & 1) + + +#define BITP(sensor,position) ((pS->previous[sensor] >> position) & 1) +# 195 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_30_mpr121.ino" +typedef struct mpr121 mpr121; +struct mpr121 { + const uint8_t i2c_addr[4] = { 0x5A, 0x5B, 0x5C, 0x5D }; + const char id[4] = { 'A', 'B', 'C', 'D' }; + bool connected[4] = { false, false, false, false }; + bool running[4] = { false, false, false, false }; + uint16_t current[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; + uint16_t previous[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; +}; + +bool mpr21_found = false; +# 217 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_30_mpr121.ino" +void Mpr121Init(struct mpr121 *pS, bool initial) +{ + + for (uint32_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) { + + if (initial && I2cActive(pS->i2c_addr[i])) { continue; } + + + pS->connected[i] = (I2cWrite8(pS->i2c_addr[i], MPR121_SRST_REG, MPR121_SRST_VAL) + && (0x24 == I2cRead8(pS->i2c_addr[i], 0x5D))); + if (pS->connected[i]) { + + + mpr21_found = true; + char device_name[16]; + snprintf_P(device_name, sizeof(device_name), PSTR("MPR121(%c)"), pS->id[i]); + I2cSetActiveFound(pS->i2c_addr[i], device_name); + + + for (uint32_t j = 0; j < 13; j++) { + + + I2cWrite8(pS->i2c_addr[i], MPR121_E0TTH_REG + 2 * j, MPR121_E0TTH_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_E0RTH_REG + 2 * j, MPR121_E0RTH_VAL); + } + + + I2cWrite8(pS->i2c_addr[i], MPR121_MHDR_REG, MPR121_MHDR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NHDR_REG, MPR121_NHDR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NCLR_REG, MPR121_NCLR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_MHDF_REG, MPR121_MHDF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NHDF_REG, MPR121_NHDF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NCLF_REG, MPR121_NCLF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXR_REG, MPR121_MHDPROXR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXR_REG, MPR121_NHDPROXR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXR_REG, MPR121_NCLPROXR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXR_REG, MPR121_FDLPROXR_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXF_REG, MPR121_MHDPROXF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXF_REG, MPR121_NHDPROXF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXF_REG, MPR121_NCLPROXF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXF_REG, MPR121_FDLPROXF_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_CDT_REG, MPR121_CDT_VAL); + + + I2cWrite8(pS->i2c_addr[i], MPR121_ECR_REG, MPR121_ECR_VAL); + + + pS->running[i] = (0x00 != I2cRead8(pS->i2c_addr[i], MPR121_ECR_REG)); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_I2C "MPR121%c: %sRunning"), pS->id[i], (pS->running[i]) ? "" : "NOT"); + + } else { + + + pS->running[i] = false; + } + } + + + if (!(pS->connected[0] || pS->connected[1] || pS->connected[2] + || pS->connected[3])) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C "MPR121: No sensors found")); + } +} +# 326 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_30_mpr121.ino" +void Mpr121Show(struct mpr121 *pS, uint8_t function) +{ + + + for (uint32_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) { + + + if (pS->connected[i]) { + + + if (!I2cValidRead16LE(&pS->current[i], pS->i2c_addr[i], MPR121_ELEX_REG)) { + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Cannot read data!"), pS->id[i]); + Mpr121Init(pS, false); + return; + } + + if (BITC(i, 15)) { + + + I2cWrite8(pS->i2c_addr[i], MPR121_ELEX_REG, 0x00); + AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Excess current detected! Fix circuits if it happens repeatedly! Soft-resetting MPR121 ..."), pS->id[i]); + Mpr121Init(pS, false); + return; + } + } + + if (pS->running[i]) { + + + if (FUNC_JSON_APPEND == function) { + ResponseAppend_P(PSTR(",\"MPR121%c\":{"), pS->id[i]); + } + + for (uint32_t j = 0; j < 13; j++) { + + + if ((FUNC_EVERY_50_MSECOND == function) + && (BITC(i, j) != BITP(i, j))) { + Response_P(PSTR("{\"MPR121%c\":{\"Button%i\":%i}}"), pS->id[i], j, BITC(i, j)); + MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data); + } + +#ifdef USE_WEBSERVER + if (FUNC_WEB_SENSOR == function) { + WSContentSend_PD(PSTR("{s}MPR121%c Button%d{m}%d{e}"), pS->id[i], j, BITC(i, j)); + } +#endif + + + if (FUNC_JSON_APPEND == function) { + ResponseAppend_P(PSTR("%s\"Button%i\":%i"), (j > 0 ? "," : ""), j, BITC(i, j)); + } + } + + + pS->previous[i] = pS->current[i]; + + + if (FUNC_JSON_APPEND == function) { + ResponseJsonEnd(); + } + } + } +} +# 410 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_30_mpr121.ino" +bool Xsns30(uint8_t function) +{ + if (!I2cEnabled(XI2C_23)) { return false; } + + bool result = false; + + + static struct mpr121 mpr121; + + if (FUNC_INIT == function) { + + Mpr121Init(&mpr121, true); + } + else if (mpr21_found) { + + switch (function) { + + + case FUNC_EVERY_50_MSECOND: + Mpr121Show(&mpr121, FUNC_EVERY_50_MSECOND); + break; + + + case FUNC_JSON_APPEND: + Mpr121Show(&mpr121, FUNC_JSON_APPEND); + break; + +#ifdef USE_WEBSERVER + + case FUNC_WEB_SENSOR: + Mpr121Show(&mpr121, FUNC_WEB_SENSOR); + break; +#endif + } + } + + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_31_ccs811.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_31_ccs811.ino" +#ifdef USE_I2C +#ifdef USE_CCS811 +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_31_ccs811.ino" +#define XSNS_31 31 +#define XI2C_24 24 + +#define EVERYNSECONDS 5 + +#include "Adafruit_CCS811.h" + +Adafruit_CCS811 ccs; +uint8_t CCS811_ready = 0; +uint8_t CCS811_type = 0;; +uint16_t eCO2; +uint16_t TVOC; +uint8_t tcnt = 0; +uint8_t ecnt = 0; + + + +void CCS811Detect(void) +{ + if (I2cActive(CCS811_ADDRESS)) { return; } + + if (!ccs.begin(CCS811_ADDRESS)) { + CCS811_type = 1; + I2cSetActiveFound(CCS811_ADDRESS, "CCS811"); + } +} + +void CCS811Update(void) +{ + tcnt++; + if (tcnt >= EVERYNSECONDS) { + tcnt = 0; + CCS811_ready = 0; + if (ccs.available()) { + if (!ccs.readData()){ + TVOC = ccs.getTVOC(); + eCO2 = ccs.geteCO2(); + CCS811_ready = 1; + if (global_update && global_humidity>0 && global_temperature!=9999) { ccs.setEnvironmentalData((uint8_t)global_humidity, global_temperature); } + ecnt = 0; + } + } else { + + ecnt++; + if (ecnt > 6) { + + ccs.begin(CCS811_ADDRESS); + } + } + } +} + +const char HTTP_SNS_CCS811[] PROGMEM = + "{s}CCS811 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" + "{s}CCS811 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; + +void CCS811Show(bool json) +{ + if (CCS811_ready) { + if (json) { + ResponseAppend_P(PSTR(",\"CCS811\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d}"), eCO2,TVOC); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, eCO2); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CCS811, eCO2, TVOC); +#endif + } + } +} + + + + + +bool Xsns31(uint8_t function) +{ + if (!I2cEnabled(XI2C_24)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + CCS811Detect(); + } + else if (CCS811_type) { + switch (function) { + case FUNC_EVERY_SECOND: + CCS811Update(); + break; + case FUNC_JSON_APPEND: + CCS811Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + CCS811Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_32_mpu6050.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_32_mpu6050.ino" +#ifdef USE_I2C +#ifdef USE_MPU6050 +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_32_mpu6050.ino" +#define XSNS_32 32 +#define XI2C_25 25 + +#define D_SENSOR_MPU6050 "MPU6050" + +#define MPU_6050_ADDR_AD0_LOW 0x68 +#define MPU_6050_ADDR_AD0_HIGH 0x69 + +uint8_t MPU_6050_address; +uint8_t MPU_6050_addresses[] = { MPU_6050_ADDR_AD0_LOW, MPU_6050_ADDR_AD0_HIGH }; +uint8_t MPU_6050_found; + +int16_t MPU_6050_ax = 0, MPU_6050_ay = 0, MPU_6050_az = 0; +int16_t MPU_6050_gx = 0, MPU_6050_gy = 0, MPU_6050_gz = 0; +int16_t MPU_6050_temperature = 0; + +#ifdef USE_MPU6050_DMP + #include "MPU6050_6Axis_MotionApps20.h" + #include "I2Cdev.h" + #include + typedef struct MPU6050_DMP{ + uint8_t devStatus; + uint16_t packetSize; + uint16_t fifoCount; + uint8_t fifoBuffer[64]; + Quaternion q; + VectorInt16 aa; + VectorInt16 aaReal; + VectorFloat gravity; + float euler[3]; + float yawPitchRoll[3]; + } MPU6050_DMP; + + MPU6050_DMP MPU6050_dmp; +#else + #include +#endif +MPU6050 mpu6050; + +void MPU_6050PerformReading(void) +{ +#ifdef USE_MPU6050_DMP + mpu6050.resetFIFO(); + MPU6050_dmp.fifoCount = mpu6050.getFIFOCount(); + while (MPU6050_dmp.fifoCount < MPU6050_dmp.packetSize) MPU6050_dmp.fifoCount = mpu6050.getFIFOCount(); + mpu6050.getFIFOBytes(MPU6050_dmp.fifoBuffer, MPU6050_dmp.packetSize); + MPU6050_dmp.fifoCount -= MPU6050_dmp.packetSize; + + mpu6050.dmpGetQuaternion(&MPU6050_dmp.q, MPU6050_dmp.fifoBuffer); + mpu6050.dmpGetEuler(MPU6050_dmp.euler, &MPU6050_dmp.q); + mpu6050.dmpGetAccel(&MPU6050_dmp.aa, MPU6050_dmp.fifoBuffer); + mpu6050.dmpGetGravity(&MPU6050_dmp.gravity, &MPU6050_dmp.q); + mpu6050.dmpGetLinearAccel(&MPU6050_dmp.aaReal, &MPU6050_dmp.aa, &MPU6050_dmp.gravity); + mpu6050.dmpGetYawPitchRoll(MPU6050_dmp.yawPitchRoll, &MPU6050_dmp.q, &MPU6050_dmp.gravity); + MPU_6050_gx = MPU6050_dmp.euler[0] * 180/M_PI; + MPU_6050_gy = MPU6050_dmp.euler[1] * 180/M_PI; + MPU_6050_gz = MPU6050_dmp.euler[2] * 180/M_PI; + MPU_6050_ax = MPU6050_dmp.aaReal.x; + MPU_6050_ay = MPU6050_dmp.aaReal.y; + MPU_6050_az = MPU6050_dmp.aaReal.z; +#else + mpu6050.getMotion6( + &MPU_6050_ax, + &MPU_6050_ay, + &MPU_6050_az, + &MPU_6050_gx, + &MPU_6050_gy, + &MPU_6050_gz + ); +#endif + MPU_6050_temperature = mpu6050.getTemperature(); +} +# 119 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_32_mpu6050.ino" +void MPU_6050Detect(void) +{ + for (uint32_t i = 0; i < sizeof(MPU_6050_addresses); i++) + { + MPU_6050_address = MPU_6050_addresses[i]; + if (!I2cSetDevice(MPU_6050_address)) { break; } + mpu6050.setAddr(MPU_6050_addresses[i]); + +#ifdef USE_MPU6050_DMP + MPU6050_dmp.devStatus = mpu6050.dmpInitialize(); + mpu6050.setXGyroOffset(220); + mpu6050.setYGyroOffset(76); + mpu6050.setZGyroOffset(-85); + mpu6050.setZAccelOffset(1788); + if (MPU6050_dmp.devStatus == 0) { + mpu6050.setDMPEnabled(true); + MPU6050_dmp.packetSize = mpu6050.dmpGetFIFOPacketSize(); + MPU_6050_found = true; + } +#else + mpu6050.initialize(); + MPU_6050_found = mpu6050.testConnection(); +#endif + Settings.flag2.axis_resolution = 2; + } + + if (MPU_6050_found) { + I2cSetActiveFound(MPU_6050_address, D_SENSOR_MPU6050); + } +} + +#define D_YAW "Yaw" +#define D_PITCH "Pitch" +#define D_ROLL "Roll" + +#ifdef USE_WEBSERVER +const char HTTP_SNS_AXIS[] PROGMEM = + "{s}" D_SENSOR_MPU6050 " " D_AX_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_AY_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_AZ_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_GX_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_GY_AXIS "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_GZ_AXIS "{m}%s{e}"; +#ifdef USE_MPU6050_DMP +const char HTTP_SNS_YPR[] PROGMEM = + "{s}" D_SENSOR_MPU6050 " " D_YAW "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_PITCH "{m}%s{e}" + "{s}" D_SENSOR_MPU6050 " " D_ROLL "{m}%s{e}"; +#endif +#endif + +#define D_JSON_AXIS_AX "AccelXAxis" +#define D_JSON_AXIS_AY "AccelYAxis" +#define D_JSON_AXIS_AZ "AccelZAxis" +#define D_JSON_AXIS_GX "GyroXAxis" +#define D_JSON_AXIS_GY "GyroYAxis" +#define D_JSON_AXIS_GZ "GyroZAxis" +#define D_JSON_YAW "Yaw" +#define D_JSON_PITCH "Pitch" +#define D_JSON_ROLL "Roll" + +void MPU_6050Show(bool json) +{ + MPU_6050PerformReading(); + + double tempConv = (MPU_6050_temperature / 340.0 + 35.53); + char temperature[33]; + dtostrfd(tempConv, Settings.flag2.temperature_resolution, temperature); + char axis_ax[33]; + dtostrfd(MPU_6050_ax, Settings.flag2.axis_resolution, axis_ax); + char axis_ay[33]; + dtostrfd(MPU_6050_ay, Settings.flag2.axis_resolution, axis_ay); + char axis_az[33]; + dtostrfd(MPU_6050_az, Settings.flag2.axis_resolution, axis_az); + char axis_gx[33]; + dtostrfd(MPU_6050_gx, Settings.flag2.axis_resolution, axis_gx); + char axis_gy[33]; + dtostrfd(MPU_6050_gy, Settings.flag2.axis_resolution, axis_gy); + char axis_gz[33]; + dtostrfd(MPU_6050_gz, Settings.flag2.axis_resolution, axis_gz); +#ifdef USE_MPU6050_DMP + char axis_yaw[33]; + dtostrfd(MPU6050_dmp.yawPitchRoll[0] / PI * 180.0, Settings.flag2.axis_resolution, axis_yaw); + char axis_pitch[33]; + dtostrfd(MPU6050_dmp.yawPitchRoll[1] / PI * 180.0, Settings.flag2.axis_resolution, axis_pitch); + char axis_roll[33]; + dtostrfd(MPU6050_dmp.yawPitchRoll[2] / PI * 180.0, Settings.flag2.axis_resolution, axis_roll); +#endif + + if (json) { + char json_axis_ax[25]; + snprintf_P(json_axis_ax, sizeof(json_axis_ax), PSTR(",\"" D_JSON_AXIS_AX "\":%s"), axis_ax); + char json_axis_ay[25]; + snprintf_P(json_axis_ay, sizeof(json_axis_ay), PSTR(",\"" D_JSON_AXIS_AY "\":%s"), axis_ay); + char json_axis_az[25]; + snprintf_P(json_axis_az, sizeof(json_axis_az), PSTR(",\"" D_JSON_AXIS_AZ "\":%s"), axis_az); + char json_axis_gx[25]; + snprintf_P(json_axis_gx, sizeof(json_axis_gx), PSTR(",\"" D_JSON_AXIS_GX "\":%s"), axis_gx); + char json_axis_gy[25]; + snprintf_P(json_axis_gy, sizeof(json_axis_gy), PSTR(",\"" D_JSON_AXIS_GY "\":%s"), axis_gy); + char json_axis_gz[25]; + snprintf_P(json_axis_gz, sizeof(json_axis_gz), PSTR(",\"" D_JSON_AXIS_GZ "\":%s"), axis_gz); +#ifdef USE_MPU6050_DMP + char json_ypr_y[25]; + snprintf_P(json_ypr_y, sizeof(json_ypr_y), PSTR(",\"" D_JSON_YAW "\":%s"), axis_yaw); + char json_ypr_p[25]; + snprintf_P(json_ypr_p, sizeof(json_ypr_p), PSTR(",\"" D_JSON_PITCH "\":%s"), axis_pitch); + char json_ypr_r[25]; + snprintf_P(json_ypr_r, sizeof(json_ypr_r), PSTR(",\"" D_JSON_ROLL "\":%s"), axis_roll); + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s%s%s%s}"), + D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz, + json_ypr_y, json_ypr_p, json_ypr_r); +#else + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s}"), + D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz); +#endif +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_TEMP, temperature); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, D_SENSOR_MPU6050, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_AXIS, axis_ax, axis_ay, axis_az, axis_gx, axis_gy, axis_gz); +#ifdef USE_MPU6050_DMP + WSContentSend_PD(HTTP_SNS_YPR, axis_yaw, axis_pitch, axis_roll); +#endif +#endif + } +} + + + + + +bool Xsns32(uint8_t function) +{ + if (!I2cEnabled(XI2C_25)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + MPU_6050Detect(); + } + else if (MPU_6050_found) { + switch (function) { + case FUNC_EVERY_SECOND: + if (tele_period == Settings.tele_period -3) { + MPU_6050PerformReading(); + } + break; + case FUNC_JSON_APPEND: + MPU_6050Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MPU_6050Show(0); + MPU_6050PerformReading(); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_33_ds3231.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_33_ds3231.ino" +#ifdef USE_I2C +#ifdef USE_DS3231 +# 35 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_33_ds3231.ino" +#define XSNS_33 33 +#define XI2C_26 26 + + +#ifndef USE_RTC_ADDR +#define USE_RTC_ADDR 0x68 +#endif + + +#define RTC_SECONDS 0x00 +#define RTC_MINUTES 0x01 +#define RTC_HOURS 0x02 +#define RTC_DAY 0x03 +#define RTC_DATE 0x04 +#define RTC_MONTH 0x05 +#define RTC_YEAR 0x06 +#define RTC_CONTROL 0x0E +#define RTC_STATUS 0x0F + +#define OSF 7 +#define EOSC 7 +#define BBSQW 6 +#define CONV 5 +#define RS2 4 +#define RS1 3 +#define INTCN 2 + + +#define HR1224 6 +#define CENTURY 7 +#define DYDT 6 +bool ds3231ReadStatus = false; +bool ds3231WriteStatus = false; +bool DS3231chipDetected = false; + + + + +void DS3231Detect(void) +{ + if (I2cActive(USE_RTC_ADDR)) { return; } + + if (I2cValidRead(USE_RTC_ADDR, RTC_STATUS, 1)) { + I2cSetActiveFound(USE_RTC_ADDR, "DS3231"); + DS3231chipDetected = true; + } +} + + + + +uint8_t bcd2dec(uint8_t n) +{ + return n - 6 * (n >> 4); +} + + + + +uint8_t dec2bcd(uint8_t n) +{ + return n + 6 * (n / 10); +} + + + + +uint32_t ReadFromDS3231(void) +{ + TIME_T tm; + tm.second = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_SECONDS)); + tm.minute = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MINUTES)); + tm.hour = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_HOURS) & ~_BV(HR1224)); + tm.day_of_week = I2cRead8(USE_RTC_ADDR, RTC_DAY); + tm.day_of_month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_DATE)); + tm.month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MONTH) & ~_BV(CENTURY)); + tm.year = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_YEAR)); + return MakeTime(tm); +} + + + +void SetDS3231Time (uint32_t epoch_time) { + TIME_T tm; + BreakTime(epoch_time, tm); + I2cWrite8(USE_RTC_ADDR, RTC_SECONDS, dec2bcd(tm.second)); + I2cWrite8(USE_RTC_ADDR, RTC_MINUTES, dec2bcd(tm.minute)); + I2cWrite8(USE_RTC_ADDR, RTC_HOURS, dec2bcd(tm.hour)); + I2cWrite8(USE_RTC_ADDR, RTC_DAY, tm.day_of_week); + I2cWrite8(USE_RTC_ADDR, RTC_DATE, dec2bcd(tm.day_of_month)); + I2cWrite8(USE_RTC_ADDR, RTC_MONTH, dec2bcd(tm.month)); + I2cWrite8(USE_RTC_ADDR, RTC_YEAR, dec2bcd(tm.year)); + I2cWrite8(USE_RTC_ADDR, RTC_STATUS, I2cRead8(USE_RTC_ADDR, RTC_STATUS) & ~_BV(OSF)); +} + +void DS3231EverySecond(void) +{ + TIME_T tmpTime; + if (!ds3231ReadStatus && Rtc.utc_time < START_VALID_TIME ) { + ntp_force_sync = true; + Rtc.utc_time = ReadFromDS3231(); + + + BreakTime(Rtc.utc_time, tmpTime); + if (Rtc.utc_time < START_VALID_TIME ) { + ds3231ReadStatus = true; + } + RtcTime.year = tmpTime.year + 1970; + Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); + Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); + AddLog_P2(LOG_LEVEL_INFO, PSTR("Set time from DS3231 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); + if (Rtc.local_time < START_VALID_TIME) { + rules_flag.time_init = 1; + } else { + rules_flag.time_set = 1; + } + } + else if (!ds3231WriteStatus && Rtc.utc_time > START_VALID_TIME && abs(Rtc.utc_time - ReadFromDS3231()) > 60) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("Write Time TO DS3231 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), + GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); + SetDS3231Time (Rtc.utc_time); + ds3231WriteStatus = true; + } +} + + + + + +bool Xsns33(uint8_t function) +{ + if (!I2cEnabled(XI2C_26)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + DS3231Detect(); + } + else if (DS3231chipDetected) { + switch (function) { + case FUNC_EVERY_SECOND: + DS3231EverySecond(); + break; + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_34_hx711.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_34_hx711.ino" +#ifdef USE_HX711 +# 35 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_34_hx711.ino" +#define XSNS_34 34 + +#ifndef HX_MAX_WEIGHT +#define HX_MAX_WEIGHT 20000 +#endif +#ifndef HX_REFERENCE +#define HX_REFERENCE 250 +#endif +#ifndef HX_SCALE +#define HX_SCALE 120 +#endif + +#define HX_TIMEOUT 120 +#define HX_SAMPLES 10 +#define HX_CAL_TIMEOUT 15 + +#define HX_GAIN_128 1 +#define HX_GAIN_32 2 +#define HX_GAIN_64 3 + +#define D_JSON_WEIGHT_REF "WeightRef" +#define D_JSON_WEIGHT_CAL "WeightCal" +#define D_JSON_WEIGHT_MAX "WeightMax" +#define D_JSON_WEIGHT_ITEM "WeightItem" +#define D_JSON_WEIGHT_CHANGE "WeightChange" +#define D_JSON_WEIGHT_RAW "WeightRaw" +#define D_JSON_WEIGHT_DELTA "WeightDelta" + +enum HxCalibrationSteps { HX_CAL_END, HX_CAL_LIMBO, HX_CAL_FINISH, HX_CAL_FAIL, HX_CAL_DONE, HX_CAL_FIRST, HX_CAL_RESET, HX_CAL_START }; + +const char kHxCalibrationStates[] PROGMEM = D_HX_CAL_FAIL "|" D_HX_CAL_DONE "|" D_HX_CAL_REFERENCE "|" D_HX_CAL_REMOVE; + +struct HX { + long weight = 0; + long raw = 0; + long last_weight = 0; + long sum_weight = 0; + long sum_raw = 0; + long offset = 0; + long scale = 1; + long weight_diff = 0; + uint8_t type = 1; + uint8_t sample_count = 0; + uint8_t calibrate_step = HX_CAL_END; + uint8_t calibrate_timer = 0; + uint8_t calibrate_msg = 0; + uint8_t pin_sck; + uint8_t pin_dout; + bool tare_flg = false; + bool weight_changed = false; + uint16_t weight_delta = 4; +} Hx; + + + +bool HxIsReady(uint16_t timeout) +{ + + uint32_t start = millis(); + while ((digitalRead(Hx.pin_dout) == HIGH) && (millis() - start < timeout)) { yield(); } + return (digitalRead(Hx.pin_dout) == LOW); +} + +long HxRead(void) +{ + if (!HxIsReady(HX_TIMEOUT)) { return -1; } + + uint8_t data[3] = { 0 }; + uint8_t filler = 0x00; + + + data[2] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); + data[1] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); + data[0] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); + + + for (unsigned int i = 0; i < HX_GAIN_128; i++) { + digitalWrite(Hx.pin_sck, HIGH); + digitalWrite(Hx.pin_sck, LOW); + } + + + if (data[2] & 0x80) { filler = 0xFF; } + + + unsigned long value = ( static_cast(filler) << 24 + | static_cast(data[2]) << 16 + | static_cast(data[1]) << 8 + | static_cast(data[0]) ); + + return static_cast(value); +} + + + +void HxResetPart(void) +{ + Hx.tare_flg = true; + Hx.sum_weight = 0; + Hx.sample_count = 0; + Hx.last_weight = 0; +} + +void HxReset(void) +{ + HxResetPart(); + Settings.energy_frequency_calibration = 0; +} + +void HxCalibrationStateTextJson(uint8_t msg_id) +{ + char cal_text[30]; + + Hx.calibrate_msg = msg_id; + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, GetTextIndexed(cal_text, sizeof(cal_text), Hx.calibrate_msg, kHxCalibrationStates)); + + if (msg_id < 3) { MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("Sensor34")); } +} + +void SetWeightDelta() +{ + + if (Settings.weight_change == 0) { + Hx.weight_delta = 4; + return; + } + + + if (Settings.weight_change > 100) { + Hx.weight_delta = (Settings.weight_change - 100) * 10 + 100; + return; + } + + + Hx.weight_delta = Settings.weight_change - 1; +} +# 192 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_34_hx711.ino" +bool HxCommand(void) +{ + bool serviced = true; + bool show_parms = false; + char sub_string[XdrvMailbox.data_len +1]; + + for (uint32_t ca = 0; ca < XdrvMailbox.data_len; ca++) { + if ((' ' == XdrvMailbox.data[ca]) || ('=' == XdrvMailbox.data[ca])) { XdrvMailbox.data[ca] = ','; } + } + + switch (XdrvMailbox.payload) { + case 1: + HxReset(); + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, "Reset"); + break; + case 2: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + Hx.scale = 1; + HxReset(); + Hx.calibrate_step = HX_CAL_START; + Hx.calibrate_timer = 1; + HxCalibrationStateTextJson(3); + break; + case 3: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + } + show_parms = true; + break; + case 4: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_calibration = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + Hx.scale = Settings.weight_calibration; + } + show_parms = true; + break; + case 5: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_max = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) / 1000; + } + show_parms = true; + break; + case 6: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_item = (unsigned long)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 10); + } + show_parms = true; + break; + case 7: + Settings.energy_frequency_calibration = Hx.weight; + Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, D_JSON_DONE); + break; + case 8: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.SensorBits1.hx711_json_weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) & 1; + } + show_parms = true; + break; + case 9: + if (strstr(XdrvMailbox.data, ",") != nullptr) { + Settings.weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); + SetWeightDelta(); + } + show_parms = true; + break; + default: + show_parms = true; + } + + if (show_parms) { + char item[33]; + dtostrfd((float)Settings.weight_item / 10, 1, item); + Response_P(PSTR("{\"Sensor34\":{\"" D_JSON_WEIGHT_REF "\":%d,\"" D_JSON_WEIGHT_CAL "\":%d,\"" D_JSON_WEIGHT_MAX "\":%d,\"" + D_JSON_WEIGHT_ITEM "\":%s,\"" D_JSON_WEIGHT_CHANGE "\":%s,\"" D_JSON_WEIGHT_DELTA "\":%d}}"), + Settings.weight_reference, Settings.weight_calibration, Settings.weight_max * 1000, + item, GetStateText(Settings.SensorBits1.hx711_json_weight_change), Settings.weight_change); + } + + return serviced; +} + + + +long HxWeight(void) +{ + return (Hx.calibrate_step < HX_CAL_FAIL) ? Hx.weight : 0; +} + +void HxInit(void) +{ + Hx.type = 0; + if ((pin[GPIO_HX711_DAT] < 99) && (pin[GPIO_HX711_SCK] < 99)) { + Hx.pin_sck = pin[GPIO_HX711_SCK]; + Hx.pin_dout = pin[GPIO_HX711_DAT]; + + pinMode(Hx.pin_sck, OUTPUT); + pinMode(Hx.pin_dout, INPUT); + + digitalWrite(Hx.pin_sck, LOW); + + SetWeightDelta(); + + if (HxIsReady(8 * HX_TIMEOUT)) { + if (!Settings.weight_max) { Settings.weight_max = HX_MAX_WEIGHT / 1000; } + if (!Settings.weight_calibration) { Settings.weight_calibration = HX_SCALE; } + if (!Settings.weight_reference) { Settings.weight_reference = HX_REFERENCE; } + Hx.scale = Settings.weight_calibration; + HxRead(); + HxResetPart(); + Hx.type = 1; + } + } +} + +void HxEvery100mSecond(void) +{ + long raw = HxRead(); + Hx.sum_raw += raw; + Hx.sum_weight += raw; + + Hx.sample_count++; + if (HX_SAMPLES == Hx.sample_count) { + long average = Hx.sum_weight / Hx.sample_count; + long raw_average = Hx.sum_raw / Hx.sample_count; + long value = average - Hx.offset; + Hx.weight = value / Hx.scale; + Hx.raw = raw_average / Hx.scale; + if (Hx.weight < 0) { + if (Settings.energy_frequency_calibration) { + long difference = Settings.energy_frequency_calibration + Hx.weight; + Hx.last_weight = difference; + if (difference < 0) { HxReset(); } + } + Hx.weight = 0; + } else { + Hx.last_weight = Settings.energy_frequency_calibration; + } + + if (Hx.tare_flg) { + Hx.tare_flg = false; + Hx.offset = average; + } + + if (Hx.calibrate_step) { + Hx.calibrate_timer--; + + if (HX_CAL_START == Hx.calibrate_step) { + Hx.calibrate_step--; + Hx.calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); + } + else if (HX_CAL_RESET == Hx.calibrate_step) { + if (Hx.calibrate_timer) { + if (Hx.weight < (long)Settings.weight_reference) { + Hx.calibrate_step--; + Hx.calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); + HxCalibrationStateTextJson(2); + } + } else { + Hx.calibrate_step = HX_CAL_FAIL; + } + } + else if (HX_CAL_FIRST == Hx.calibrate_step) { + if (Hx.calibrate_timer) { + if (Hx.weight > (long)Settings.weight_reference) { + Hx.calibrate_step--; + } + } else { + Hx.calibrate_step = HX_CAL_FAIL; + } + } + else if (HX_CAL_DONE == Hx.calibrate_step) { + if (Hx.weight > (long)Settings.weight_reference) { + Hx.calibrate_step = HX_CAL_FINISH; + Settings.weight_calibration = Hx.weight / Settings.weight_reference; + Hx.weight = 0; + HxCalibrationStateTextJson(1); + } else { + Hx.calibrate_step = HX_CAL_FAIL; + } + } + + if (HX_CAL_FAIL == Hx.calibrate_step) { + Hx.calibrate_step--; + Hx.tare_flg = true; + HxCalibrationStateTextJson(0); + } + if (HX_CAL_FINISH == Hx.calibrate_step) { + Hx.calibrate_step--; + Hx.calibrate_timer = 3 * (10 / HX_SAMPLES); + Hx.scale = Settings.weight_calibration; + } + + if (!Hx.calibrate_timer) { + Hx.calibrate_step = HX_CAL_END; + } + } else { + Hx.weight += Hx.last_weight; + + if (Settings.SensorBits1.hx711_json_weight_change) { + if (abs(Hx.weight - Hx.weight_diff) > Hx.weight_delta) { + Hx.weight_diff = Hx.weight; + Hx.weight_changed = true; + } + else if (Hx.weight_changed && (Hx.weight == Hx.weight_diff)) { + mqtt_data[0] = '\0'; + ResponseAppendTime(); + HxShow(true); + ResponseJsonEnd(); + MqttPublishTeleSensor(); + Hx.weight_changed = false; + } + } + } + + Hx.sum_weight = 0; + Hx.sum_raw = 0; + Hx.sample_count = 0; + } +} + +void HxSaveBeforeRestart(void) +{ + Settings.energy_frequency_calibration = Hx.weight; + Hx.sample_count = HX_SAMPLES +1; +} + +#ifdef USE_WEBSERVER +const char HTTP_HX711_WEIGHT[] PROGMEM = + "{s}HX711 " D_WEIGHT "{m}%s " D_UNIT_KILOGRAM "{e}"; +const char HTTP_HX711_COUNT[] PROGMEM = + "{s}HX711 " D_COUNT "{m}%d{e}"; +const char HTTP_HX711_CAL[] PROGMEM = + "{s}HX711 %s{m}{e}"; +#endif + +void HxShow(bool json) +{ + char scount[30] = { 0 }; + + uint16_t count = 0; + float weight = 0; + if (Hx.calibrate_step < HX_CAL_FAIL) { + if (Hx.weight && Settings.weight_item) { + count = (Hx.weight * 10) / Settings.weight_item; + if (count > 1) { + snprintf_P(scount, sizeof(scount), PSTR(",\"" D_JSON_COUNT "\":%d"), count); + } + } + weight = (float)Hx.weight / 1000; + } + char weight_chr[33]; + dtostrfd(weight, Settings.flag2.weight_resolution, weight_chr); + + if (json) { + ResponseAppend_P(PSTR(",\"HX711\":{\"" D_JSON_WEIGHT "\":%s%s, \"" D_JSON_WEIGHT_RAW "\":%d}"), weight_chr, scount, Hx.raw); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_HX711_WEIGHT, weight_chr); + if (count > 1) { + WSContentSend_PD(HTTP_HX711_COUNT, count); + } + if (Hx.calibrate_step) { + char cal_text[30]; + WSContentSend_PD(HTTP_HX711_CAL, GetTextIndexed(cal_text, sizeof(cal_text), Hx.calibrate_msg, kHxCalibrationStates)); + } +#endif + } +} + +#ifdef USE_WEBSERVER +#ifdef USE_HX711_GUI + + + + +#define WEB_HANDLE_HX711 "s34" + +const char S_CONFIGURE_HX711[] PROGMEM = D_CONFIGURE_HX711; + +const char HTTP_BTN_MENU_MAIN_HX711[] PROGMEM = + "

"; + +const char HTTP_BTN_MENU_HX711[] PROGMEM = + "

"; + +const char HTTP_FORM_HX711[] PROGMEM = + "
 " D_CALIBRATION " " + "
" + "

" D_REFERENCE_WEIGHT " (" D_UNIT_KILOGRAM ")

" + "
" + "
" + "


" + + "
 " D_HX711_PARAMETERS " " + "
" + "

" D_ITEM_WEIGHT " (" D_UNIT_KILOGRAM ")

"; + +void HandleHxAction(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_HX711); + + if (WebServer->hasArg("save")) { + HxSaveSettings(); + HandleConfiguration(); + return; + } + + char stemp1[20]; + + if (WebServer->hasArg("reset")) { + snprintf_P(stemp1, sizeof(stemp1), PSTR("Sensor34 1")); + ExecuteWebCommand(stemp1, SRC_WEBGUI); + + HandleRoot(); + return; + } + + if (WebServer->hasArg("calibrate")) { + WebGetArg("p1", stemp1, sizeof(stemp1)); + Settings.weight_reference = (!strlen(stemp1)) ? 0 : (unsigned long)(CharToFloat(stemp1) * 1000); + + HxLogUpdates(); + + snprintf_P(stemp1, sizeof(stemp1), PSTR("Sensor34 2")); + ExecuteWebCommand(stemp1, SRC_WEBGUI); + + HandleRoot(); + return; + } + + WSContentStart_P(S_CONFIGURE_HX711); + WSContentSendStyle(); + dtostrfd((float)Settings.weight_reference / 1000, 3, stemp1); + char stemp2[20]; + dtostrfd((float)Settings.weight_item / 10000, 4, stemp2); + WSContentSend_P(HTTP_FORM_HX711, stemp1, stemp2); + WSContentSend_P(HTTP_FORM_END); + WSContentSpaceButton(BUTTON_CONFIGURATION); + WSContentStop(); +} + +void HxSaveSettings(void) +{ + char tmp[100]; + + WebGetArg("p2", tmp, sizeof(tmp)); + Settings.weight_item = (!strlen(tmp)) ? 0 : (unsigned long)(CharToFloat(tmp) * 10000); + + HxLogUpdates(); +} + +void HxLogUpdates(void) +{ + char weigth_ref_chr[33]; + dtostrfd((float)Settings.weight_reference / 1000, Settings.flag2.weight_resolution, weigth_ref_chr); + char weigth_item_chr[33]; + dtostrfd((float)Settings.weight_item / 10000, 4, weigth_item_chr); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_JSON_WEIGHT_REF " %s, " D_JSON_WEIGHT_ITEM " %s"), weigth_ref_chr, weigth_item_chr); +} + +#endif +#endif + + + + + +bool Xsns34(uint8_t function) +{ + bool result = false; + + if (Hx.type) { + switch (function) { + case FUNC_EVERY_100_MSECOND: + HxEvery100mSecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_34 == XdrvMailbox.index) { + result = HxCommand(); + } + break; + case FUNC_JSON_APPEND: + HxShow(1); + break; + case FUNC_SAVE_BEFORE_RESTART: + HxSaveBeforeRestart(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HxShow(0); + break; +#ifdef USE_HX711_GUI + case FUNC_WEB_ADD_MAIN_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_MAIN_HX711); + break; + case FUNC_WEB_ADD_BUTTON: + WSContentSend_P(HTTP_BTN_MENU_HX711); + break; + case FUNC_WEB_ADD_HANDLER: + WebServer->on("/" WEB_HANDLE_HX711, HandleHxAction); + break; +#endif +#endif + case FUNC_INIT: + HxInit(); + break; + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_35_tx20.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_35_tx20.ino" +#ifdef USE_TX20_WIND_SENSOR +# 29 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_35_tx20.ino" +#define XSNS_35 35 + +#define TX20_BIT_TIME 1220 +#define TX20_RESET_VALUES 60 + + + +extern "C" { +#include "gpio.h" +} + +#ifdef USE_WEBSERVER + +const char HTTP_SNS_TX20[] PROGMEM = + "{s}TX20 " D_TX20_WIND_SPEED "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" + "{s}TX20 " D_TX20_WIND_SPEED_AVG "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" + "{s}TX20 " D_TX20_WIND_SPEED_MAX "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" + "{s}TX20 " D_TX20_WIND_DIRECTION "{m}%s{e}"; + +#endif + +const char kTx20Directions[] PROGMEM = D_TX20_NORTH "|" + D_TX20_NORTH D_TX20_NORTH D_TX20_EAST "|" + D_TX20_NORTH D_TX20_EAST "|" + D_TX20_EAST D_TX20_NORTH D_TX20_EAST "|" + D_TX20_EAST "|" + D_TX20_EAST D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH "|" + D_TX20_SOUTH D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_WEST D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_WEST "|" + D_TX20_WEST D_TX20_NORTH D_TX20_WEST "|" + D_TX20_NORTH D_TX20_WEST "|" + D_TX20_NORTH D_TX20_NORTH D_TX20_WEST; + +uint8_t tx20_sa = 0; +uint8_t tx20_sb = 0; +uint8_t tx20_sd = 0; +uint8_t tx20_se = 0; +uint16_t tx20_sc = 0; +uint16_t tx20_sf = 0; + +float tx20_wind_speed_kmh = 0; +float tx20_wind_speed_max = 0; +float tx20_wind_speed_avg = 0; +float tx20_wind_sum = 0; +int tx20_count = 0; +uint8_t tx20_wind_direction = 0; + +bool tx20_available = false; + +#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 +void Tx20StartRead(void) ICACHE_RAM_ATTR; +#endif + +void Tx20StartRead(void) +{ +# 101 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_35_tx20.ino" + tx20_available = false; + + tx20_sa = 0; + tx20_sb = 0; + tx20_sd = 0; + tx20_se = 0; + tx20_sc = 0; + tx20_sf = 0; + + delayMicroseconds(TX20_BIT_TIME / 2); + + for (int32_t bitcount = 41; bitcount > 0; bitcount--) { + uint8_t dpin = (digitalRead(pin[GPIO_TX20_TXD_BLACK])); + if (bitcount > 41 - 5) { + + tx20_sa = (tx20_sa << 1) | (dpin ^ 1); + } else if (bitcount > 41 - 5 - 4) { + + tx20_sb = tx20_sb >> 1 | ((dpin ^ 1) << 3); + } else if (bitcount > 41 - 5 - 4 - 12) { + + tx20_sc = tx20_sc >> 1 | ((dpin ^ 1) << 11); + } else if (bitcount > 41 - 5 - 4 - 12 - 4) { + + tx20_sd = tx20_sd >> 1 | ((dpin ^ 1) << 3); + } else if (bitcount > 41 - 5 - 4 - 12 - 4 - 4) { + + tx20_se = tx20_se >> 1 | (dpin << 3); + } else { + + tx20_sf = tx20_sf >> 1 | (dpin << 11); + } + + delayMicroseconds(TX20_BIT_TIME); + } + + uint8_t chk = (tx20_sb + (tx20_sc & 0xf) + ((tx20_sc >> 4) & 0xf) + ((tx20_sc >> 8) & 0xf)); + chk &= 0xf; + + if ((chk == tx20_sd) && (tx20_sc < 400)) { + tx20_available = true; + } + + + + + + + + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << pin[GPIO_TX20_TXD_BLACK]); +} + +void Tx20Read(void) +{ + if (!(uptime % TX20_RESET_VALUES)) { + tx20_count = 0; + tx20_wind_sum = 0; + tx20_wind_speed_max = 0; + } + else if (tx20_available) { + tx20_wind_speed_kmh = float(tx20_sc) * 0.36; + if (tx20_wind_speed_kmh > tx20_wind_speed_max) { + tx20_wind_speed_max = tx20_wind_speed_kmh; + } + tx20_count++; + tx20_wind_sum += tx20_wind_speed_kmh; + tx20_wind_speed_avg = tx20_wind_sum / tx20_count; + tx20_wind_direction = tx20_sb; + } +} + +void Tx20Init(void) { + pinMode(pin[GPIO_TX20_TXD_BLACK], INPUT); + attachInterrupt(pin[GPIO_TX20_TXD_BLACK], Tx20StartRead, RISING); +} + +void Tx20Show(bool json) +{ + char wind_speed_string[33]; + dtostrfd(tx20_wind_speed_kmh, 2, wind_speed_string); + char wind_speed_max_string[33]; + dtostrfd(tx20_wind_speed_max, 2, wind_speed_max_string); + char wind_speed_avg_string[33]; + dtostrfd(tx20_wind_speed_avg, 2, wind_speed_avg_string); + char wind_direction_string[4]; + GetTextIndexed(wind_direction_string, sizeof(wind_direction_string), tx20_wind_direction, kTx20Directions); + + if (json) { + ResponseAppend_P(PSTR(",\"TX20\":{\"Speed\":%s,\"SpeedAvg\":%s,\"SpeedMax\":%s,\"Direction\":\"%s\"}"), + wind_speed_string, wind_speed_avg_string, wind_speed_max_string, wind_direction_string); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TX20, wind_speed_string, wind_speed_avg_string, wind_speed_max_string, wind_direction_string); +#endif + } +} + + + + + +bool Xsns35(uint8_t function) +{ + bool result = false; + + if (pin[GPIO_TX20_TXD_BLACK] < 99) { + switch (function) { + case FUNC_INIT: + Tx20Init(); + break; + case FUNC_EVERY_SECOND: + Tx20Read(); + break; + case FUNC_JSON_APPEND: + Tx20Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Tx20Show(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_36_mgc3130.ino" +# 22 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_36_mgc3130.ino" +#ifdef USE_I2C +#ifdef USE_MGC3130 +# 35 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_36_mgc3130.ino" +#define XSNS_36 36 +#define XI2C_27 27 + +#warning **** MGC3130: It is recommended to disable all unneeded I2C-drivers **** + +#define MGC3130_I2C_ADDR 0x42 + +#define MGC3130_xfer pin[GPIO_MGC3130_XFER] +#define MGC3130_reset pin[GPIO_MGC3130_RESET] + + +bool MGC3130_type = false; +char MGC3130stype[] = "MGC3130"; + + +#define MGC3130_SYSTEM_STATUS 0x15 +#define MGC3130_REQUEST_MSG 0x06 +#define MGC3130_FW_VERSION 0x83 +#define MGC3130_SET_RUNTIME 0xA2 +#define MGC3130_SENSOR_DATA 0x91 + + +#define MGC3130_GESTURE_GARBAGE 1 +#define MGC3130_FLICK_WEST_EAST 2 +#define MGC3130_FLICK_EAST_WEST 3 +#define MGC3130_FLICK_SOUTH_NORTH 4 +#define MGC3130_FLICK_NORTH_SOUTH 5 +#define MGC3130_CIRCLE_CLOCKWISE 6 +#define MGC3130_CIRCLE_CCLOCKWISE 7 + +#define MGC3130_MIN_ROTVALUE 0 +#define MGC3130_MAX_ROTVALUE 1023 +#define MGC3130_MIN_ZVALUE 32768 + + +#ifdef USE_WEBSERVER +const char HTTP_MGC_3130_SNS[] PROGMEM = + "{s}" "%s" "{m}%s{e}" + "{s}" "HwRev" "{m}%u.%u{e}" + "{s}" "loaderVer" "{m}%u.%u{e}" + "{s}" "platVer" "{m}%u{e}"; +#endif + + + + + + + +#pragma pack(1) +union MGC3130_Union{ + uint8_t buffer[132]; + struct + { + + uint8_t msgSize; + uint8_t flag; + uint8_t counter; + uint8_t id; + + struct { + uint8_t DSPStatus:1; + uint8_t gestureInfo:1; + uint8_t touchInfo:1; + uint8_t airWheelInfo:1; + uint8_t xyzPosition:1; + uint8_t noisePower:1; + uint8_t reserved:2; + uint8_t electrodeConfiguration:3; + uint8_t CICData:1; + uint8_t SDData:1; + uint16_t reserved2:3; + } outputConfigMask; + uint8_t timestamp; + struct { + uint8_t positionValid:1; + uint8_t airWheelValid:1; + uint8_t rawDataValid:1; + uint8_t noisePowerValid:1; + uint8_t environmentalNoise:1; + uint8_t clipping:1; + uint8_t reserved:1; + uint8_t DSPRunning:1; + } systemInfo; + uint16_t dspInfo; + struct { + uint8_t gestureCode:8; + uint8_t reserved:4; + uint8_t gestureType:4; + uint8_t edgeFlick:1; + uint16_t reserved2:14; + uint8_t gestureInProgress:1; + } gestureInfo; + struct { + uint8_t touchSouth:1; + uint8_t touchWest:1; + uint8_t touchNorth:1; + uint8_t touchEast:1; + uint8_t touchCentre:1; + uint8_t tapSouth:1; + uint8_t tapWest:1; + uint8_t tapNorth:1; + uint8_t tapEast :1; + uint8_t tapCentre:1; + uint8_t doubleTapSouth:1; + uint8_t doubleTapWest:1; + uint8_t doubleTapNorth:1; + uint8_t doubleTapEast:1; + uint8_t doubleTapCentre:1; + uint8_t reserved:1; + uint8_t touchCounter; + uint8_t reserved2; + } touchInfo; + int8_t airWheel; + uint8_t reserved; + uint16_t x; + uint16_t y; + uint16_t z; + float noisePower; + float CICData[4]; + float SDData[4]; + } out; + struct { + uint8_t header[3]; + + uint8_t valid; + uint8_t hwRev[2]; + uint8_t parameterStartAddr; + uint8_t loaderVersion[2]; + uint8_t loaderPlatform; + uint8_t fwStartAddr; + char fwVersion[120]; + } fw; + struct{ + uint8_t id; + uint8_t size; + uint16_t error; + uint32_t reserved; + uint32_t reserved1; + } status; +} MGC_data; +#pragma pack() + +char MGC3130_currentGesture[12]; + +int8_t MGC3130_delta, MGC3130_lastrotation = 0; +int16_t MGC3130_rotValue, MGC3130_lastSentRotValue = 0; + +uint16_t MGC3130_lastSentX, MGC3130_lastSentY, MGC3130_lastSentZ = 0; + +uint8_t hwRev[2], loaderVersion[2], loaderPlatform = 0; +char MGC3130_firmwareInfo[20]; + +uint8_t MGC3130_touchTimeout = 0; +uint16_t MGC3130_touchCounter = 1; +uint32_t MGC3130_touchTimeStamp = millis(); +bool MGC3130_triggeredByTouch = false; + +uint8_t MGC3130_mode = 1; + + + +uint8_t MGC3130autoCal[] = {0x10, 0x00, 0x00, 0xA2, 0x80, 0x00 , 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; +uint8_t MGC3130disableAirwheel[] = {0x10, 0x00, 0x00, 0xA2, 0x90, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; +uint8_t MGC3130enableAirwheel[] = {0x10, 0x00, 0x00, 0xA2, 0x90, 0x00 , 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; + +void MGC3130_handleSensorData(){ + if ( MGC_data.out.outputConfigMask.touchInfo && MGC3130_touchTimeout == 0){ + if (MGC3130_handleTouch()){ + MGC3130_triggeredByTouch = true; + MqttPublishSensor(); + } + } + + if(MGC3130_mode == 1){ + if( MGC_data.out.outputConfigMask.gestureInfo && MGC_data.out.gestureInfo.gestureCode > 0){ + MGC3130_handleGesture(); + MqttPublishSensor(); + } + } + if(MGC3130_mode == 2){ + if(MGC_data.out.outputConfigMask.airWheelInfo && MGC_data.out.systemInfo.airWheelValid){ + MGC3130_handleAirWheel(); + MqttPublishSensor(); + } + } + if(MGC3130_mode == 3){ + if(MGC_data.out.systemInfo.positionValid && (MGC_data.out.z > MGC3130_MIN_ZVALUE)){ + MqttPublishSensor(); + } + } +} + +void MGC3130_sendMessage(uint8_t data[], uint8_t length){ + Wire.beginTransmission(MGC3130_I2C_ADDR); + Wire.write(data,length); + Wire.endTransmission(); + delay(2); + MGC3130_receiveMessage(); +} + + +void MGC3130_handleGesture(){ + + char edge[5]; + if (MGC_data.out.gestureInfo.edgeFlick){ + snprintf_P(edge, sizeof(edge), PSTR("ED_")); + } + else{ + snprintf_P(edge, sizeof(edge), PSTR("")); + } + switch(MGC_data.out.gestureInfo.gestureCode){ + case MGC3130_GESTURE_GARBAGE: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("NONE")); + break; + case MGC3130_FLICK_WEST_EAST: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_WE"), edge); + break; + case MGC3130_FLICK_EAST_WEST: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_EW"), edge); + break; + case MGC3130_FLICK_SOUTH_NORTH: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_SN"), edge); + break; + case MGC3130_FLICK_NORTH_SOUTH: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_NS"), edge); + break; + case MGC3130_CIRCLE_CLOCKWISE: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("CW")); + break; + case MGC3130_CIRCLE_CCLOCKWISE: + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("CCW")); + break; + } + +} + +bool MGC3130_handleTouch(){ + + bool success = false; + if (MGC_data.out.touchInfo.doubleTapCentre && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_C")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.doubleTapEast && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_E")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.doubleTapNorth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_N")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.doubleTapWest && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_W")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.doubleTapSouth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_S")); + MGC3130_touchTimeout = 5; + success = true; + MGC3130_touchCounter = 1; + } + if (MGC_data.out.touchInfo.tapCentre && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_C")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.tapEast && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_E")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.tapNorth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_N")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.tapWest && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_W")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.tapSouth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_S")); + MGC3130_touchTimeout = 2; + success = true; + MGC3130_touchCounter = 1; + } + else if (MGC_data.out.touchInfo.touchCentre && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_C")); + success = true; + MGC3130_touchCounter++; + } + else if (MGC_data.out.touchInfo.touchEast && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_E")); + success = true; + MGC3130_touchCounter++; + } + else if (MGC_data.out.touchInfo.touchNorth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_N")); + success = true; + MGC3130_touchCounter++; + } + else if (MGC_data.out.touchInfo.touchWest && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_W")); + success = true; + MGC3130_touchCounter++; + } + else if (MGC_data.out.touchInfo.touchSouth && !success){ + + snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_S")); + success = true; + MGC3130_touchCounter++; + } + + return success; +} + +void MGC3130_handleAirWheel(){ + MGC3130_delta = MGC_data.out.airWheel - MGC3130_lastrotation; + MGC3130_lastrotation = MGC_data.out.airWheel; + + MGC3130_rotValue = MGC3130_rotValue + MGC3130_delta; + if(MGC3130_rotValue < MGC3130_MIN_ROTVALUE){ + MGC3130_rotValue = MGC3130_MIN_ROTVALUE; + } + if(MGC3130_rotValue > MGC3130_MAX_ROTVALUE){ + MGC3130_rotValue = MGC3130_MAX_ROTVALUE; + } +} + +void MGC3130_handleSystemStatus(){ + +} + +bool MGC3130_receiveMessage(){ + if(MGC3130_readData()){ + switch(MGC_data.out.id){ + case MGC3130_SENSOR_DATA: + MGC3130_handleSensorData(); + break; + case MGC3130_SYSTEM_STATUS: + MGC3130_handleSystemStatus(); + break; + case MGC3130_FW_VERSION: + hwRev[0] = MGC_data.fw.hwRev[1]; + hwRev[1] = MGC_data.fw.hwRev[0]; + loaderVersion[0] = MGC_data.fw.loaderVersion[0]; + loaderVersion[1] = MGC_data.fw.loaderVersion[1]; + loaderPlatform = MGC_data.fw.loaderPlatform; + snprintf_P(MGC3130_firmwareInfo, sizeof(MGC3130_firmwareInfo), PSTR("FW: %s"), MGC_data.fw.fwVersion); + MGC3130_firmwareInfo[20] = '\0'; + + break; + } + return true; + } + return false; +} + +bool MGC3130_readData() +{ + bool success = false; + if (!digitalRead(MGC3130_xfer)){ + pinMode(MGC3130_xfer, OUTPUT); + digitalWrite(MGC3130_xfer, LOW); + Wire.requestFrom(MGC3130_I2C_ADDR, (uint16_t)32); + + MGC_data.buffer[0] = 4; + unsigned char i = 0; + while(Wire.available() && (i < MGC_data.buffer[0])){ + MGC_data.buffer[i] = Wire.read(); + i++; + } + digitalWrite(MGC3130_xfer, HIGH); + pinMode(MGC3130_xfer, INPUT); + success = true; + } + return success; +} + +void MGC3130_nextMode(){ + if (MGC3130_mode < 3){ + MGC3130_mode++; + } + else{ + MGC3130_mode = 1; + } + switch(MGC3130_mode){ + case 1: + MGC3130_sendMessage(MGC3130disableAirwheel,16); + break; + case 2: + MGC3130_sendMessage(MGC3130enableAirwheel,16); + break; + case 3: + MGC3130_sendMessage(MGC3130disableAirwheel,16); + break; + } +} + +void MGC3130_loop() +{ + if(MGC3130_touchTimeout > 0){ + MGC3130_touchTimeout--; + } + MGC3130_receiveMessage(); +} + +void MGC3130_detect(void) +{ + if (MGC3130_type || I2cActive(MGC3130_I2C_ADDR)) { return; } + + pinMode(MGC3130_xfer, INPUT_PULLUP); + pinMode(MGC3130_reset, OUTPUT); + digitalWrite(MGC3130_reset, LOW); + delay(10); + digitalWrite(MGC3130_reset, HIGH); + delay(50); + + if (MGC3130_receiveMessage()) { + I2cSetActiveFound(MGC3130_I2C_ADDR, MGC3130stype); + MGC3130_currentGesture[0] = '\0'; + MGC3130_type = true; + } +} + + + + + +void MGC3130_show(bool json) +{ + if (!MGC3130_type) { return; } + + char status_chr[2]; + if (MGC_data.out.systemInfo.DSPRunning) { + sprintf (status_chr, "1"); + } + else{ + sprintf (status_chr, "0"); + } + + if (json) { + if (MGC3130_mode == 3 && !MGC3130_triggeredByTouch) { + if (MGC_data.out.systemInfo.positionValid && !(MGC_data.out.x == MGC3130_lastSentX && MGC_data.out.y == MGC3130_lastSentY && MGC_data.out.z == MGC3130_lastSentZ)) { + ResponseAppend_P(PSTR(",\"%s\":{\"X\":%u,\"Y\":%u,\"Z\":%u}"), + MGC3130stype, MGC_data.out.x/64, MGC_data.out.y/64, (MGC_data.out.z-(uint16_t)MGC3130_MIN_ZVALUE)/64); + MGC3130_lastSentX = MGC_data.out.x; + MGC3130_lastSentY = MGC_data.out.y; + MGC3130_lastSentZ = MGC_data.out.z; + } + } + MGC3130_triggeredByTouch = false; + + if (MGC3130_mode == 2) { + if (MGC_data.out.systemInfo.airWheelValid && (MGC3130_rotValue != MGC3130_lastSentRotValue)) { + ResponseAppend_P(PSTR(",\"%s\":{\"AW\":%i}"), MGC3130stype, MGC3130_rotValue); + MGC3130_lastSentRotValue = MGC3130_rotValue; + } + } + + if (MGC3130_currentGesture[0] != '\0') { + if (millis() - MGC3130_touchTimeStamp > 220 ) { + MGC3130_touchCounter = 1; + } + ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%u}"), MGC3130stype, MGC3130_currentGesture, MGC3130_touchCounter); + MGC3130_currentGesture[0] = '\0'; + MGC3130_touchTimeStamp = millis(); + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_MGC_3130_SNS, MGC3130stype, status_chr, hwRev[0], hwRev[1], loaderVersion[0], loaderVersion[1], loaderPlatform ); +#endif + } +} +# 557 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_36_mgc3130.ino" +bool MGC3130CommandSensor() +{ + bool serviced = true; + + switch (XdrvMailbox.payload) { + case 0: + MGC3130_nextMode(); + break; + case 1: + MGC3130_mode = 1; + MGC3130_sendMessage(MGC3130disableAirwheel,16); + break; + case 2: + MGC3130_mode = 2; + MGC3130_sendMessage(MGC3130enableAirwheel,16); + break; + case 3: + MGC3130_mode = 3; + MGC3130_sendMessage(MGC3130disableAirwheel,16); + break; + } + return serviced; +} + + + + + +bool Xsns36(uint8_t function) +{ + if (!I2cEnabled(XI2C_27)) { return false; } + + bool result = false; + + if ((FUNC_INIT == function) && (pin[GPIO_MGC3130_XFER] < 99) && (pin[GPIO_MGC3130_RESET] < 99)) { + MGC3130_detect(); + } + else if (MGC3130_type) { + switch (function) { + case FUNC_EVERY_50_MSECOND: + MGC3130_loop(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_36 == XdrvMailbox.index) { + result = MGC3130CommandSensor(); + } + break; + case FUNC_JSON_APPEND: + MGC3130_show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MGC3130_show(0); + break; +#endif + } + } + return result; +} +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_37_rfsensor.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_37_rfsensor.ino" +#ifdef USE_RF_SENSOR +# 33 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_37_rfsensor.ino" +#define XSNS_37 37 + + + + +#define RFSNS_VALID_WINDOW 1800 + +#define RFSNS_LOOPS_PER_MILLI 1900 +#define RFSNS_RAW_BUFFER_SIZE 180 +#define RFSNS_MIN_RAW_PULSES 112 + +#define RFSNS_MIN_PULSE_LENGTH 300 +#define RFSNS_RAWSIGNAL_SAMPLE 50 +#define RFSNS_SIGNAL_TIMEOUT 10 +#define RFSNS_SIGNAL_REPEAT_TIME 500 + +typedef struct RawSignalStruct +{ + int Number; + uint8_t Repeats; + uint8_t Multiply; + unsigned long Time; + uint8_t Pulses[RFSNS_RAW_BUFFER_SIZE+2]; + +} raw_signal_t; + +raw_signal_t *rfsns_raw_signal = nullptr; +uint8_t rfsns_rf_bit; +uint8_t rfsns_rf_port; +uint8_t rfsns_any_sensor = 0; + + + + + +bool RfSnsFetchSignal(uint8_t DataPin, bool StateSignal) +{ + uint8_t Fbit = digitalPinToBitMask(DataPin); + uint8_t Fport = digitalPinToPort(DataPin); + uint8_t FstateMask = (StateSignal ? Fbit : 0); + + if ((*portInputRegister(Fport) & Fbit) == FstateMask) { + const unsigned long LoopsPerMilli = RFSNS_LOOPS_PER_MILLI; + + + + + + + unsigned long PulseLength = 0; + if (rfsns_raw_signal->Time) { + if (rfsns_raw_signal->Repeats && (rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) { + PulseLength = micros() + RFSNS_SIGNAL_TIMEOUT *1000; + while (((rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && (PulseLength > micros())) { + if ((*portInputRegister(Fport) & Fbit) == FstateMask) { + PulseLength = micros() + RFSNS_SIGNAL_TIMEOUT *1000; + } + } + while (((rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && ((*portInputRegister(Fport) & Fbit) != FstateMask)); + } + } + + int RawCodeLength = 1; + bool Ftoggle = false; + unsigned long numloops = 0; + unsigned long maxloops = RFSNS_SIGNAL_TIMEOUT * LoopsPerMilli; + rfsns_raw_signal->Multiply = RFSNS_RAWSIGNAL_SAMPLE; + do { + numloops = 0; + while(((*portInputRegister(Fport) & Fbit) == FstateMask) ^ Ftoggle) { + if (numloops++ == maxloops) { break; } + } + PulseLength = (numloops *1000) / LoopsPerMilli; + if (PulseLength < RFSNS_MIN_PULSE_LENGTH) { break; } + Ftoggle = !Ftoggle; + rfsns_raw_signal->Pulses[RawCodeLength++] = PulseLength / (unsigned long)rfsns_raw_signal->Multiply; + } + while(RawCodeLength < RFSNS_RAW_BUFFER_SIZE && numloops <= maxloops); + + if ((RawCodeLength >= RFSNS_MIN_RAW_PULSES) && (RawCodeLength < RFSNS_RAW_BUFFER_SIZE -1)) { + rfsns_raw_signal->Repeats = 0; + rfsns_raw_signal->Number = RawCodeLength -1; + rfsns_raw_signal->Pulses[rfsns_raw_signal->Number] = 0; + rfsns_raw_signal->Time = millis(); + return true; + } + else + rfsns_raw_signal->Number = 0; + } + + return false; +} + +#ifdef USE_THEO_V2 +# 149 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_37_rfsensor.ino" +#define RFSNS_THEOV2_MAX_CHANNEL 2 + +#define RFSNS_THEOV2_PULSECOUNT 114 +#define RFSNS_THEOV2_RF_PULSE_MID 1000 + +typedef struct { + uint32_t time; + int16_t temp; + uint16_t lux; + uint8_t volt; +} theo_v2_t1_t; + +typedef struct { + uint32_t time; + int16_t temp; + uint16_t hum; + uint8_t volt; +} theo_v2_t2_t; + +theo_v2_t1_t *rfsns_theo_v2_t1 = nullptr; +theo_v2_t2_t *rfsns_theo_v2_t2 = nullptr; + +void RfSnsInitTheoV2(void) +{ + rfsns_theo_v2_t1 = (theo_v2_t1_t*)malloc(RFSNS_THEOV2_MAX_CHANNEL * sizeof(theo_v2_t1_t)); + rfsns_theo_v2_t2 = (theo_v2_t2_t*)malloc(RFSNS_THEOV2_MAX_CHANNEL * sizeof(theo_v2_t2_t)); + rfsns_any_sensor++; +} + +void RfSnsAnalyzeTheov2(void) +{ + if (rfsns_raw_signal->Number != RFSNS_THEOV2_PULSECOUNT) { return; } + + uint8_t Checksum; + uint8_t Channel; + uint8_t Type; + uint8_t Voltage; + int Payload1; + int Payload2; + + uint8_t b, bytes, bits, id; + + uint8_t idx = 3; + uint8_t chksum = 0; + for (bytes = 0; bytes < 7; bytes++) { + b = 0; + for (bits = 0; bits <= 7; bits++) + { + if ((rfsns_raw_signal->Pulses[idx] * rfsns_raw_signal->Multiply) > RFSNS_THEOV2_RF_PULSE_MID) { + b |= 1 << bits; + } + idx += 2; + } + if (bytes > 0) { chksum += b; } + + switch (bytes) { + case 0: + Checksum = b; + break; + case 1: + id = b; + Channel = b & 0x7; + Type = (b >> 3) & 0x1f; + break; + case 2: + Voltage = b; + break; + case 3: + Payload1 = b; + break; + case 4: + Payload1 = (b << 8) | Payload1; + break; + case 5: + Payload2 = b; + break; + case 6: + Payload2 = (b << 8) | Payload2; + break; + } + } + + if (Checksum != chksum) { return; } + if ((Channel == 0) || (Channel > RFSNS_THEOV2_MAX_CHANNEL)) { return; } + Channel--; + + rfsns_raw_signal->Repeats = 1; + + int Payload3 = Voltage & 0x3f; + + switch (Type) { + case 1: + rfsns_theo_v2_t1[Channel].time = LocalTime(); + rfsns_theo_v2_t1[Channel].volt = Payload3; + rfsns_theo_v2_t1[Channel].temp = Payload1; + rfsns_theo_v2_t1[Channel].lux = Payload2; + break; + case 2: + rfsns_theo_v2_t2[Channel].time = LocalTime(); + rfsns_theo_v2_t2[Channel].volt = Payload3; + rfsns_theo_v2_t2[Channel].temp = Payload1; + rfsns_theo_v2_t2[Channel].hum = Payload2; + break; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: TheoV2, ChkCalc %d, Chksum %d, id %d, Type %d, Ch %d, Volt %d, BattLo %d, Pld1 %d, Pld2 %d"), + chksum, Checksum, id, Type, Channel +1, Payload3, (Voltage & 0x80) >> 7, Payload1, Payload2); +} + +void RfSnsTheoV2Show(bool json) +{ + bool sensor_once = false; + + for (uint32_t i = 0; i < RFSNS_THEOV2_MAX_CHANNEL; i++) { + if (rfsns_theo_v2_t1[i].time) { + char sensor[10]; + snprintf_P(sensor, sizeof(sensor), PSTR("TV2T1C%d"), i +1); + char voltage[33]; + dtostrfd((float)rfsns_theo_v2_t1[i].volt / 10, 1, voltage); + + if (rfsns_theo_v2_t1[i].time < LocalTime() - RFSNS_VALID_WINDOW) { + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_RFRECEIVED "\":\"%s\",\"" D_JSON_VOLTAGE "\":%s}"), + sensor, GetDT(rfsns_theo_v2_t1[i].time).c_str(), voltage); + } + } else { + char temperature[33]; + dtostrfd(ConvertTemp((float)rfsns_theo_v2_t1[i].temp / 100), Settings.flag2.temperature_resolution, temperature); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_VOLTAGE "\":%s}"), + sensor, temperature, rfsns_theo_v2_t1[i].lux, voltage); +#ifdef USE_DOMOTICZ + if ((0 == tele_period) && !sensor_once) { + DomoticzSensor(DZ_TEMP, temperature); + DomoticzSensor(DZ_ILLUMINANCE, rfsns_theo_v2_t1[i].lux); + sensor_once = true; + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, sensor, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, sensor, rfsns_theo_v2_t1[i].lux); +#endif + } + } + } + } + + sensor_once = false; + for (uint32_t i = 0; i < RFSNS_THEOV2_MAX_CHANNEL; i++) { + if (rfsns_theo_v2_t2[i].time) { + char sensor[10]; + snprintf_P(sensor, sizeof(sensor), PSTR("TV2T2C%d"), i +1); + char voltage[33]; + dtostrfd((float)rfsns_theo_v2_t2[i].volt / 10, 1, voltage); + + if (rfsns_theo_v2_t2[i].time < LocalTime() - RFSNS_VALID_WINDOW) { + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_RFRECEIVED" \":\"%s\",\"" D_JSON_VOLTAGE "\":%s}"), + sensor, GetDT(rfsns_theo_v2_t2[i].time).c_str(), voltage); + } + } else { + float temp = ConvertTemp((float)rfsns_theo_v2_t2[i].temp / 100); + float humi = ConvertHumidity((float)rfsns_theo_v2_t2[i].hum / 100); + char temperature[33]; + dtostrfd(temp, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(humi, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s,\"" D_JSON_VOLTAGE "\":%s}"), + sensor, temperature, humidity, voltage); + if ((0 == tele_period) && !sensor_once) { +#ifdef USE_DOMOTICZ + DomoticzTempHumSensor(temperature, humidity); +#endif +#ifdef USE_KNX + KnxSensor(KNX_TEMPERATURE, temp); + KnxSensor(KNX_HUMIDITY, humi); +#endif + sensor_once = true; + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, sensor, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, sensor, humidity); +#endif + } + } + } + } +} + +#endif + +#ifdef USE_ALECTO_V2 +# 392 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_37_rfsensor.ino" +#define RFSNS_DKW2012_PULSECOUNT 176 +#define RFSNS_ACH2010_MIN_PULSECOUNT 160 +#define RFSNS_ACH2010_MAX_PULSECOUNT 160 + +#define D_ALECTOV2 "AlectoV2" + +const char kAlectoV2Directions[] PROGMEM = D_TX20_NORTH "|" + D_TX20_NORTH D_TX20_NORTH D_TX20_EAST "|" + D_TX20_NORTH D_TX20_EAST "|" + D_TX20_EAST D_TX20_NORTH D_TX20_EAST "|" + D_TX20_EAST "|" + D_TX20_EAST D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH D_TX20_SOUTH D_TX20_EAST "|" + D_TX20_SOUTH "|" + D_TX20_SOUTH D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_WEST D_TX20_SOUTH D_TX20_WEST "|" + D_TX20_WEST "|" + D_TX20_WEST D_TX20_NORTH D_TX20_WEST "|" + D_TX20_NORTH D_TX20_WEST "|" + D_TX20_NORTH D_TX20_NORTH D_TX20_WEST; + +typedef struct { + uint32_t time; + float temp; + float rain; + float wind; + float gust; + uint8_t type; + uint8_t humi; + uint8_t wdir; +} alecto_v2_t; + +alecto_v2_t *rfsns_alecto_v2 = nullptr; +uint16_t rfsns_alecto_rain_base = 0; + +void RfSnsInitAlectoV2(void) +{ + rfsns_alecto_v2 = (alecto_v2_t*)malloc(sizeof(alecto_v2_t)); + rfsns_any_sensor++; +} + +void RfSnsAnalyzeAlectov2() +{ + if (!(((rfsns_raw_signal->Number >= RFSNS_ACH2010_MIN_PULSECOUNT) && + (rfsns_raw_signal->Number <= RFSNS_ACH2010_MAX_PULSECOUNT)) || (rfsns_raw_signal->Number == RFSNS_DKW2012_PULSECOUNT))) { return; } + + uint8_t c = 0; + uint8_t rfbit; + uint8_t data[9] = { 0 }; + uint8_t msgtype = 0; + uint8_t rc = 0; + int temp; + uint8_t checksum = 0; + uint8_t checksumcalc = 0; + uint8_t maxidx = 8; + unsigned long atime; + float factor; + char buf1[16]; + + if (rfsns_raw_signal->Number > RFSNS_ACH2010_MAX_PULSECOUNT) { maxidx = 9; } + + uint8_t idx = maxidx; + for (uint32_t x = rfsns_raw_signal->Number; x > 0; x = x-2) { + if (rfsns_raw_signal->Pulses[x-1] * rfsns_raw_signal->Multiply < 0x300) { + rfbit = 0x80; + } else { + rfbit = 0; + } + data[idx] = (data[idx] >> 1) | rfbit; + c++; + if (c == 8) { + if (idx == 0) { break; } + c = 0; + idx--; + } + } + + checksum = data[maxidx]; + checksumcalc = RfSnsAlectoCRC8(data, maxidx); + + msgtype = (data[0] >> 4) & 0xf; + rc = (data[0] << 4) | (data[1] >> 4); + + if (checksum != checksumcalc) { return; } + if ((msgtype != 10) && (msgtype != 5)) { return; } + + rfsns_raw_signal->Repeats = 1; + + + + + + factor = 1.22; + + + + + + rfsns_alecto_v2->time = LocalTime(); + rfsns_alecto_v2->type = (RFSNS_DKW2012_PULSECOUNT == rfsns_raw_signal->Number); + rfsns_alecto_v2->temp = (float)(((data[1] & 0x3) * 256 + data[2]) - 400) / 10; + rfsns_alecto_v2->humi = data[3]; + uint16_t rain = (data[6] * 256) + data[7]; + + if (rain < rfsns_alecto_rain_base) { rfsns_alecto_rain_base = rain; } + if (rfsns_alecto_rain_base > 0) { + rfsns_alecto_v2->rain += ((float)rain - rfsns_alecto_rain_base) * 0.30; + } + rfsns_alecto_rain_base = rain; + rfsns_alecto_v2->wind = (float)data[4] * factor; + rfsns_alecto_v2->gust = (float)data[5] * factor; + if (rfsns_alecto_v2->type) { + rfsns_alecto_v2->wdir = data[8] & 0xf; + } + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: " D_ALECTOV2 ", ChkCalc %d, Chksum %d, rc %d, Temp %d, Hum %d, Rain %d, Wind %d, Gust %d, Dir %d, Factor %s"), + checksumcalc, checksum, rc, ((data[1] & 0x3) * 256 + data[2]) - 400, data[3], (data[6] * 256) + data[7], data[4], data[5], data[8] & 0xf, dtostrfd(factor, 3, buf1)); +} + +void RfSnsAlectoResetRain(void) +{ + if ((RtcTime.hour == 0) && (RtcTime.minute == 0) && (RtcTime.second == 5)) { + rfsns_alecto_v2->rain = 0; + } +} + + + + + + + +uint8_t RfSnsAlectoCRC8(uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + while (len--) { + uint8_t inbyte = *addr++; + for (uint32_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x80; + crc <<= 1; + if (mix) { crc ^= 0x31; } + inbyte <<= 1; + } + } + return crc; +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_ALECTOV2[] PROGMEM = + "{s}" D_ALECTOV2 " " D_RAIN "{m}%s " D_UNIT_MILLIMETER "{e}" + "{s}" D_ALECTOV2 " " D_TX20_WIND_SPEED "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" + "{s}" D_ALECTOV2 " " D_TX20_WIND_SPEED_MAX "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}"; +const char HTTP_SNS_ALECTOV2_WDIR[] PROGMEM = + "{s}" D_ALECTOV2 " " D_TX20_WIND_DIRECTION "{m}%s{e}"; +#endif + +void RfSnsAlectoV2Show(bool json) +{ + if (rfsns_alecto_v2->time) { + if (rfsns_alecto_v2->time < LocalTime() - RFSNS_VALID_WINDOW) { + if (json) { + ResponseAppend_P(PSTR(",\"" D_ALECTOV2 "\":{\"" D_JSON_RFRECEIVED "\":\"%s\"}"), GetDT(rfsns_alecto_v2->time).c_str()); + } + } else { + float temp = ConvertTemp(rfsns_alecto_v2->temp); + char temperature[33]; + dtostrfd(temp, Settings.flag2.temperature_resolution, temperature); + float humi = ConvertHumidity((float)rfsns_alecto_v2->humi); + char humidity[33]; + dtostrfd(humi, Settings.flag2.humidity_resolution, humidity); + char rain[33]; + dtostrfd(rfsns_alecto_v2->rain, 2, rain); + char wind[33]; + dtostrfd(rfsns_alecto_v2->wind, 2, wind); + char gust[33]; + dtostrfd(rfsns_alecto_v2->gust, 2, gust); + char wdir[4]; + char direction[20]; + if (rfsns_alecto_v2->type) { + GetTextIndexed(wdir, sizeof(wdir), rfsns_alecto_v2->wdir, kAlectoV2Directions); + snprintf_P(direction, sizeof(direction), PSTR(",\"Direction\":\"%s\""), wdir); + } + + if (json) { + ResponseAppend_P(PSTR(",\"" D_ALECTOV2 "\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s,\"Rain\":%s,\"Wind\":%s,\"Gust\":%s%s}"), + temperature, humidity, rain, wind, gust, (rfsns_alecto_v2->type) ? direction : ""); + if (0 == tele_period) { +#ifdef USE_DOMOTICZ + + + + +#endif + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, D_ALECTOV2, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, D_ALECTOV2, humidity); + WSContentSend_PD(HTTP_SNS_ALECTOV2, rain, wind, gust); + if (rfsns_alecto_v2->type) { + WSContentSend_PD(HTTP_SNS_ALECTOV2_WDIR, wdir); + } +#endif + } + } + } +} +#endif + +void RfSnsInit(void) +{ + rfsns_raw_signal = (raw_signal_t*)(malloc(sizeof(raw_signal_t))); + if (rfsns_raw_signal) { + memset(rfsns_raw_signal, 0, sizeof(raw_signal_t)); +#ifdef USE_THEO_V2 + RfSnsInitTheoV2(); +#endif +#ifdef USE_ALECTO_V2 + RfSnsInitAlectoV2(); +#endif + if (rfsns_any_sensor) { + rfsns_rf_bit = digitalPinToBitMask(pin[GPIO_RF_SENSOR]); + rfsns_rf_port = digitalPinToPort(pin[GPIO_RF_SENSOR]); + pinMode(pin[GPIO_RF_SENSOR], INPUT); + } else { + free(rfsns_raw_signal); + rfsns_raw_signal = nullptr; + } + } +} + +void RfSnsAnalyzeRawSignal(void) +{ + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: Pulses %d"), (int)rfsns_raw_signal->Number); + +#ifdef USE_THEO_V2 + RfSnsAnalyzeTheov2(); +#endif +#ifdef USE_ALECTO_V2 + RfSnsAnalyzeAlectov2(); +#endif +} + +void RfSnsEverySecond(void) +{ +#ifdef USE_ALECTO_V2 + RfSnsAlectoResetRain(); +#endif +} + +void RfSnsShow(bool json) +{ +#ifdef USE_THEO_V2 + RfSnsTheoV2Show(json); +#endif +#ifdef USE_ALECTO_V2 + RfSnsAlectoV2Show(json); +#endif +} + + + + + +bool Xsns37(uint8_t function) +{ + bool result = false; + + if ((pin[GPIO_RF_SENSOR] < 99) && (FUNC_INIT == function)) { + RfSnsInit(); + } + else if (rfsns_raw_signal) { + switch (function) { + case FUNC_LOOP: + if ((*portInputRegister(rfsns_rf_port) &rfsns_rf_bit) == rfsns_rf_bit) { + if (RfSnsFetchSignal(pin[GPIO_RF_SENSOR], HIGH)) { + RfSnsAnalyzeRawSignal(); + } + } + sleep = 0; + break; + case FUNC_EVERY_SECOND: + RfSnsEverySecond(); + break; + case FUNC_JSON_APPEND: + RfSnsShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + RfSnsShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_38_az7798.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_38_az7798.ino" +#ifdef USE_AZ7798 + +#define XSNS_38 38 +# 112 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_38_az7798.ino" +#include + +#ifndef CO2_LOW +#define CO2_LOW 800 +#endif +#ifndef CO2_HIGH +#define CO2_HIGH 1200 +#endif + +#define AZ_READ_TIMEOUT 400 + +#define AZ_CLOCK_UPDATE_INTERVAL (24UL * 60 * 60) +#define AZ_EPOCH (946684800UL) + +TasmotaSerial *AzSerial; + +const char ktype[] = "AZ7798"; +uint8_t az_type = 1; +uint16_t az_co2 = 0; +double az_temperature = 0; +double az_humidity = 0; +uint8_t az_received = 0; +uint8_t az_state = 0; +unsigned long az_clock_update = 10; + + + +void AzEverySecond(void) +{ + unsigned long start = millis(); + + az_state++; + if (5 == az_state) { + az_state = 0; + + AzSerial->flush(); + AzSerial->write(":\r", 2); + az_received = 0; + + uint8_t az_response[32]; + uint8_t counter = 0; + uint8_t i, j; + uint8_t response_substr[16]; + + do { + if (AzSerial->available() > 0) { + az_response[counter] = AzSerial->read(); + if(az_response[counter] == 0x0d) { az_received = 1; } + counter++; + } else { + delay(5); + } + } while(((millis() - start) < AZ_READ_TIMEOUT) && (counter < sizeof(az_response)) && !az_received); + + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, az_response, counter); + + if (!az_received) { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 comms timeout")); + return; + } + + i = 0; + while((az_response[i] != 'T') && (i < counter)) {i++;} + if(az_response[i] != 'T') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find start of response")); + return; + } + i++; + j = 0; + + while((az_response[i] != 'C') && (az_response[i] != 'F') && (i < counter)) { + response_substr[j++] = az_response[i++]; + } + if((az_response[i] != 'C') && (az_response[i] != 'F')){ + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of temperature")); + return; + } + response_substr[j] = 0; + az_temperature = CharToFloat((char*)response_substr); + if(az_response[i] == 'C') { + az_temperature = ConvertTemp((float)az_temperature); + } else { + az_temperature = ConvertTemp((az_temperature - 32) / 1.8); + } + i++; + if(az_response[i] != ':') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error first delimiter")); + return; + } + i++; + if(az_response[i] != 'C') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of CO2")); + return; + } + i++; + j = 0; + + while((az_response[i] != 'p') && (i < counter)) { + response_substr[j++] = az_response[i++]; + } + if(az_response[i] != 'p') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of CO2")); + return; + } + response_substr[j] = 0; + az_co2 = atoi((char*)response_substr); + LightSetSignal(CO2_LOW, CO2_HIGH, az_co2); + i += 3; + if(az_response[i] != ':') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error second delimiter")); + return; + } + i++; + if(az_response[i] != 'H') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of humidity")); + return; + } + i++; + j = 0; + + while((az_response[i] != '%') && (i < counter)) { + response_substr[j++] = az_response[i++]; + } + if(az_response[i] != '%') { + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of humidity")); + return; + } + response_substr[j] = 0; + az_humidity = ConvertHumidity(CharToFloat((char*)response_substr)); + } + + + if ((az_clock_update == 0) && (LocalTime() > AZ_EPOCH)) { + char tmpString[16]; + sprintf(tmpString, "C %d\r", (int)(LocalTime() - AZ_EPOCH)); + AzSerial->write(tmpString); + + do { + if (AzSerial->available() > 0) { + if(AzSerial->read() == 0x0d) { break; } + } else { + delay(5); + } + } while(((millis() - start) < AZ_READ_TIMEOUT)); + az_clock_update = AZ_CLOCK_UPDATE_INTERVAL; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 clock updated")); + } else { + az_clock_update--; + } +} + + + +void AzInit(void) +{ + az_type = 0; + if ((pin[GPIO_AZ_RXD] < 99) && (pin[GPIO_AZ_TXD] < 99)) { + AzSerial = new TasmotaSerial(pin[GPIO_AZ_RXD], pin[GPIO_AZ_TXD], 1); + if (AzSerial->begin(9600)) { + if (AzSerial->hardwareSerial()) { ClaimSerial(); } + az_type = 1; + } + } +} + +void AzShow(bool json) +{ + char temperature[33]; + dtostrfd(az_temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(az_humidity, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"), ktype, az_co2, temperature, humidity); +#ifdef USE_DOMOTICZ + if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, az_co2); +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CO2, ktype, az_co2); + WSContentSend_PD(HTTP_SNS_TEMP, ktype, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, ktype, humidity); +#endif + } +} + + + + + +bool Xsns38(uint8_t function) +{ + bool result = false; + + if(az_type){ + switch (function) { + case FUNC_INIT: + AzInit(); + break; + case FUNC_EVERY_SECOND: + AzEverySecond(); + break; + case FUNC_JSON_APPEND: + AzShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + AzShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_39_max31855.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_39_max31855.ino" +#ifdef USE_MAX31855 + +#define XSNS_39 39 + +bool initialized = false; + +struct MAX31855_ResultStruct{ + uint8_t ErrorCode; + float ProbeTemperature; + float ReferenceTemperature; +} MAX31855_Result; + +void MAX31855_Init(void){ + if(initialized) + return; + + + pinMode(pin[GPIO_MAX31855CS], OUTPUT); + pinMode(pin[GPIO_MAX31855CLK], OUTPUT); + pinMode(pin[GPIO_MAX31855DO], INPUT); + + + digitalWrite(pin[GPIO_MAX31855CS], HIGH); + digitalWrite(pin[GPIO_MAX31855CLK], LOW); + + initialized = true; +} + + + + + +void MAX31855_GetResult(void){ + int32_t RawData = MAX31855_ShiftIn(32); + uint8_t probeerror = RawData & 0x7; + + MAX31855_Result.ErrorCode = probeerror; + MAX31855_Result.ReferenceTemperature = MAX31855_GetReferenceTemperature(RawData); + if(probeerror) + MAX31855_Result.ProbeTemperature = NAN; + else + MAX31855_Result.ProbeTemperature = MAX31855_GetProbeTemperature(RawData); +} + + + + + + +float MAX31855_GetProbeTemperature(int32_t RawData){ + if(RawData & 0x80000000) + RawData = (RawData >> 18) | 0xFFFFC000; + else + RawData >>= 18; + + float result = (RawData * 0.25); + + return ConvertTemp(result); +} + + + + + +float MAX31855_GetReferenceTemperature(int32_t RawData){ + if(RawData & 0x8000) + RawData = (RawData >> 4) | 0xFFFFF000; + else + RawData = (RawData >> 4) & 0x00000FFF; + + float result = (RawData * 0.0625); + + return ConvertTemp(result); +} + + + + + +int32_t MAX31855_ShiftIn(uint8_t Length){ + int32_t dataIn = 0; + + digitalWrite(pin[GPIO_MAX31855CS], LOW); + delayMicroseconds(1); + + for (uint32_t i = 0; i < Length; i++) + { + digitalWrite(pin[GPIO_MAX31855CLK], LOW); + delayMicroseconds(1); + dataIn <<= 1; + if(digitalRead(pin[GPIO_MAX31855DO])) + dataIn |= 1; + digitalWrite(pin[GPIO_MAX31855CLK], HIGH); + delayMicroseconds(1); + } + + digitalWrite(pin[GPIO_MAX31855CS], HIGH); + digitalWrite(pin[GPIO_MAX31855CLK], LOW); + return dataIn; +} + +void MAX31855_Show(bool Json){ + char probetemp[33]; + char referencetemp[33]; + dtostrfd(MAX31855_Result.ProbeTemperature, Settings.flag2.temperature_resolution, probetemp); + dtostrfd(MAX31855_Result.ReferenceTemperature, Settings.flag2.temperature_resolution, referencetemp); + + if(Json){ + ResponseAppend_P(PSTR(",\"MAX31855\":{\"" D_JSON_PROBETEMPERATURE "\":%s,\"" D_JSON_REFERENCETEMPERATURE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ + probetemp, referencetemp, MAX31855_Result.ErrorCode); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_TEMP, probetemp); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, MAX31855_Result.ProbeTemperature); + } +#endif + } else { +#ifdef USE_WEBSERVER + WSContentSend_PD(HTTP_SNS_TEMP, "MAX31855", probetemp, TempUnit()); +#endif + } +} + + + + + +bool Xsns39(uint8_t function) +{ + bool result = false; + if((pin[GPIO_MAX31855CS] < 99) && (pin[GPIO_MAX31855CLK] < 99) && (pin[GPIO_MAX31855DO] < 99)){ + + switch (function) { + case FUNC_INIT: + MAX31855_Init(); + break; + case FUNC_EVERY_SECOND: + MAX31855_GetResult(); + break; + case FUNC_JSON_APPEND: + MAX31855_Show(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MAX31855_Show(false); + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_40_pn532.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_40_pn532.ino" +#ifdef USE_PN532_HSU + +#define XSNS_40 40 + +#include + +TasmotaSerial *PN532_Serial; + +#define PN532_INVALID_ACK -1 +#define PN532_TIMEOUT -2 +#define PN532_INVALID_FRAME -3 +#define PN532_NO_SPACE -4 + +#define PN532_PREAMBLE 0x00 +#define PN532_STARTCODE1 0x00 +#define PN532_STARTCODE2 0xFF +#define PN532_POSTAMBLE 0x00 + +#define PN532_HOSTTOPN532 0xD4 +#define PN532_PN532TOHOST 0xD5 + +#define PN532_ACK_WAIT_TIME 0x0A + +#define PN532_COMMAND_GETFIRMWAREVERSION 0x02 +#define PN532_COMMAND_SAMCONFIGURATION 0x14 +#define PN532_COMMAND_RFCONFIGURATION 0x32 +#define PN532_COMMAND_INDATAEXCHANGE 0x40 +#define PN532_COMMAND_INLISTPASSIVETARGET 0x4A + +#define PN532_MIFARE_ISO14443A 0x00 +#define MIFARE_CMD_READ 0x30 +#define MIFARE_CMD_AUTH_A 0x60 +#define MIFARE_CMD_AUTH_B 0x61 +#define MIFARE_CMD_WRITE 0xA0 + +uint8_t pn532_model = 0; +uint8_t pn532_command = 0; +uint8_t pn532_scantimer = 0; + +uint8_t pn532_packetbuffer[64]; + +#ifdef USE_PN532_DATA_FUNCTION +uint8_t pn532_function = 0; +uint8_t pn532_newdata[16]; +uint8_t pn532_newdata_len = 0; +#endif + +void PN532_Init(void) +{ + if ((pin[GPIO_PN532_RXD] < 99) && (pin[GPIO_PN532_TXD] < 99)) { + PN532_Serial = new TasmotaSerial(pin[GPIO_PN532_RXD], pin[GPIO_PN532_TXD], 1); + if (PN532_Serial->begin(115200)) { + if (PN532_Serial->hardwareSerial()) { ClaimSerial(); } + PN532_wakeup(); + uint32_t ver = PN532_getFirmwareVersion(); + if (ver) { + PN532_setPassiveActivationRetries(0xFF); + PN532_SAMConfig(); + pn532_model = 1; + AddLog_P2(LOG_LEVEL_INFO,"NFC: PN532 NFC Reader detected (V%u.%u)",(ver>>16) & 0xFF, (ver>>8) & 0xFF); + } + } + } +} + +int8_t PN532_receive(uint8_t *buf, int len, uint16_t timeout) +{ + int read_bytes = 0; + int ret; + unsigned long start_millis; + while (read_bytes < len) { + start_millis = millis(); + do { + ret = PN532_Serial->read(); + if (ret >= 0) { + break; + } + } while((timeout == 0) || ((millis()- start_millis ) < timeout)); + + if (ret < 0) { + if (read_bytes) { + return read_bytes; + } else { + return PN532_TIMEOUT; + } + } + buf[read_bytes] = (uint8_t)ret; + read_bytes++; + } + return read_bytes; +} + +int8_t PN532_readAckFrame(void) +{ + const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; + uint8_t ackBuf[sizeof(PN532_ACK)]; + + if (PN532_receive(ackBuf, sizeof(PN532_ACK), PN532_ACK_WAIT_TIME) <= 0) { + return PN532_TIMEOUT; + } + + if (memcmp(&ackBuf, &PN532_ACK, sizeof(PN532_ACK))) { + return PN532_INVALID_ACK; + } + return 0; +} + +int8_t PN532_writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) +{ + + PN532_Serial->flush(); + + pn532_command = header[0]; + PN532_Serial->write((uint8_t)PN532_PREAMBLE); + PN532_Serial->write((uint8_t)PN532_STARTCODE1); + PN532_Serial->write(PN532_STARTCODE2); + + uint8_t length = hlen + blen + 1; + PN532_Serial->write(length); + PN532_Serial->write(~length + 1); + + PN532_Serial->write(PN532_HOSTTOPN532); + uint8_t sum = PN532_HOSTTOPN532; + + PN532_Serial->write(header, hlen); + for (uint32_t i = 0; i < hlen; i++) { + sum += header[i]; + } + + PN532_Serial->write(body, blen); + for (uint32_t i = 0; i < blen; i++) { + sum += body[i]; + } + + uint8_t checksum = ~sum + 1; + PN532_Serial->write(checksum); + PN532_Serial->write((uint8_t)PN532_POSTAMBLE); + + return PN532_readAckFrame(); +} + +int16_t PN532_readResponse(uint8_t buf[], uint8_t len, uint16_t timeout = 50) +{ + uint8_t tmp[3]; + + + if (PN532_receive(tmp, 3, timeout)<=0) { + return PN532_TIMEOUT; + } + if (0 != tmp[0] || 0!= tmp[1] || 0xFF != tmp[2]) { + return PN532_INVALID_FRAME; + } + + + uint8_t length[2]; + if (PN532_receive(length, 2, timeout) <= 0) { + return PN532_TIMEOUT; + } + + if (0 != (uint8_t)(length[0] + length[1])) { + return PN532_INVALID_FRAME; + } + length[0] -= 2; + if (length[0] > len) { + return PN532_NO_SPACE; + } + + + uint8_t cmd = pn532_command + 1; + if (PN532_receive(tmp, 2, timeout) <= 0) { + return PN532_TIMEOUT; + } + if (PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]) { + return PN532_INVALID_FRAME; + } + + if (PN532_receive(buf, length[0], timeout) != length[0]) { + return PN532_TIMEOUT; + } + + uint8_t sum = PN532_PN532TOHOST + cmd; + for (uint32_t i=0; i status) { + return 0; + } + + response = pn532_packetbuffer[0]; + response <<= 8; + response |= pn532_packetbuffer[1]; + response <<= 8; + response |= pn532_packetbuffer[2]; + response <<= 8; + response |= pn532_packetbuffer[3]; + + return response; +} + +void PN532_wakeup(void) +{ + uint8_t wakeup[5] = {0x55, 0x55, 0, 0, 0 }; + PN532_Serial->write(wakeup,sizeof(wakeup)); + + + PN532_Serial->flush(); +} + +bool PN532_readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, uint16_t timeout = 50) +{ + pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = cardbaudrate; + if (PN532_writeCommand(pn532_packetbuffer, 3)) { + return 0x0; + } + + if (PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout) < 0) { + return 0x0; + } +# 274 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_40_pn532.ino" + if (pn532_packetbuffer[0] != 1) { + return 0; + } + + uint16_t sens_res = pn532_packetbuffer[2]; + sens_res <<= 8; + sens_res |= pn532_packetbuffer[3]; + + + *uidLength = pn532_packetbuffer[5]; + + for (uint32_t i = 0; i < pn532_packetbuffer[5]; i++) { + uid[i] = pn532_packetbuffer[6 + i]; + } + + return 1; +} + +bool PN532_setPassiveActivationRetries(uint8_t maxRetries) +{ + pn532_packetbuffer[0] = PN532_COMMAND_RFCONFIGURATION; + pn532_packetbuffer[1] = 5; + pn532_packetbuffer[2] = 0xFF; + pn532_packetbuffer[3] = 0x01; + pn532_packetbuffer[4] = maxRetries; + if (PN532_writeCommand(pn532_packetbuffer, 5)) { + return 0; + } + return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +bool PN532_SAMConfig(void) +{ + pn532_packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION; + pn532_packetbuffer[1] = 0x01; + pn532_packetbuffer[2] = 0x14; + pn532_packetbuffer[3] = 0x00; + if (PN532_writeCommand(pn532_packetbuffer, 4)) { + return false; + } + return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +#ifdef USE_PN532_DATA_FUNCTION + +uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData) +{ + uint8_t i; + uint8_t _key[6]; + uint8_t _uid[7]; + uint8_t _uidLen; + + + memcpy(&_key, keyData, 6); + memcpy(&_uid, uid, uidLen); + _uidLen = uidLen; + + + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_B : MIFARE_CMD_AUTH_A; + pn532_packetbuffer[3] = blockNumber; + memcpy(&pn532_packetbuffer[4], &_key, 6); + for (i = 0; i < _uidLen; i++) { + pn532_packetbuffer[10 + i] = _uid[i]; + } + + if (PN532_writeCommand(pn532_packetbuffer, 10 + _uidLen)) { return 0; } + + + PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + + + + if (pn532_packetbuffer[0] != 0x00) { + + return 0; + } + + return 1; +} + +uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data) +{ + + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = MIFARE_CMD_READ; + pn532_packetbuffer[3] = blockNumber; + + + if (PN532_writeCommand(pn532_packetbuffer, 4)) { + return 0; + } + + + PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer)); + + + if (pn532_packetbuffer[0] != 0x00) { + return 0; + } + + + + memcpy (data, &pn532_packetbuffer[1], 16); + + return 1; +} + +uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data) +{ + + pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; + pn532_packetbuffer[1] = 1; + pn532_packetbuffer[2] = MIFARE_CMD_WRITE; + pn532_packetbuffer[3] = blockNumber; + memcpy(&pn532_packetbuffer[4], data, 16); + + + if (PN532_writeCommand(pn532_packetbuffer, 20)) { + return 0; + } + + + return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); +} + +#endif + +void PN532_ScanForTag(void) +{ + if (!pn532_model) { return; } + uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; + uint8_t uid_len = 0; + uint8_t card_data[16]; + bool erase_success = false; + bool set_success = false; + if (PN532_readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uid_len)) { + char uids[15]; + +#ifdef USE_PN532_DATA_FUNCTION + char card_datas[34]; +#endif + + ToHex_P((unsigned char*)uid, uid_len, uids, sizeof(uids)); + +#ifdef USE_PN532_DATA_FUNCTION + if (uid_len == 4) { + uint8_t keyuniversal[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + if (mifareclassic_AuthenticateBlock (uid, uid_len, 1, 1, keyuniversal)) { + if (mifareclassic_ReadDataBlock(1, card_data)) { +#ifdef USE_PN532_DATA_RAW + memcpy(&card_datas,&card_data,sizeof(card_data)); +#else + for (uint32_t i = 0;i < sizeof(card_data);i++) { + if ((isalpha(card_data[i])) || ((isdigit(card_data[i])))) { + card_datas[i] = char(card_data[i]); + } else { + card_datas[i] = '\0'; + } + } +#endif + } + if (pn532_function == 1) { + for (uint32_t i = 0;i<16;i++) { + card_data[i] = 0x00; + } + if (mifareclassic_WriteDataBlock(1, card_data)) { + erase_success = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase success")); + memcpy(&card_datas,&card_data,sizeof(card_data)); + } + } + if (pn532_function == 2) { +#ifdef USE_PN532_DATA_RAW + memcpy(&card_data,&pn532_newdata,sizeof(card_data)); + if (mifareclassic_WriteDataBlock(1, card_data)) { + set_success = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); + memcpy(&card_datas,&card_data,sizeof(card_data)); + } +#else + bool IsAlphaNumeric = true; + for (uint32_t i = 0;i < pn532_newdata_len;i++) { + if ((!isalpha(pn532_newdata[i])) && (!isdigit(pn532_newdata[i]))) { + IsAlphaNumeric = false; + } + } + if (IsAlphaNumeric) { + memcpy(&card_data,&pn532_newdata,pn532_newdata_len); + card_data[pn532_newdata_len] = '\0'; + if (mifareclassic_WriteDataBlock(1, card_data)) { + set_success = true; + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); + memcpy(&card_datas,&card_data,sizeof(card_data)); + } + } else { + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data must be alphanumeric")); + } +#endif + } + } else { + sprintf(card_datas,"AUTHFAIL"); + } + } + switch (pn532_function) { + case 0x01: + if (!erase_success) { + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase fail - exiting erase mode")); + } + break; + case 0x02: + if (!set_success) { + AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Write failed - exiting set mode")); + } + default: + break; + } + pn532_function = 0; +#endif + +#ifdef USE_PN532_DATA_FUNCTION + ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\", \"DATA\":\"%s\"}}"), uids, card_datas); +#else + ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\"}}"), uids); +#endif + + MqttPublishTeleSensor(); + +#ifdef USE_PN532_CAUSE_EVENTS + + char command[71]; +#ifdef USE_PN532_DATA_FUNCTION + sprintf(command,"backlog event PN532_UID=%s;event PN532_DATA=%s",uids,card_datas); +#else + sprintf(command,"event PN532_UID=%s",uids); +#endif + ExecuteCommand(command, SRC_RULE); +#endif + + pn532_scantimer = 7; + } +} + +#ifdef USE_PN532_DATA_FUNCTION + +bool PN532_Command(void) +{ + bool serviced = true; + uint8_t paramcount = 0; + if (XdrvMailbox.data_len > 0) { + paramcount=1; + } else { + serviced = false; + return serviced; + } + char sub_string[XdrvMailbox.data_len]; + char sub_string_tmp[XdrvMailbox.data_len]; + for (uint32_t ca=0;ca 1) { + if (XdrvMailbox.data[XdrvMailbox.data_len-1] == ',') { + serviced = false; + return serviced; + } + sprintf(sub_string_tmp,subStr(sub_string, XdrvMailbox.data, ",", 2)); + pn532_newdata_len = strlen(sub_string_tmp); + if (pn532_newdata_len > 15) { pn532_newdata_len = 15; } + memcpy(&pn532_newdata,&sub_string_tmp,pn532_newdata_len); + pn532_newdata[pn532_newdata_len] = 0x00; + pn532_function = 2; + AddLog_P2(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Next scanned tag data block 1 will be set to '%s'"), pn532_newdata); + ResponseTime_P(PSTR(",\"PN532\":{\"COMMAND\":\"S\"}}")); + return serviced; + } + } +} + +#endif + +bool Xsns40(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_INIT: + PN532_Init(); + result = true; + break; + case FUNC_EVERY_50_MSECOND: + break; + case FUNC_EVERY_100_MSECOND: + break; + case FUNC_EVERY_250_MSECOND: + if (pn532_scantimer > 0) { + pn532_scantimer--; + } else { + PN532_ScanForTag(); + } + break; + case FUNC_EVERY_SECOND: + break; +#ifdef USE_PN532_DATA_FUNCTION + case FUNC_COMMAND_SENSOR: + if (XSNS_40 == XdrvMailbox.index) { + result = PN532_Command(); + } + break; +#endif + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_41_max44009.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_41_max44009.ino" +#ifdef USE_I2C +#ifdef USE_MAX44009 + + + + + + +#define XSNS_41 41 +#define XI2C_28 28 + +#define MAX44009_ADDR1 0x4A +#define MAX44009_ADDR2 0x4B +#define MAX44009_NO_REGISTERS 8 +#define REG_CONFIG 0x02 +#define REG_LUMINANCE 0x03 +#define REG_LOWER_THRESHOLD 0x06 +#define REG_THRESHOLD_TIMER 0x07 + +#define MAX44009_CONTINUOUS_AUTO_MODE 0x80 + +uint8_t max44009_address; +uint8_t max44009_addresses[] = { MAX44009_ADDR1, MAX44009_ADDR2, 0 }; +uint8_t max44009_found = 0; +uint8_t max44009_valid = 0; +float max44009_illuminance = 0; +char max44009_types[] = "MAX44009"; + +bool Max4409Read_lum(void) +{ + max44009_valid = 0; + uint8_t regdata[2]; + + + if (I2cValidRead16((uint16_t *)®data, max44009_address, REG_LUMINANCE)) { + int exponent = (regdata[0] & 0xF0) >> 4; + int mantissa = ((regdata[0] & 0x0F) << 4) | (regdata[1] & 0x0F); + max44009_illuminance = (float)(((0x00000001 << exponent) * (float)mantissa) * 0.045); + max44009_valid = 1; + return true; + } else { + return false; + } +} + + + +void Max4409Detect(void) +{ + uint8_t buffer1; + uint8_t buffer2; + for (uint32_t i = 0; 0 != max44009_addresses[i]; i++) { + + max44009_address = max44009_addresses[i]; + if (I2cActive(max44009_address)) { continue; } + + if ((I2cValidRead8(&buffer1, max44009_address, REG_LOWER_THRESHOLD)) && + (I2cValidRead8(&buffer2, max44009_address, REG_THRESHOLD_TIMER))) { + + if ((0x00 == buffer1) && + (0xFF == buffer2)) { + + + + Wire.beginTransmission(max44009_address); + + + Wire.write(REG_CONFIG); + Wire.write(MAX44009_CONTINUOUS_AUTO_MODE); + if (0 == Wire.endTransmission()) { + I2cSetActiveFound(max44009_address, max44009_types); + max44009_found = 1; + break; + } + } + } + } +} + +void Max4409EverySecond(void) +{ + Max4409Read_lum(); +} + +void Max4409Show(bool json) +{ + char illum_str[8]; + + if (max44009_valid) { + + + + uint8_t prec = 0; + if (10 > max44009_illuminance ) { + prec = 3; + } else if (100 > max44009_illuminance) { + prec = 2; + } else if (1000 > max44009_illuminance) { + prec = 1; + } + dtostrf(max44009_illuminance, sizeof(illum_str) -1, prec, illum_str); + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%s}"), max44009_types, illum_str); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_ILLUMINANCE, illum_str); + } +#endif +#ifdef USE_WEBSERVER + } else { + + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, max44009_types, (int)max44009_illuminance); +#endif + } + } +} + + + + + +bool Xsns41(uint8_t function) +{ + if (!I2cEnabled(XI2C_28)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Max4409Detect(); + } + else if (max44009_found) { + switch (function) { + case FUNC_EVERY_SECOND: + Max4409EverySecond(); + break; + case FUNC_JSON_APPEND: + Max4409Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Max4409Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_42_scd30.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_42_scd30.ino" +#ifdef USE_I2C +#ifdef USE_SCD30 + +#define XSNS_42 42 +#define XI2C_29 29 + + + +#define SCD30_ADDRESS 0x61 + +#define SCD30_MAX_MISSED_READS 3 +#define SCD30_STATE_NO_ERROR 0 +#define SCD30_STATE_ERROR_DATA_CRC 1 +#define SCD30_STATE_ERROR_READ_MEAS 2 +#define SCD30_STATE_ERROR_SOFT_RESET 3 +#define SCD30_STATE_ERROR_I2C_RESET 4 +#define SCD30_STATE_ERROR_UNKNOWN 5 + +#include "Arduino.h" +#include + +#define D_CMND_SCD30 "SCD30" + +const char S_JSON_SCD30_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d}"; +const char S_JSON_SCD30_COMMAND_NFW_VALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d.%d}"; +const char S_JSON_SCD30_COMMAND[] PROGMEM = "{\"" D_CMND_SCD30 "%s\"}"; +const char kSCD30_Commands[] PROGMEM = "Alt|Auto|Cal|FW|Int|Pres|TOff"; + + + + + +enum SCD30_Commands { + CMND_SCD30_ALTITUDE, + CMND_SCD30_AUTOMODE, + CMND_SCD30_CALIBRATE, + CMND_SCD30_FW, + CMND_SCD30_INTERVAL, + CMND_SCD30_PRESSURE, + CMND_SCD30_TEMPOFFSET +}; + +FrogmoreScd30 scd30; + +bool scd30Found = false; +bool scd30IsDataValid = false; +int scd30ErrorState = SCD30_STATE_NO_ERROR; +uint16_t scd30Interval_sec; +int scd30Loop_count = 0; +int scd30DataNotAvailable_count = 0; +int scd30GoodMeas_count = 0; +int scd30Reset_count = 0; +int scd30CrcError_count = 0; +int scd30Co2Zero_count = 0; +int i2cReset_count = 0; +uint16_t scd30_CO2 = 0; +uint16_t scd30_CO2EAvg = 0; +float scd30_Humid = 0.0; +float scd30_Temp = 0.0; + +void Scd30Detect(void) +{ + if (I2cActive(SCD30_ADDRESS)) { return; } + + scd30.begin(); + + uint8_t major = 0; + uint8_t minor = 0; + if (scd30.getFirmwareVersion(&major, &minor)) { return; } + uint16_t interval_sec; + if (scd30.getMeasurementInterval(&scd30Interval_sec)) { return; } + if (scd30.beginMeasuring()) { return; } + + I2cSetActiveFound(SCD30_ADDRESS, "SCD30"); + scd30Found = true; + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SCD: FW v%d.%d"), major, minor); +} + + +void Scd30Update(void) +{ + scd30Loop_count++; + if (scd30Loop_count > (scd30Interval_sec - 1)) { + int error = 0; + switch (scd30ErrorState) { + case SCD30_STATE_NO_ERROR: { + error = scd30.readMeasurement(&scd30_CO2, &scd30_CO2EAvg, &scd30_Temp, &scd30_Humid); + switch (error) { + case ERROR_SCD30_NO_ERROR: + scd30Loop_count = 0; + scd30IsDataValid = true; + scd30GoodMeas_count++; + break; + + case ERROR_SCD30_NO_DATA: + scd30DataNotAvailable_count++; + break; + + case ERROR_SCD30_CRC_ERROR: + scd30ErrorState = SCD30_STATE_ERROR_DATA_CRC; + scd30CrcError_count++; +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: CRC error, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld"), + scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); +#endif + break; + + case ERROR_SCD30_CO2_ZERO: + scd30Co2Zero_count++; +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: CO2 zero, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld"), + scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); +#endif + break; + + default: { + scd30ErrorState = SCD30_STATE_ERROR_READ_MEAS; +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: Update: ReadMeasurement error: 0x%lX, counter: %ld"), error, scd30Loop_count); +#endif + return; + } + break; + } + } + break; + + case SCD30_STATE_ERROR_DATA_CRC: { + +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), + scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: got CRC error, try again, counter: %ld"), scd30Loop_count); +#endif + scd30ErrorState = ERROR_SCD30_NO_ERROR; + } + break; + + case SCD30_STATE_ERROR_READ_MEAS: { + +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), + scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: not answering, sending soft reset, counter: %ld"), scd30Loop_count); +#endif + scd30Reset_count++; + error = scd30.softReset(); + if (error) { +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: resetting got error: 0x%lX"), error); +#endif + error >>= 8; + if (error == 4) { + scd30ErrorState = SCD30_STATE_ERROR_SOFT_RESET; + } else { + scd30ErrorState = SCD30_STATE_ERROR_UNKNOWN; + } + } else { + scd30ErrorState = ERROR_SCD30_NO_ERROR; + } + } + break; + + case SCD30_STATE_ERROR_SOFT_RESET: { + +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), + scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); + AddLog_P(LOG_LEVEL_ERROR, PSTR("SCD30: clearing i2c bus")); +#endif + i2cReset_count++; + error = scd30.clearI2CBus(); + if (error) { + scd30ErrorState = SCD30_STATE_ERROR_I2C_RESET; +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: error clearing i2c bus: 0x%lX"), error); +#endif + } else { + scd30ErrorState = ERROR_SCD30_NO_ERROR; + } + } + break; + + default: { + +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: unknown error state: 0x%lX"), scd30ErrorState); +#endif + scd30ErrorState = SCD30_STATE_ERROR_SOFT_RESET; + } + } + + if (scd30Loop_count > (SCD30_MAX_MISSED_READS * scd30Interval_sec)) { + scd30IsDataValid = false; + } + } +} + + +int Scd30GetCommand(int command_code, uint16_t *pvalue) +{ + switch (command_code) + { + case CMND_SCD30_ALTITUDE: + return scd30.getAltitudeCompensation(pvalue); + break; + + case CMND_SCD30_AUTOMODE: + return scd30.getCalibrationType(pvalue); + break; + + case CMND_SCD30_CALIBRATE: + return scd30.getForcedRecalibrationFactor(pvalue); + break; + + case CMND_SCD30_INTERVAL: + return scd30.getMeasurementInterval(pvalue); + break; + + case CMND_SCD30_PRESSURE: + return scd30.getAmbientPressure(pvalue); + break; + + case CMND_SCD30_TEMPOFFSET: + return scd30.getTemperatureOffset(pvalue); + break; + + default: + + break; + } +} + +int Scd30SetCommand(int command_code, uint16_t value) +{ + switch (command_code) + { + case CMND_SCD30_ALTITUDE: + return scd30.setAltitudeCompensation(value); + break; + + case CMND_SCD30_AUTOMODE: + return scd30.setCalibrationType(value); + break; + + case CMND_SCD30_CALIBRATE: + return scd30.setForcedRecalibrationFactor(value); + break; + + case CMND_SCD30_INTERVAL: + { + int error = scd30.setMeasurementInterval(value); + if (!error) + { + scd30Interval_sec = value; + } + + return error; + } + break; + + case CMND_SCD30_PRESSURE: + return scd30.setAmbientPressure(value); + break; + + case CMND_SCD30_TEMPOFFSET: + return scd30.setTemperatureOffset(value); + break; + + default: + + break; + } +} + + + + + +bool Scd30CommandSensor() +{ + char command[CMDSZ]; + bool serviced = true; + uint8_t prefix_len = strlen(D_CMND_SCD30); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_SCD30), prefix_len)) { + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + prefix_len, kSCD30_Commands); + + switch (command_code) { + case CMND_SCD30_ALTITUDE: + case CMND_SCD30_AUTOMODE: + case CMND_SCD30_CALIBRATE: + case CMND_SCD30_INTERVAL: + case CMND_SCD30_PRESSURE: + case CMND_SCD30_TEMPOFFSET: + { + uint16_t value = 0; + if (XdrvMailbox.data_len > 0) + { + value = XdrvMailbox.payload; + Scd30SetCommand(command_code, value); + } + else + { + Scd30GetCommand(command_code, &value); + } + + Response_P(S_JSON_SCD30_COMMAND_NVALUE, command, value); + } + break; + + case CMND_SCD30_FW: + { + uint8_t major = 0; + uint8_t minor = 0; + int error; + error = scd30.getFirmwareVersion(&major, &minor); + if (error) + { +#ifdef SCD30_DEBUG + AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: error getting FW version: 0x%lX"), error); +#endif + serviced = false; + } + else + { + Response_P(S_JSON_SCD30_COMMAND_NFW_VALUE, command, major, minor); + } + } + break; + + default: + + serviced = false; + break; + } + } + return serviced; +} + +void Scd30Show(bool json) +{ + if (scd30IsDataValid) + { + char humidity[10]; + dtostrfd(ConvertHumidity(scd30_Humid), Settings.flag2.humidity_resolution, humidity); + char temperature[10]; + dtostrfd(ConvertTemp(scd30_Temp), Settings.flag2.temperature_resolution, temperature); + + if (json) { + + ResponseAppend_P(PSTR(",\"SCD30\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"), + scd30_CO2, scd30_CO2EAvg, temperature, humidity); +#ifdef USE_DOMOTICZ + if (0 == tele_period) + { + DomoticzSensor(DZ_AIRQUALITY, scd30_CO2); + DomoticzTempHumSensor(temperature, humidity); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CO2EAVG, "SCD30", scd30_CO2EAvg); + WSContentSend_PD(HTTP_SNS_CO2, "SCD30", scd30_CO2); + WSContentSend_PD(HTTP_SNS_TEMP, "SCD30", temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, "SCD30", humidity); +#endif + } + } +} + + + + + +bool Xsns42(byte function) +{ + if (!I2cEnabled(XI2C_29)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Scd30Detect(); + } + else if (scd30Found) { + switch (function) { + case FUNC_EVERY_SECOND: + Scd30Update(); + break; + case FUNC_COMMAND: + result = Scd30CommandSensor(); + break; + case FUNC_JSON_APPEND: + Scd30Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Scd30Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_43_hre.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_43_hre.ino" +#ifdef USE_HRE +# 49 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_43_hre.ino" +#define XSNS_43 43 + +enum hre_states { + hre_idle, + hre_sync, + hre_syncing, + hre_read, + hre_reading, + hre_sleep, + hre_sleeping +}; + +hre_states hre_state = hre_idle; + +float hre_usage = 0; +float hre_rate = 0; +uint32_t hre_usage_time = 0; + +int hre_read_errors = 0; +bool hre_good = false; + + + +int hreReadBit() +{ + digitalWrite(pin[GPIO_HRE_CLOCK], HIGH); + delay(1); + int bit = digitalRead(pin[GPIO_HRE_DATA]); + digitalWrite(pin[GPIO_HRE_CLOCK], LOW); + delay(1); + return bit; +} + + + +char hreReadChar(int &parity_errors) +{ + + hreReadBit(); + + unsigned ch=0; + int sum=0; + for (uint32_t i=0; i<7; i++) + { + int b = hreReadBit(); + ch |= b << i; + sum += b; + } + + + if ( (sum & 0x1) != hreReadBit()) + parity_errors++; + + + hreReadBit(); + + return ch; +} + +void hreInit(void) +{ + hre_read_errors = 0; + hre_good = false; + + pinMode(pin[GPIO_HRE_CLOCK], OUTPUT); + pinMode(pin[GPIO_HRE_DATA], INPUT); + + + + digitalWrite(pin[GPIO_HRE_CLOCK], LOW); + + hre_state = hre_sync; +} + + +void hreEvery50ms(void) +{ + static int sync_counter = 0; + static int sync_run = 0; + + static uint32_t curr_start = 0; + static int read_counter = 0; + static int parity_errors = 0; + static char buff[46]; + + static char ch; + static size_t i; + + switch (hre_state) + { + case hre_sync: + if (uptime < 10) + break; + sync_run = 0; + sync_counter = 0; + hre_state = hre_syncing; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_syncing")); + break; + + case hre_syncing: + + + for (uint32_t i=0; i<20; i++) + { + if (hreReadBit()) + sync_run++; + else + sync_run = 0; + if (sync_run == 62) + { + hre_state = hre_read; + break; + } + sync_counter++; + } + + if (sync_counter > 1000) + { + hre_state = hre_sleep; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE D_ERROR)); + } + break; + + + case hre_read: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "sync_run:%d, sync_counter:%d"), sync_run, sync_counter); + read_counter = 0; + parity_errors = 0; + curr_start = uptime; + memset(buff, 0, sizeof(buff)); + hre_state = hre_reading; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_reading")); + + + + + + case hre_reading: + + buff[read_counter++] = hreReadChar(parity_errors); + buff[read_counter++] = hreReadChar(parity_errors); + + if (read_counter == 46) + { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "pe:%d, re:%d, buff:%s"), + parity_errors, hre_read_errors, buff); + if (parity_errors == 0) + { + float curr_usage; + curr_usage = 0.01 * atol(buff+24); + if (hre_usage_time) + { + double dt = 1.666e-2 * (curr_start - hre_usage_time); + hre_rate = (curr_usage - hre_usage)/dt; + } + hre_usage = curr_usage; + hre_usage_time = curr_start; + hre_good = true; + + hre_state = hre_sleep; + } + else + { + hre_read_errors++; + hre_state = hre_sleep; + } + } + break; + + case hre_sleep: + hre_usage_time = curr_start; + hre_state = hre_sleeping; + AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_sleeping")); + + case hre_sleeping: + + + if (uptime - hre_usage_time >= 27) + hre_state = hre_sync; + } +} + +void hreShow(boolean json) +{ + if (!hre_good) + return; + + const char *id = "HRE"; + + char usage[16]; + char rate[16]; + dtostrfd(hre_usage, 2, usage); + dtostrfd(hre_rate, 3, rate); + + if (json) + { + ResponseAppend_P(JSON_SNS_GNGPM, id, usage, rate); +#ifdef USE_WEBSERVER + } + else + { + WSContentSend_PD(HTTP_SNS_GALLONS, id, usage); + WSContentSend_PD(HTTP_SNS_GPM, id, rate); +#endif + } +} + + + + + +bool Xsns43(byte function) +{ + + if (pin[GPIO_HRE_CLOCK] >= 99 || pin[GPIO_HRE_DATA] >= 99) + return false; + + switch (function) + { + case FUNC_INIT: + hreInit(); + break; + case FUNC_EVERY_50_MSECOND: + hreEvery50ms(); + break; + case FUNC_EVERY_SECOND: + break; + case FUNC_JSON_APPEND: + hreShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + hreShow(0); + break; +#endif + } + return false; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_44_sps30.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_44_sps30.ino" +#ifdef USE_I2C +#ifdef USE_SPS30 + +#define XSNS_44 44 +#define XI2C_30 30 + +#define SPS30_ADDR 0x69 + +#include +#include + +uint8_t sps30_ready = 0; +uint8_t sps30_running; + +struct SPS30 { + float PM1_0; + float PM2_5; + float PM4_0; + float PM10; + float NCPM0_5; + float NCPM1_0; + float NCPM2_5; + float NCPM4_0; + float NCPM10; + float TYPSIZ; +} sps30_result; + +#define SPS_CMD_START_MEASUREMENT 0x0010 +#define SPS_CMD_START_MEASUREMENT_ARG 0x0300 +#define SPS_CMD_STOP_MEASUREMENT 0x0104 +#define SPS_CMD_READ_MEASUREMENT 0x0300 +#define SPS_CMD_GET_DATA_READY 0x0202 +#define SPS_CMD_AUTOCLEAN_INTERVAL 0x8004 +#define SPS_CMD_CLEAN 0x5607 +#define SPS_CMD_GET_ACODE 0xd025 +#define SPS_CMD_GET_SERIAL 0xd033 +#define SPS_CMD_RESET 0xd304 +#define SPS_WRITE_DELAY_US 20000 +#define SPS_MAX_SERIAL_LEN 32 + +uint8_t sps30_calc_CRC(uint8_t *data) { + uint8_t crc = 0xFF; + for (uint32_t i = 0; i < 2; i++) { + crc ^= data[i]; + for (uint32_t bit = 8; bit > 0; --bit) { + if(crc & 0x80) { + crc = (crc << 1) ^ 0x31u; + } else { + crc = (crc << 1); + } + } + } + return crc; +} + +void CmdClean(void); + +unsigned char twi_readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop); + +void sps30_get_data(uint16_t cmd, uint8_t *data, uint8_t dlen) { +unsigned char cmdb[2]; +uint8_t tmp[3]; +uint8_t index=0; +memset(data,0,dlen); +uint8_t twi_buff[64]; + + Wire.beginTransmission(SPS30_ADDR); + cmdb[0]=cmd>>8; + cmdb[1]=cmd; + Wire.write(cmdb,2); + Wire.endTransmission(); + + + dlen/=2; + dlen*=3; + + twi_readFrom(SPS30_ADDR,twi_buff,dlen,1); + + uint8_t bind=0; + while (bind>8; + cmdb[1]=cmd; + + if (cmd==SPS_CMD_START_MEASUREMENT) { + cmdb[2]=SPS_CMD_START_MEASUREMENT_ARG>>8; + cmdb[3]=SPS_CMD_START_MEASUREMENT_ARG&0xff; + cmdb[4]=sps30_calc_CRC(&cmdb[2]); + Wire.write(cmdb,5); + } else { + Wire.write(cmdb,2); + } + Wire.endTransmission(); +} + +void SPS30_Detect(void) +{ + if (!I2cSetDevice(SPS30_ADDR)) { return; } + I2cSetActiveFound(SPS30_ADDR, "SPS30"); + + uint8_t dcode[32]; + sps30_get_data(SPS_CMD_GET_SERIAL,dcode,sizeof(dcode)); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("sps30 found with serial: %s"),dcode); + sps30_cmd(SPS_CMD_START_MEASUREMENT); + sps30_running = 1; + sps30_ready = 1; +} + +#define D_UNIT_PM "ug/m3" +#define D_UNIT_NCPM "#/m3" + +#ifdef USE_WEBSERVER +const char HTTP_SNS_SPS30_a[] PROGMEM ="{s}SPS30 " "%s" "{m}%s " D_UNIT_PM "{e}"; +const char HTTP_SNS_SPS30_b[] PROGMEM ="{s}SPS30 " "%s" "{m}%s " D_UNIT_NCPM "{e}"; +const char HTTP_SNS_SPS30_c[] PROGMEM ="{s}SPS30 " "TYPSIZ" "{m}%s " "um" "{e}"; +#endif + +#define PMDP 2 + +#define SPS30_HOURS Settings.sps30_inuse_hours + + + +void SPS30_Every_Second() { + if (!sps30_running) return; + + if (uptime%10==0) { + uint8_t vars[sizeof(float)*10]; + sps30_get_data(SPS_CMD_READ_MEASUREMENT,vars,sizeof(vars)); + float *fp=&sps30_result.PM1_0; + + typedef union { + uint8_t array[4]; + float value; + } ByteToFloat; + + ByteToFloat conv; + + for (uint32_t count=0; count<10; count++) { + for (uint32_t i = 0; i < 4; i++){ + conv.array[3-i] = vars[count*sizeof(float)+i]; + } + *fp++=conv.value; + } + } + + if (uptime%3600==0 && uptime>60) { + + + SPS30_HOURS++; + if (SPS30_HOURS>(7*24)) { + CmdClean(); + SPS30_HOURS=0; + } + } + +} + +void SPS30_Show(bool json) +{ + if (!sps30_running) { return; } + + char str[64]; + if (json) { + dtostrfd(sps30_result.PM1_0,PMDP,str); + ResponseAppend_P(PSTR(",\"SPS30\":{\"" "PM1_0" "\":%s"), str); + dtostrfd(sps30_result.PM2_5,PMDP,str); + ResponseAppend_P(PSTR(",\"" "PM2_5" "\":%s"), str); + dtostrfd(sps30_result.PM4_0,PMDP,str); + ResponseAppend_P(PSTR(",\"" "PM4_0" "\":%s"), str); + dtostrfd(sps30_result.PM10,PMDP,str); + ResponseAppend_P(PSTR(",\"" "PM10" "\":%s"), str); + dtostrfd(sps30_result.NCPM0_5,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM0_5" "\":%s"), str); + dtostrfd(sps30_result.NCPM1_0,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM1_0" "\":%s"), str); + dtostrfd(sps30_result.NCPM2_5,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM2_5" "\":%s"), str); + dtostrfd(sps30_result.NCPM4_0,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM4_0" "\":%s"), str); + dtostrfd(sps30_result.NCPM10,PMDP,str); + ResponseAppend_P(PSTR(",\"" "NCPM10" "\":%s"), str); + dtostrfd(sps30_result.TYPSIZ,PMDP,str); + ResponseAppend_P(PSTR(",\"" "TYPSIZ" "\":%s}"), str); + +#ifdef USE_WEBSERVER + } else { + dtostrfd(sps30_result.PM1_0,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 1.0",str); + dtostrfd(sps30_result.PM2_5,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 2.5",str); + dtostrfd(sps30_result.PM4_0,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 4.0",str); + dtostrfd(sps30_result.PM10,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 10",str); + dtostrfd(sps30_result.NCPM0_5,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 0.5",str); + dtostrfd(sps30_result.NCPM1_0,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 1.0",str); + dtostrfd(sps30_result.NCPM2_5,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 2.5",str); + dtostrfd(sps30_result.NCPM4_0,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 4.0",str); + dtostrfd(sps30_result.NCPM10,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 10",str); + dtostrfd(sps30_result.TYPSIZ,PMDP,str); + WSContentSend_PD(HTTP_SNS_SPS30_c,str); +#endif + } +} + +void CmdClean(void) +{ + sps30_cmd(SPS_CMD_CLEAN); + ResponseTime_P(PSTR(",\"SPS30\":{\"CFAN\":\"true\"}}")); + MqttPublishTeleSensor(); +} + +bool SPS30_cmd(void) +{ + bool serviced = true; + if (XdrvMailbox.data_len > 0) { + char *cp=XdrvMailbox.data; + if (*cp=='c') { + + CmdClean(); + } else if (*cp=='0' || *cp=='1') { + sps30_running=*cp&1; + sps30_cmd(sps30_running?SPS_CMD_START_MEASUREMENT:SPS_CMD_STOP_MEASUREMENT); + } else { + serviced=false; + } + } + Response_P(PSTR("{\"SPS30\":\"%s\"}"), sps30_running?"running":"stopped"); + + return serviced; +} + + + + + +bool Xsns44(byte function) +{ + if (!I2cEnabled(XI2C_30)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + SPS30_Detect(); + } + else if (sps30_ready) { + switch (function) { + case FUNC_EVERY_SECOND: + SPS30_Every_Second(); + break; + case FUNC_JSON_APPEND: + SPS30_Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + SPS30_Show(0); + break; + #endif + case FUNC_COMMAND_SENSOR: + if (XSNS_44 == XdrvMailbox.index) { + result = SPS30_cmd(); + } + break; + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_45_vl53l0x.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_45_vl53l0x.ino" +#ifdef USE_I2C +#ifdef USE_VL53L0X + +#define XSNS_45 45 +#define XI2C_31 31 + +#include +#include "VL53L0X.h" +VL53L0X sensor; + +uint8_t vl53l0x_ready = 0; +uint16_t vl53l0x_distance; +uint16_t Vl53l0_buffer[5]; +uint8_t Vl53l0_index; + + + +void Vl53l0Detect(void) +{ + if (!I2cSetDevice(0x29)) { return; } + + if (!sensor.init()) { return; } + + I2cSetActiveFound(sensor.getAddress(), "VL53L0X"); + + sensor.setTimeout(500); + + + + + + sensor.startContinuous(); + vl53l0x_ready = 1; + + Vl53l0_index=0; +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_VL53L0X[] PROGMEM = + "{s}VL53L0X " D_DISTANCE "{m}%d" D_UNIT_MILLIMETER "{e}"; +#endif + +#define USE_VL_MEDIAN + +void Vl53l0Every_250MSecond(void) +{ + uint16_t tbuff[5],tmp; + uint8_t flag; + + + uint16_t dist = sensor.readRangeContinuousMillimeters(); + if (dist==0 || dist>2000) { + dist=9999; + } + +#ifdef USE_VL_MEDIAN + + Vl53l0_buffer[Vl53l0_index]=dist; + Vl53l0_index++; + if (Vl53l0_index>=5) Vl53l0_index=0; + + + memmove(tbuff,Vl53l0_buffer,sizeof(tbuff)); + for (byte ocnt=0; ocnt<5; ocnt++) { + flag=0; + for (byte count=0; count<4; count++) { + if (tbuff[count]>tbuff[count+1]) { + tmp=tbuff[count]; + tbuff[count]=tbuff[count+1]; + tbuff[count+1]=tmp; + flag=1; + } + } + if (!flag) break; + } + vl53l0x_distance=tbuff[2]; +#else + vl53l0x_distance=dist; +#endif +} + +void Vl53l0Show(boolean json) +{ + if (json) { + ResponseAppend_P(PSTR(",\"VL53L0X\":{\"" D_JSON_DISTANCE "\":%d}"), vl53l0x_distance); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_VL53L0X, vl53l0x_distance); +#endif + } +} + + + + + +bool Xsns45(byte function) +{ + if (!I2cEnabled(XI2C_31)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Vl53l0Detect(); + } + else if (vl53l0x_ready) { + switch (function) { + case FUNC_EVERY_250_MSECOND: + Vl53l0Every_250MSecond(); + break; + case FUNC_JSON_APPEND: + Vl53l0Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Vl53l0Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_46_MLX90614.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_46_MLX90614.ino" +#ifdef USE_I2C +#ifdef USE_MLX90614 + +#define XSNS_46 46 +#define XI2C_32 32 + +#define I2_ADR_IRT 0x5a + +#define MLX90614_RAWIR1 0x04 +#define MLX90614_RAWIR2 0x05 +#define MLX90614_TA 0x06 +#define MLX90614_TOBJ1 0x07 +#define MLX90614_TOBJ2 0x08 + +struct { + union { + uint16_t value; + uint32_t i2c_buf; + }; + float obj_temp; + float amb_temp; + bool ready = false; +} mlx90614; + +void MLX90614_Init(void) +{ + if (!I2cSetDevice(I2_ADR_IRT)) { return; } + I2cSetActiveFound(I2_ADR_IRT, "MLX90614"); + mlx90614.ready = true; +} + +void MLX90614_Every_Second(void) +{ + mlx90614.i2c_buf = I2cRead24(I2_ADR_IRT, MLX90614_TOBJ1); + if (mlx90614.value & 0x8000) { + mlx90614.obj_temp = -999; + } else { + mlx90614.obj_temp = ((float)mlx90614.value * 0.02) - 273.15; + } + mlx90614.i2c_buf = I2cRead24(I2_ADR_IRT,MLX90614_TA); + if (mlx90614.value & 0x8000) { + mlx90614.amb_temp = -999; + } else { + mlx90614.amb_temp = ((float)mlx90614.value * 0.02) - 273.15; + } +} + +#ifdef USE_WEBSERVER + const char HTTP_IRTMP[] PROGMEM = + "{s}MXL90614 " "OBJ-" D_TEMPERATURE "{m}%s C" "{e}" + "{s}MXL90614 " "AMB-" D_TEMPERATURE "{m}%s C" "{e}"; +#endif + +void MLX90614_Show(uint8_t json) +{ + char obj_tstr[16]; + dtostrfd(mlx90614.obj_temp, Settings.flag2.temperature_resolution, obj_tstr); + char amb_tstr[16]; + dtostrfd(mlx90614.amb_temp, Settings.flag2.temperature_resolution, amb_tstr); + + if (json) { + ResponseAppend_P(PSTR(",\"MLX90614\":{\"OBJTMP\":%s,\"AMBTMP\":%s}"), obj_tstr, amb_tstr); +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_IRTMP, obj_tstr, amb_tstr); +#endif + } +} + + + + + +bool Xsns46(byte function) +{ + if (!I2cEnabled(XI2C_32)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + MLX90614_Init(); + } + else if (mlx90614.ready) { + switch (function) { + case FUNC_EVERY_SECOND: + MLX90614_Every_Second(); + break; + case FUNC_JSON_APPEND: + MLX90614_Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MLX90614_Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_47_max31865.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_47_max31865.ino" +#ifdef USE_MAX31865 + +#ifndef USE_SPI +#error "MAX31865 requires USE_SPI enabled" +#endif + +#include "Adafruit_MAX31865.h" + +#define XSNS_47 47 + +#if MAX31865_PTD_WIRES == 4 + #define PTD_WIRES MAX31865_4WIRE +#elif MAX31865_PTD_WIRES == 3 + #define PTD_WIRES MAX31865_3WIRE +#else + #define PTD_WIRES MAX31865_2WIRE +#endif + +int8_t init_status = 0; + +Adafruit_MAX31865 max31865; + +struct MAX31865_Result_Struct { + uint8_t ErrorCode; + uint16_t Rtd; + float PtdResistance; + float PtdTemp; +} MAX31865_Result; + +void MAX31865_Init(void){ + if(init_status) + return; + + max31865.setPins( + pin[GPIO_SSPI_CS], + pin[GPIO_SSPI_MOSI], + pin[GPIO_SSPI_MISO], + pin[GPIO_SSPI_SCLK] + ); + + if(max31865.begin(PTD_WIRES)) + init_status = 1; + else + init_status = -1; +} + + + + + +void MAX31865_GetResult(void){ + uint16_t rtd; + + rtd = max31865.readRTD(); + MAX31865_Result.Rtd = rtd; + MAX31865_Result.PtdResistance = max31865.rtd_to_resistance(rtd, MAX31865_REF_RES); + MAX31865_Result.PtdTemp = max31865.rtd_to_temperature(rtd, MAX31865_PTD_RES, MAX31865_REF_RES) + MAX31865_PTD_BIAS; +} + +void MAX31865_Show(bool Json){ + char temperature[33]; + char resistance[33]; + + dtostrfd(MAX31865_Result.PtdResistance, Settings.flag2.temperature_resolution, resistance); + dtostrfd(MAX31865_Result.PtdTemp, Settings.flag2.temperature_resolution, temperature); + + if(Json){ + ResponseAppend_P(PSTR(",\"MAX31865\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RESISTANCE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ + temperature, resistance, MAX31865_Result.ErrorCode); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_TEMP, temperature); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, MAX31865_Result.PtdTemp); + } +#endif + } else { +#ifdef USE_WEBSERVER + WSContentSend_PD(HTTP_SNS_TEMP, "MAX31865", temperature, TempUnit()); +#endif + } +} + + + + + +bool Xsns47(uint8_t function) +{ + bool result = false; + if((pin[GPIO_SSPI_MISO] < 99) && (pin[GPIO_SSPI_MOSI] < 99) && + (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_CS] < 99)) { + + switch (function) { + case FUNC_INIT: + MAX31865_Init(); + break; + + case FUNC_EVERY_SECOND: + MAX31865_GetResult(); + break; + + case FUNC_JSON_APPEND: + MAX31865_Show(true); + break; + +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MAX31865_Show(false); + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_48_chirp.ino" +# 35 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_48_chirp.ino" +#ifdef USE_I2C +#ifdef USE_CHIRP +# 47 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_48_chirp.ino" +#define XSNS_48 48 +#define XI2C_33 33 + +#define CHIRP_MAX_SENSOR_COUNT 3 + +#define CHIRP_ADDR_STANDARD 0x20 + + + + + +#define D_CMND_CHIRP "CHIRP" + +const char S_JSON_CHIRP_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_CHIRP "%s\":%d}"; +const char S_JSON_CHIRP_COMMAND[] PROGMEM = "{\"" D_CMND_CHIRP "%s\"}"; +const char kCHIRP_Commands[] PROGMEM = "Select|Set|Scan|Reset|Sleep|Wake"; + +const char kChirpTypes[] PROGMEM = "CHIRP"; + + + + + +enum CHIRP_Commands { + CMND_CHIRP_SELECT, + CMND_CHIRP_SET, + CMND_CHIRP_SCAN, + CMND_CHIRP_RESET, + CMND_CHIRP_SLEEP, + CMND_CHIRP_WAKE }; + + + + + + +#define CHIRP_GET_CAPACITANCE 0x00 +#define CHIRP_SET_ADDRESS 0x01 +#define CHIRP_GET_ADDRESS 0x02 +#define CHIRP_MEASURE_LIGHT 0x03 +#define CHIRP_GET_LIGHT 0x04 +#define CHIRP_GET_TEMPERATURE 0x05 +#define CHIRP_RESET 0x06 +#define CHIRP_GET_VERSION 0x07 +#define CHIRP_SLEEP 0x08 +#define CHIRP_GET_BUSY 0x09 + + + + + +void ChirpWriteI2CRegister(uint8_t addr, uint8_t reg) { + Wire.beginTransmission(addr); + Wire.write(reg); + Wire.endTransmission(); +} + +uint16_t ChirpFinishReadI2CRegister16bit(uint8_t addr) { + Wire.requestFrom(addr,(uint8_t)2); + uint16_t t = Wire.read() << 8; + t = t | Wire.read(); + return t; +} + + + + + +uint8_t chirp_current = 0; +uint8_t chirp_found_sensors = 0; + +char chirp_name[7]; +uint8_t chirp_next_job = 0; +uint32_t chirp_timeout_count = 0; + +#pragma pack(1) +struct ChirpSensor_t{ + uint16_t moisture = 0; + uint16_t light = 0; + int16_t temperature = 0; + uint8_t version = 0; + uint8_t address:7; + uint8_t explicitSleep:1; +}; +#pragma pack() + +ChirpSensor_t chirp_sensor[CHIRP_MAX_SENSOR_COUNT]; + + + +void ChirpReset(uint8_t addr) { + ChirpWriteI2CRegister(addr, CHIRP_RESET); +} + + + +void ChirpResetAll(void) { + for (uint32_t i = 0; i < chirp_found_sensors; i++) { + if (chirp_sensor[i].version) { + ChirpReset(chirp_sensor[i].address); + } + } +} + + +void ChirpClockSet() { + Wire.setClockStretchLimit(4000); + Wire.setClock(50000); +} + + + +void ChirpSleep(uint8_t addr) { + ChirpWriteI2CRegister(addr, CHIRP_SLEEP); +} +# 185 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_48_chirp.ino" +void ChirpSelect(uint8_t sensor) { + if(sensor < chirp_found_sensors) { + chirp_current = sensor; + DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u now active."), chirp_current); + } + if (sensor == 255) { + DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u active at address 0x%x."), chirp_current, chirp_sensor[chirp_current].address); + } +} + + + +uint8_t ChirpReadVersion(uint8_t addr) { + return (I2cRead8(addr, CHIRP_GET_VERSION)); +} + + + +bool ChirpSet(uint8_t addr) { + if(addr < 128){ + if (I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr)){ + if(chirp_sensor[chirp_current].version>0x25 && chirp_sensor[chirp_current].version != 255){ + delay(5); + I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr); + + } + DEBUG_SENSOR_LOG(PSTR("CHIRP: Wrote adress %u "), addr); + ChirpReset(chirp_sensor[chirp_current].address); + chirp_sensor[chirp_current].address = addr; + chirp_timeout_count = 10; + chirp_next_job = 0; + if(chirp_sensor[chirp_current].version == 255){ + AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: wrote new address %u, please power off device"), addr); + chirp_sensor[chirp_current].version == 0; + } + return true; + } + } + AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: address %u incorrect and not used"), addr); + return false; +} + + + +bool ChirpScan() +{ + ChirpClockSet(); + chirp_found_sensors = 0; + for (uint8_t address = 1; address <= 127; address++) { + chirp_sensor[chirp_found_sensors].version = 0; + chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address); + delay(2); + chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address); + if (chirp_sensor[chirp_found_sensors].version > 0) { + I2cSetActiveFound(address, "CHIRP"); + if (chirp_found_sensors 0); +} + + + +void ChirpDetect(void) +{ + if (chirp_next_job > 0) { return; } + + DEBUG_SENSOR_LOG(PSTR("CHIRP: scan will start ...")); + if (ChirpScan()) { + uint8_t chirp_model = 0; + GetTextIndexed(chirp_name, sizeof(chirp_name), chirp_model, kChirpTypes); + } +} + + +void ChirpServiceAllSensors(uint8_t job){ + for (uint32_t i = 0; i < chirp_found_sensors; i++) { + if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) { + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare for sensor at address 0x%x"), chirp_sensor[i].address); + switch(job){ + case 0: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_CAPACITANCE); + break; + case 1: + chirp_sensor[i].moisture = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); + break; + case 2: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_TEMPERATURE); + break; + case 3: + chirp_sensor[i].temperature = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); + break; + case 4: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_MEASURE_LIGHT); + break; + case 5: + ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_LIGHT); + break; + case 6: + chirp_sensor[i].light = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); + break; + default: + break; + } + } + } +} + + + +void ChirpEvery100MSecond(void) +{ + + if(chirp_timeout_count == 0) { + switch(chirp_next_job) { + case 0: + DEBUG_SENSOR_LOG(PSTR("CHIRP: reset all")); + ChirpResetAll(); + chirp_timeout_count = 10; + chirp_next_job++; + break; + case 1: + + + chirp_next_job++; + break; + case 2: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read")); + ChirpServiceAllSensors(0); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 3: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read")); + ChirpServiceAllSensors(1); + chirp_next_job++; + break; + case 4: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read - 2nd")); + ChirpServiceAllSensors(0); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 5: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read - 2nd")); + ChirpServiceAllSensors(1); + chirp_next_job++; + break; + case 6: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read")); + ChirpServiceAllSensors(2); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 7: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read")); + ChirpServiceAllSensors(3); + chirp_next_job++; + break; + case 8: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read - 2nd")); + ChirpServiceAllSensors(2); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 9: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read - 2nd")); + ChirpServiceAllSensors(3); + chirp_next_job++; + break; + case 10: + DEBUG_SENSOR_LOG(PSTR("CHIRP: start light measure process")); + ChirpServiceAllSensors(4); + chirp_timeout_count = 90; + chirp_next_job++; + break; + case 11: + DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare light read")); + ChirpServiceAllSensors(5); + chirp_timeout_count = 11; + chirp_next_job++; + break; + case 12: + DEBUG_SENSOR_LOG(PSTR("CHIRP: finish light read")); + ChirpServiceAllSensors(6); + chirp_next_job++; + break; + case 13: + DEBUG_SENSOR_LOG(PSTR("CHIRP: paused, waiting for TELE")); + break; + case 14: + if (Settings.tele_period > 16){ + chirp_timeout_count = (Settings.tele_period - 17) * 10; + DEBUG_SENSOR_LOG(PSTR("CHIRP: timeout 1/10 sec: %u, tele: %u"), chirp_timeout_count, Settings.tele_period); + } + else{ + AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: TELEPERIOD must be > 16 seconds !")); + + } + chirp_next_job = 1; + break; + } + } + else { + chirp_timeout_count--; + } +} + + + + +#ifdef USE_WEBSERVER + + +const char HTTP_SNS_DARKNESS[] PROGMEM = "{s} " D_JSON_DARKNESS "{m}%s %%{e}"; +const char HTTP_SNS_CHIRPVER[] PROGMEM = "{s} CHIRP-sensor %u at address{m}0x%x{e}" + "{s} FW-version{m}%s {e}"; ; +const char HTTP_SNS_CHIRPSLEEP[] PROGMEM = "{s} {m} is sleeping ...{e}"; +#endif + + + +void ChirpShow(bool json) +{ + for (uint32_t i = 0; i < chirp_found_sensors; i++) { + if (chirp_sensor[i].version) { + + char str_temperature[33]; + double t_temperature = ((double) chirp_sensor[i].temperature )/10.0; + dtostrfd(t_temperature, Settings.flag2.temperature_resolution, str_temperature); + char str_light[33]; + dtostrfd(chirp_sensor[i].light, 0, str_light); + char str_version[7]; + if(chirp_sensor[i].version == 0xff){ + strncpy_P(str_version, PSTR("Chirp!"), sizeof(str_version)); + } + else{ + sprintf(str_version, "%x", chirp_sensor[i].version); + } + + if (json) { + if(!chirp_sensor[i].explicitSleep) { + ResponseAppend_P(PSTR(",\"%s%u\":{\"" D_JSON_MOISTURE "\":%d"), chirp_name, i, chirp_sensor[i].moisture); + if(chirp_sensor[i].temperature!=-1){ + ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"),str_temperature); + } + ResponseAppend_P(PSTR(",\"" D_JSON_DARKNESS "\":%s}"),str_light); + } + else { + ResponseAppend_P(PSTR(",\"%s%u\":{\"sleeping\"}"),chirp_name, i); + } + #ifdef USE_DOMOTICZ + if (0 == tele_period) { + char str_moisture[33]; + dtostrfd(chirp_sensor[i].moisture, 0, str_moisture); + DomoticzTempHumSensor(str_temperature, str_moisture); + DomoticzSensor(DZ_ILLUMINANCE,chirp_sensor[i].light); + } + #endif + #ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_CHIRPVER, i, chirp_sensor[i].address, str_version); + if (chirp_sensor[i].explicitSleep){ + WSContentSend_PD(HTTP_SNS_CHIRPSLEEP); + } + else { + WSContentSend_PD(HTTP_SNS_MOISTURE, "", chirp_sensor[i].moisture); + WSContentSend_PD(HTTP_SNS_DARKNESS, str_light); + if (chirp_sensor[i].temperature!=-1) { + WSContentSend_PD(HTTP_SNS_TEMP, "", str_temperature, TempUnit()); + } + } + + #endif + } + } + } +} + + + + + +bool ChirpCmd(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t disp_len = strlen(D_CMND_CHIRP); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_CHIRP), disp_len)) { + int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kCHIRP_Commands); + + switch (command_code) { + case CMND_CHIRP_SELECT: + case CMND_CHIRP_SET: + if (XdrvMailbox.data_len > 0) { + if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(XdrvMailbox.payload); } + if (command_code == CMND_CHIRP_SET) { ChirpSet((uint8_t)XdrvMailbox.payload); } + Response_P(S_JSON_CHIRP_COMMAND_NVALUE, command, XdrvMailbox.payload); + } + else { + if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(255); } + Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload); + } + break; + case CMND_CHIRP_SCAN: + case CMND_CHIRP_SLEEP: + case CMND_CHIRP_WAKE: + case CMND_CHIRP_RESET: + if (command_code == CMND_CHIRP_SCAN) { chirp_next_job = 0; + ChirpDetect(); } + if (command_code == CMND_CHIRP_SLEEP) { chirp_sensor[chirp_current].explicitSleep = true; + ChirpSleep(chirp_sensor[chirp_current].address); } + if (command_code == CMND_CHIRP_WAKE) { chirp_sensor[chirp_current].explicitSleep = false; + ChirpReadVersion(chirp_sensor[chirp_current].address); } + if (command_code == CMND_CHIRP_RESET) { ChirpReset(chirp_sensor[chirp_current].address); } + Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload); + break; + default: + + serviced = false; + break; + } + } + return serviced; +} + + + + + +bool Xsns48(uint8_t function) +{ + if (!I2cEnabled(XI2C_33)) { return false; } + + bool result = false; + + switch (function) { + case FUNC_EVERY_100_MSECOND: + if(chirp_found_sensors > 0){ + ChirpEvery100MSecond(); + } + break; + case FUNC_COMMAND: + result = ChirpCmd(); + break; + case FUNC_JSON_APPEND: + ChirpShow(1); + chirp_next_job = 14; + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + ChirpShow(0); + break; +#endif + case FUNC_INIT: + ChirpDetect(); + break; + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_50_paj7620.ino" +# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_50_paj7620.ino" +#ifdef USE_I2C +#ifdef USE_PAJ7620 + + + + + + +#define XSNS_50 50 +#define XI2C_34 34 + +#define PAJ7620_ADDR 0x73 + +#define PAJ7620_BANK_SEL 0xEF + + + +#define PAJ7620_GET_GESTURE 0x43 +#define PAJ7620_PROXIMITY_AVG_Y 0x6c + +#define PAJ7620_OBJECT_CENTER_X 0xad +#define PAJ7620_OBJECT_CENTER_Y 0xaf + +#define PAJ7620_DOWN 1 +#define PAJ7620_UP 2 +#define PAJ7620_RIGHT 4 +#define PAJ7620_LEFT 8 +#define PAJ7620_NEAR 16 +#define PAJ7620_FAR 32 +#define PAJ7620_CW 64 +#define PAJ7620_CCW 128 + + + + +const uint8_t PAJ7620initRegisterArray[][2] PROGMEM = { + {0xEF,0x00}, + {0x32,0x29}, {0x33,0x01}, {0x34,0x00}, {0x35,0x01}, {0x36,0x00}, {0x37,0x07}, {0x38,0x17}, {0x39,0x06}, + {0x3A,0x12}, {0x3F,0x00}, {0x40,0x02}, {0x41,0xFF}, {0x42,0x01}, {0x46,0x2D}, {0x47,0x0F}, {0x48,0x3C}, + {0x49,0x00}, {0x4A,0x1E}, {0x4B,0x00}, {0x4C,0x20}, {0x4D,0x00}, {0x4E,0x1A}, {0x4F,0x14}, {0x50,0x00}, + {0x51,0x10}, {0x52,0x00}, {0x5C,0x02}, {0x5D,0x00}, {0x5E,0x10}, {0x5F,0x3F}, {0x60,0x27}, {0x61,0x28}, + {0x62,0x00}, {0x63,0x03}, {0x64,0xF7}, {0x65,0x03}, {0x66,0xD9}, {0x67,0x03}, {0x68,0x01}, {0x69,0xC8}, + {0x6A,0x40}, {0x6D,0x04}, {0x6E,0x00}, {0x6F,0x00}, {0x70,0x80}, {0x71,0x00}, {0x72,0x00}, {0x73,0x00}, + {0x74,0xF0}, {0x75,0x00}, {0x80,0x42}, {0x81,0x44}, {0x82,0x04}, {0x83,0x20}, {0x84,0x20}, {0x85,0x00}, + {0x86,0x10}, {0x87,0x00}, {0x88,0x05}, {0x89,0x18}, {0x8A,0x10}, {0x8B,0x01}, {0x8C,0x37}, {0x8D,0x00}, + {0x8E,0xF0}, {0x8F,0x81}, {0x90,0x06}, {0x91,0x06}, {0x92,0x1E}, {0x93,0x0D}, {0x94,0x0A}, {0x95,0x0A}, + {0x96,0x0C}, {0x97,0x05}, {0x98,0x0A}, {0x99,0x41}, {0x9A,0x14}, {0x9B,0x0A}, {0x9C,0x3F}, {0x9D,0x33}, + {0x9E,0xAE}, {0x9F,0xF9}, {0xA0,0x48}, {0xA1,0x13}, {0xA2,0x10}, {0xA3,0x08}, {0xA4,0x30}, {0xA5,0x19}, + {0xA6,0x10}, {0xA7,0x08}, {0xA8,0x24}, {0xA9,0x04}, {0xAA,0x1E}, {0xAB,0x1E}, {0xCC,0x19}, {0xCD,0x0B}, + {0xCE,0x13}, {0xCF,0x64}, {0xD0,0x21}, {0xD1,0x0F}, {0xD2,0x88}, {0xE0,0x01}, {0xE1,0x04}, {0xE2,0x41}, + {0xE3,0xD6}, {0xE4,0x00}, {0xE5,0x0C}, {0xE6,0x0A}, {0xE7,0x00}, {0xE8,0x00}, {0xE9,0x00}, {0xEE,0x07}, + {0xEF,0x01}, + {0x00,0x1E}, {0x01,0x1E}, {0x02,0x0F}, {0x03,0x10}, {0x04,0x02}, {0x05,0x00}, {0x06,0xB0}, {0x07,0x04}, + {0x08,0x0D}, {0x09,0x0E}, {0x0A,0x9C}, {0x0B,0x04}, {0x0C,0x05}, {0x0D,0x0F}, {0x0E,0x02}, {0x0F,0x12}, + {0x10,0x02}, {0x11,0x02}, {0x12,0x00}, {0x13,0x01}, {0x14,0x05}, {0x15,0x07}, {0x16,0x05}, {0x17,0x07}, + {0x18,0x01}, {0x19,0x04}, {0x1A,0x05}, {0x1B,0x0C}, {0x1C,0x2A}, {0x1D,0x01}, {0x1E,0x00}, {0x21,0x00}, + {0x22,0x00}, {0x23,0x00}, {0x25,0x01}, {0x26,0x00}, {0x27,0x39}, {0x28,0x7F}, {0x29,0x08}, {0x30,0x03}, + {0x31,0x00}, {0x32,0x1A}, {0x33,0x1A}, {0x34,0x07}, {0x35,0x07}, {0x36,0x01}, {0x37,0xFF}, {0x38,0x36}, + {0x39,0x07}, {0x3A,0x00}, {0x3E,0xFF}, {0x3F,0x00}, {0x40,0x77}, {0x41,0x40}, {0x42,0x00}, {0x43,0x30}, + {0x44,0xA0}, {0x45,0x5C}, {0x46,0x00}, {0x47,0x00}, {0x48,0x58}, {0x4A,0x1E}, {0x4B,0x1E}, {0x4C,0x00}, + {0x4D,0x00}, {0x4E,0xA0}, {0x4F,0x80}, {0x50,0x00}, {0x51,0x00}, {0x52,0x00}, {0x53,0x00}, {0x54,0x00}, + {0x57,0x80}, {0x59,0x10}, {0x5A,0x08}, {0x5B,0x94}, {0x5C,0xE8}, {0x5D,0x08}, {0x5E,0x3D}, {0x5F,0x99}, + {0x60,0x45}, {0x61,0x40}, {0x63,0x2D}, {0x64,0x02}, {0x65,0x96}, {0x66,0x00}, {0x67,0x97}, {0x68,0x01}, + {0x69,0xCD}, {0x6A,0x01}, {0x6B,0xB0}, {0x6C,0x04}, {0x6D,0x2C}, {0x6E,0x01}, {0x6F,0x32}, {0x71,0x00}, + {0x72,0x01}, {0x73,0x35}, {0x74,0x00}, {0x75,0x33}, {0x76,0x31}, {0x77,0x01}, {0x7C,0x84}, {0x7D,0x03}, + {0x7E,0x01}, + {0xEF,0x00} +}; + + + + + +const char kPaj7620Directions[] PROGMEM = "Down|Up|Right|Left|Near|Far|CW|CCW"; + +const uint8_t PAJ7620_PIN[]= {1,2,3,4}; + + + + + +char PAJ7620_name[] = "PAJ7620"; + +uint32_t PAJ7620_timeout_counter = 10; +uint32_t PAJ7620_next_job = 0; +uint32_t PAJ7620_mode = 1; + +struct { + uint8_t current; + uint8_t last; + uint8_t same; + uint8_t unfinished; +} PAJ7620_gesture; + +bool PAJ7620_finished_gesture = false; +char PAJ7620_currentGestureName[6]; + +struct{ + uint8_t x; + uint8_t y; + uint8_t last_x; + uint8_t last_y; + uint8_t proximity; + uint8_t last_proximity; + uint8_t corner; + struct { + uint8_t step:3; + uint8_t countdown:3; + uint8_t valid:1; + } PIN; +} PAJ7620_state; + + + + + +void PAJ7620SelectBank(uint8_t bank) +{ + I2cWrite(PAJ7620_ADDR, PAJ7620_BANK_SEL, bank &1, 1); +} + + + +void PAJ7620DecodeGesture(void) +{ + uint32_t index = 0; + switch (PAJ7620_gesture.current) { + case PAJ7620_LEFT: + index++; + case PAJ7620_RIGHT: + index++; + case PAJ7620_UP: + index++; + case PAJ7620_DOWN: + if (PAJ7620_gesture.unfinished) { + PAJ7620_finished_gesture = true; + break; + } + PAJ7620_gesture.unfinished = PAJ7620_gesture.current; + PAJ7620_timeout_counter = 5; + break; + case PAJ7620_NEAR: + index = 4; + PAJ7620_finished_gesture = true; + PAJ7620_timeout_counter = 25; + break; + case PAJ7620_FAR: + index = 5; + PAJ7620_finished_gesture = true; + PAJ7620_timeout_counter = 25; + break; + case PAJ7620_CW: + index = 6; + PAJ7620_finished_gesture = true; + break; + case PAJ7620_CCW: + index = 7; + PAJ7620_finished_gesture = true; + break; + default: + index = 8; + if (PAJ7620_gesture.unfinished) { + PAJ7620_finished_gesture = true; + } + break; + } + if (index < 8) { + GetTextIndexed(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), index, kPaj7620Directions); + } + + if (PAJ7620_finished_gesture) { + if (PAJ7620_gesture.unfinished) { + if ((PAJ7620_gesture.current != PAJ7620_NEAR) && (PAJ7620_gesture.current != PAJ7620_FAR)) { + PAJ7620_gesture.current = PAJ7620_gesture.unfinished; + } + } + if (PAJ7620_gesture.current == PAJ7620_gesture.last) { + PAJ7620_gesture.same++; + } else { + PAJ7620_gesture.same = 1; + } + PAJ7620_gesture.last = PAJ7620_gesture.current; + PAJ7620_finished_gesture = false; + PAJ7620_gesture.unfinished = 0; + PAJ7620_timeout_counter += 3; + MqttPublishSensor(); + } +} + + + +void PAJ7620ReadGesture(void) +{ + switch (PAJ7620_mode) { + case 1: + PAJ7620_gesture.current = I2cRead8(PAJ7620_ADDR,PAJ7620_GET_GESTURE); + if ((PAJ7620_gesture.current > 0) || PAJ7620_gesture.unfinished) { + DEBUG_SENSOR_LOG(PSTR("PAJ: gesture: %u"), PAJ7620_gesture.current); + PAJ7620DecodeGesture(); + } + break; + case 2: + PAJ7620_state.proximity = I2cRead8(PAJ7620_ADDR, PAJ7620_PROXIMITY_AVG_Y); + if ((PAJ7620_state.proximity > 0) || (PAJ7620_state.last_proximity > 0)) { + if (PAJ7620_state.proximity != PAJ7620_state.last_proximity) { + PAJ7620_state.last_proximity = PAJ7620_state.proximity; + DEBUG_SENSOR_LOG(PSTR("PAJ: Proximity: %u"), PAJ7620_state.proximity); + MqttPublishSensor(); + } + } + break; + case 3: + case 4: + case 5: + PAJ7620_state.x = I2cRead8(PAJ7620_ADDR, PAJ7620_OBJECT_CENTER_X); + PAJ7620_state.y = I2cRead8(PAJ7620_ADDR, PAJ7620_OBJECT_CENTER_Y); + if ((PAJ7620_state.y > 0) && (PAJ7620_state.x > 0)) { + if ((PAJ7620_state.y != PAJ7620_state.last_y) || (PAJ7620_state.x != PAJ7620_state.last_x)) { + PAJ7620_state.last_y = PAJ7620_state.y; + PAJ7620_state.last_x = PAJ7620_state.x; + DEBUG_SENSOR_LOG(PSTR("PAJ: x: %u y: %u"), PAJ7620_state.x, PAJ7620_state.y); + + PAJ7620_state.corner = 0; + + + + switch (PAJ7620_state.y) { + case 0: case 1: case 2: case 3: case 4: case 5: + PAJ7620_state.corner = 3; + break; + case 9: case 10: case 11: case 12: case 13: case 14: + PAJ7620_state.corner = 1; + break; + } + if (PAJ7620_state.corner != 0) { + switch (PAJ7620_state.x) { + case 0: case 1: case 2: case 3: case 4: case 5: + break; + case 9: case 10: case 11: case 12: case 13: case 14: + PAJ7620_state.corner++; + break; + default: + PAJ7620_state.corner = 0; + break; + } + } + DEBUG_SENSOR_LOG(PSTR("PAJ: corner: %u"), PAJ7620_state.corner); + + if (PAJ7620_state.PIN.countdown == 0) { + PAJ7620_state.PIN.step = 0; + PAJ7620_state.PIN.valid = 0; + } + if (!PAJ7620_state.PIN.step) { + if (PAJ7620_state.corner == PAJ7620_PIN[PAJ7620_state.PIN.step]) { + PAJ7620_state.PIN.step = 1; + PAJ7620_state.PIN.countdown = 7; + } + } else { + if (PAJ7620_state.corner == PAJ7620_PIN[PAJ7620_state.PIN.step]) { + PAJ7620_state.PIN.step += 1; + PAJ7620_state.PIN.countdown = 7; + } else { + PAJ7620_state.PIN.countdown -= 1; + } + } + if (PAJ7620_state.PIN.step == 4) { + PAJ7620_state.PIN.valid = 1; + DEBUG_SENSOR_LOG(PSTR("PAJ: PIN valid!!")); + PAJ7620_state.PIN.countdown = 0; + } + MqttPublishSensor(); + } + } + break; + } +} + + + +void PAJ7620Detect(void) +{ + if (I2cActive(PAJ7620_ADDR)) { return; } + + PAJ7620SelectBank(0); + PAJ7620SelectBank(0); + uint16_t PAJ7620_id = I2cRead16LE(PAJ7620_ADDR,0); + uint8_t PAJ7620_ver = I2cRead8(PAJ7620_ADDR,2); + if (0x7620 == PAJ7620_id) { + I2cSetActiveFound(PAJ7620_ADDR, PAJ7620_name); + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAJ: ID: 0x%x and VER: %u"), PAJ7620_id, PAJ7620_ver); + PAJ7620_next_job = 1; + } + else { + DEBUG_SENSOR_LOG(PSTR("PAJ: sensor not found, false ID 0x%x"), PAJ7620_id); + } +} + + + +void PAJ7620Init(void) +{ + DEBUG_SENSOR_LOG(PSTR("PAJ: init sensor start %u"),millis()); + union{ + uint32_t raw; + uint8_t reg_val[4]; + } buf; + + for (uint32_t i = 0; i < (sizeof(PAJ7620initRegisterArray) / 2); i += 2) + { + buf.raw = pgm_read_dword(PAJ7620initRegisterArray + i); + DEBUG_SENSOR_LOG("PAJ: %x %x %x %x",buf.reg_val[0],buf.reg_val[1],buf.reg_val[2],buf.reg_val[3]); + I2cWrite(PAJ7620_ADDR, buf.reg_val[0], buf.reg_val[1], 1); + I2cWrite(PAJ7620_ADDR, buf.reg_val[2], buf.reg_val[3], 1); + } + DEBUG_SENSOR_LOG(PSTR("PAJ: init sensor done %u"),millis()); + PAJ7620_next_job = 2; +} + + + +void PAJ7620Loop(void) +{ + if (0 == PAJ7620_timeout_counter) { + switch (PAJ7620_next_job) { + case 1: + PAJ7620Init(); + break; + case 2: + if (PAJ7620_mode != 0) { + PAJ7620ReadGesture(); + } + break; + } + } else { + PAJ7620_timeout_counter--; + } +} + + + +void PAJ7620Show(bool json) +{ + if (json) { + if (PAJ7620_currentGestureName[0] != '\0' ) { + ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%u}"), PAJ7620_name, PAJ7620_currentGestureName, PAJ7620_gesture.same); + PAJ7620_currentGestureName[0] = '\0'; + return; + } + switch (PAJ7620_mode) { + case 2: + ResponseAppend_P(PSTR(",\"%s\":{\"Proximity\":%u}"), PAJ7620_name, PAJ7620_state.proximity); + break; + case 3: + if (PAJ7620_state.corner > 0) { + ResponseAppend_P(PSTR(",\"%s\":{\"Corner\":%u}"), PAJ7620_name, PAJ7620_state.corner); + } + break; + case 4: + if (PAJ7620_state.PIN.valid) { + ResponseAppend_P(PSTR(",\"%s\":{\"PIN\":%u}"), PAJ7620_name, 1); + PAJ7620_state.PIN.valid = 0; + } + break; + case 5: + ResponseAppend_P(PSTR(",\"%s\":{\"x\":%u,\"y\":%u}"), PAJ7620_name, PAJ7620_state.x, PAJ7620_state.y); + break; + } + } +} +# 411 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_50_paj7620.ino" +bool PAJ7620CommandSensor(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { + PAJ7620_mode = XdrvMailbox.payload; + } + Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_50, PAJ7620_mode); + + return true; +} + + + + + +bool Xsns50(uint8_t function) +{ + if (!I2cEnabled(XI2C_34)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + PAJ7620Detect(); + } + else if (PAJ7620_next_job) { + switch (function) { + case FUNC_COMMAND_SENSOR: + if (XSNS_50 == XdrvMailbox.index){ + result = PAJ7620CommandSensor(); + } + break; + case FUNC_EVERY_100_MSECOND: + PAJ7620Loop(); + break; + case FUNC_JSON_APPEND: + PAJ7620Show(1); + break; + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_51_rdm6300.ino" +# 21 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_51_rdm6300.ino" +#ifdef USE_RDM6300 + +#define XSNS_51 51 + +#define RDM6300_BAUDRATE 9600 + +#include + +#define RDM_TIMEOUT 100 +char rdm_uid_str[10]; + + +#define RDM6300_BLOCK 2*10 + +uint8_t rdm_blcnt; +TasmotaSerial *RDM6300_Serial = nullptr; + +void RDM6300_Init() { + if (pin[GPIO_RDM6300_RX] < 99) { + RDM6300_Serial = new TasmotaSerial(pin[GPIO_RDM6300_RX],-1,1); + if (RDM6300_Serial->begin(RDM6300_BAUDRATE)) { + if (RDM6300_Serial->hardwareSerial()) { + ClaimSerial(); + } + } + } + rdm_blcnt=0; +} + + +void RDM6300_ScanForTag() { + char rdm_buffer[14]; + uint8_t rdm_index; + uint8_t rdm_array[6]; + + if (!RDM6300_Serial) return; + + if (rdm_blcnt>0) { + rdm_blcnt--; + while (RDM6300_Serial->available()) RDM6300_Serial->read(); + return; + } + + if (RDM6300_Serial->available()) { + + char c=RDM6300_Serial->read(); + if (c!=2) return; + + + rdm_index=0; + uint32_t cmillis=millis(); + while (1) { + if (RDM6300_Serial->available()) { + char c=RDM6300_Serial->read(); + if (c==3) { + + break; + } + rdm_buffer[rdm_index++]=c; + if (rdm_index>13) { + + return; + } + } + if ((millis()-cmillis)>RDM_TIMEOUT) { + + return; + } + } + + + rdm_blcnt=RDM6300_BLOCK; + + + rm6300_hstring_to_array(rdm_array,sizeof(rdm_array),rdm_buffer); + uint8_t accu=0; + for (uint8_t count=0;count<5;count++) { + accu^=rdm_array[count]; + } + if (accu!=rdm_array[5]) { + + return; + } + + + memcpy(rdm_uid_str,&rdm_buffer[2],8); + rdm_uid_str[9]=0; + + ResponseTime_P(PSTR(",\"RDM6300\":{\"UID\":\"%s\"}}"), rdm_uid_str); + MqttPublishTeleSensor(); + + + + + + } + + +} + +uint8_t rm6300_hexnibble(char chr) { + uint8_t rVal = 0; + if (isdigit(chr)) { + rVal = chr - '0'; + } else { + if (chr >= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; + if (chr >= 'a' && chr <= 'f') rVal = chr + 10 - 'a'; + } + return rVal; +} + + +void rm6300_hstring_to_array(uint8_t array[], uint8_t len, char buffer[]) +{ + char *cp=buffer; + for (uint8_t i = 0; i < len; i++) { + uint8_t val = rm6300_hexnibble(*cp++) << 4; + array[i]= val | rm6300_hexnibble(*cp++); + } +} + +#ifdef USE_WEBSERVER +const char HTTP_RDM6300[] PROGMEM = + "{s}RDM6300 " "UID" "{m}%s" "{e}"; + +void RDM6300_Show(void) { + if (!RDM6300_Serial) return; + if (!rdm_uid_str[0]) strcpy(rdm_uid_str,"????"); + WSContentSend_PD(HTTP_RDM6300,rdm_uid_str); +} +#endif + + + + + +bool Xsns51(byte function) +{ + bool result = false; + + switch (function) { + case FUNC_INIT: + RDM6300_Init(); + break; + case FUNC_EVERY_100_MSECOND: + RDM6300_ScanForTag(); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + RDM6300_Show(); + break; +#endif + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_52_ibeacon.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_52_ibeacon.ino" +#ifdef USE_IBEACON + + + +#define XSNS_52 52 + +#include + +#define HM17_BAUDRATE 9600 + +#define IBEACON_DEBUG + + +#define HM17_V110 + + + +#define IB_TIMEOUT_INTERVAL 30 + +#define IB_UPDATE_TIME_INTERVAL 10 + +TasmotaSerial *IBEACON_Serial = nullptr; + + +uint8_t hm17_found,hm17_cmd,hm17_flag; + +#ifdef IBEACON_DEBUG +uint8_t hm17_debug=0; +#endif + + + +#define HM17_BSIZ 128 +char hm17_sbuffer[HM17_BSIZ]; +uint8_t hm17_sindex,hm17_result,hm17_scanning,hm17_connecting; +uint32_t hm17_lastms; +char ib_mac[14]; + + +#if 1 +uint8_t ib_upd_interval,ib_tout_interval; +#define IB_UPDATE_TIME ib_upd_interval +#define IB_TIMEOUT_TIME ib_tout_interval +#else +#undef IB_UPDATE_TIME +#undef IB_TIMEOUT_TIME +#define IB_UPDATE_TIME Settings.ib_upd_interval +#define IB_TIMEOUT_TIME Settings.ib_tout_interval +#endif + +enum {HM17_TEST,HM17_ROLE,HM17_IMME,HM17_DISI,HM17_IBEA,HM17_SCAN,HM17_DISC,HM17_RESET,HM17_RENEW,HM17_CON}; +#define HM17_SUCESS 99 + +struct IBEACON { + char FACID[8]; + char UID[32]; + char MAJOR[4]; + char MINOR[4]; + char PWR[2]; + char MAC[12]; + char RSSI[4]; +}; + +#define MAX_IBEACONS 16 + +struct IBEACON_UID { + char MAC[12]; + char RSSI[4]; + uint8_t FLAGS; + uint8_t TIME; +} ibeacons[MAX_IBEACONS]; + + +void IBEACON_Init() { + + hm17_found=0; + + + if ((pin[GPIO_IBEACON_RX] < 99) && (pin[GPIO_IBEACON_TX] < 99)) { + IBEACON_Serial = new TasmotaSerial(pin[GPIO_IBEACON_RX], pin[GPIO_IBEACON_TX],1); + if (IBEACON_Serial->begin(HM17_BAUDRATE)) { + if (IBEACON_Serial->hardwareSerial()) { + ClaimSerial(); + } + hm17_sendcmd(HM17_TEST); + hm17_lastms=millis(); + + IB_UPDATE_TIME=IB_UPDATE_TIME_INTERVAL; + IB_TIMEOUT_TIME=IB_TIMEOUT_INTERVAL; + } + } +} + +void hm17_every_second(void) { + if (!IBEACON_Serial) return; + + if (hm17_found) { + if (IB_UPDATE_TIME && (uptime%IB_UPDATE_TIME==0)) { + if (hm17_cmd!=99) { + if (hm17_flag&2) { + ib_sendbeep(); + } else { + if (!hm17_connecting) { + hm17_sendcmd(HM17_DISI); + } + } + } + } + for (uint32_t cnt=0;cntIB_TIMEOUT_TIME) { + ibeacons[cnt].FLAGS=0; + ibeacon_mqtt(ibeacons[cnt].MAC,"0000"); + } + } + } + } else { + if (uptime%20==0) { + hm17_sendcmd(HM17_TEST); + } + } +} + +void hm17_sbclr(void) { + memset(hm17_sbuffer,0,HM17_BSIZ); + hm17_sindex=0; + IBEACON_Serial->flush(); +} + +void hm17_sendcmd(uint8_t cmd) { + hm17_sbclr(); + hm17_cmd=cmd; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("hm17cmd %d"),cmd); +#endif + switch (cmd) { + case HM17_TEST: + IBEACON_Serial->write("AT"); + break; + case HM17_ROLE: + IBEACON_Serial->write("AT+ROLE1"); + break; + case HM17_IMME: + IBEACON_Serial->write("AT+IMME1"); + break; + case HM17_DISI: + IBEACON_Serial->write("AT+DISI?"); + hm17_scanning=1; + break; + case HM17_IBEA: + IBEACON_Serial->write("AT+IBEA1"); + break; + case HM17_RESET: + IBEACON_Serial->write("AT+RESET"); + break; + case HM17_RENEW: + IBEACON_Serial->write("AT+RENEW"); + break; + case HM17_SCAN: + IBEACON_Serial->write("AT+SCAN5"); + break; + case HM17_DISC: + IBEACON_Serial->write("AT+DISC?"); + hm17_scanning=1; + break; + case HM17_CON: + IBEACON_Serial->write((const uint8_t*)"AT+CON",6); + IBEACON_Serial->write((const uint8_t*)ib_mac,12); + hm17_connecting=1; + break; + } +} + +uint32_t ibeacon_add(struct IBEACON *ib) { + + if (!strncmp(ib->MAC,"FFFF",4) || strncmp(ib->FACID,"00000000",8)) { + for (uint32_t cnt=0;cntMAC,12)) { + + memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); + ibeacons[cnt].TIME=0; + return 1; + } + } + } + for (uint32_t cnt=0;cntMAC,12); + memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); + ibeacons[cnt].FLAGS=1; + ibeacons[cnt].TIME=0; + return 1; + } + } + } + return 0; +} + +void hm17_decode(void) { + struct IBEACON ib; + switch (hm17_cmd) { + case HM17_TEST: + if (!strncmp(hm17_sbuffer,"OK",2)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("AT OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + hm17_found=1; + } + break; + case HM17_ROLE: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("ROLE OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_IMME: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("IMME OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_IBEA: + if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("IBEA OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_SCAN: + if (!strncmp(hm17_sbuffer,"OK+Set:5",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("SCAN OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_RESET: + if (!strncmp(hm17_sbuffer,"OK+RESET",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("RESET OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_RENEW: + if (!strncmp(hm17_sbuffer,"OK+RENEW",8)) { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("RENEW OK")); +#endif + hm17_sbclr(); + hm17_result=HM17_SUCESS; + } + break; + case HM17_CON: + if (!strncmp(hm17_sbuffer,"OK+CONNA",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNA OK")); +#endif + hm17_connecting=2; + break; + } + if (!strncmp(hm17_sbuffer,"OK+CONNE",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNE ERROR")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+CONNF",8)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNF ERROR")); +#endif + break; + } + if (hm17_connecting==2 && !strncmp(hm17_sbuffer,"OK+CONN",7)) { + hm17_sbclr(); +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONN OK")); +#endif + hm17_connecting=3; + hm17_sendcmd(HM17_TEST); + hm17_connecting=0; + break; + } + break; + + case HM17_DISI: + case HM17_DISC: + if (!strncmp(hm17_sbuffer,"OK+DISCS",8)) { + hm17_sbclr(); + hm17_result=1; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISCS OK")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISIS",8)) { + hm17_sbclr(); + hm17_result=1; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISIS OK")); +#endif + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISCE",8)) { + hm17_sbclr(); + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISCE OK")); +#endif + hm17_scanning=0; + break; + } + if (!strncmp(hm17_sbuffer,"OK+NAME:",8)) { + if (hm17_sbuffer[hm17_sindex-1]=='\n') { + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("NAME OK")); + AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + hm17_sbclr(); + } + break; + } + if (!strncmp(hm17_sbuffer,"OK+DIS0:",8)) { + if (hm17_cmd==HM17_DISI) { +#ifdef HM17_V110 + goto hm17_v110; +#endif + } else { + if (hm17_sindex==20) { + hm17_result=HM17_SUCESS; +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("DIS0 OK")); + AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + hm17_sbclr(); + } + } + break; + } + if (!strncmp(hm17_sbuffer,"OK+DISC:",8)) { +hm17_v110: + if (hm17_cmd==HM17_DISI) { + if (hm17_sindex==78) { +#ifdef IBEACON_DEBUG + if (hm17_debug) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("DISC: OK")); + + AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); + } +#endif + memcpy(ib.FACID,&hm17_sbuffer[8],8); + memcpy(ib.UID,&hm17_sbuffer[8+8+1],32); + memcpy(ib.MAJOR,&hm17_sbuffer[8+8+1+32+1],4); + memcpy(ib.MINOR,&hm17_sbuffer[8+8+1+32+1+4],4); + memcpy(ib.PWR,&hm17_sbuffer[8+8+1+32+1+4+4],2); + memcpy(ib.MAC,&hm17_sbuffer[8+8+1+32+1+4+4+2+1],12); + memcpy(ib.RSSI,&hm17_sbuffer[8+8+1+32+1+4+4+2+1+12+1],4); + + if (ibeacon_add(&ib)) { + ibeacon_mqtt(ib.MAC,ib.RSSI); + } + hm17_sbclr(); + hm17_result=1; + } + } else { +#ifdef IBEACON_DEBUG + if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); +#endif + } + break; + } + } +} + +void IBEACON_loop() { + + if (!IBEACON_Serial) return; + +uint32_t difftime=millis()-hm17_lastms; + + while (IBEACON_Serial->available()) { + hm17_lastms=millis(); + + if (hm17_sindexread(); + hm17_sindex++; + hm17_decode(); + } else { + hm17_sindex=0; + break; + } + } + + if (hm17_cmd==99) { + if (hm17_sindex>=HM17_BSIZ-2 || (hm17_sindex && (difftime>100))) { + AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),hm17_sbuffer); + hm17_sbclr(); + } + } + +} + +#ifdef USE_WEBSERVER +const char HTTP_IBEACON[] PROGMEM = + "{s}IBEACON-UID : %s" " - RSSI : %s" "{m}{e}"; + +void IBEACON_Show(void) { +char mac[14]; +char rssi[6]; + + for (uint32_t cnt=0;cnt 0) { + char *cp=XdrvMailbox.data; + if (*cp>='0' && *cp<='8') { + hm17_sendcmd(*cp&7); + Response_P(S_JSON_IBEACON, XSNS_52,"hm17cmd",*cp&7); + } else if (*cp=='s') { + cp++; + len--; + while (*cp==' ') { + len--; + cp++; + } + IBEACON_Serial->write((uint8_t*)cp,len); + hm17_cmd=99; + Response_P(S_JSON_IBEACON1, XSNS_52,"hm17cmd",cp); + } else if (*cp=='u') { + cp++; + if (*cp) IB_UPDATE_TIME=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"uintv",IB_UPDATE_TIME); + } else if (*cp=='t') { + cp++; + if (*cp) IB_TIMEOUT_TIME=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"lintv",IB_TIMEOUT_TIME); + } else if (*cp=='c') { + for (uint32_t cnt=0;cnt + + +#define SPECIAL_SS + + + + + +#if MY_LANGUAGE==de-DE + +#define D_TPWRIN "Verbrauch" +#define D_TPWROUT "Einspeisung" +#define D_TPWRCURR "Aktueller Verbrauch" +#define D_TPWRCURR1 "Verbrauch P1" +#define D_TPWRCURR2 "Verbrauch P2" +#define D_TPWRCURR3 "Verbrauch P3" +#define D_Strom_L1 "Strom L1" +#define D_Strom_L2 "Strom L2" +#define D_Strom_L3 "Strom L3" +#define D_Spannung_L1 "Spannung L1" +#define D_Spannung_L2 "Spannung L2" +#define D_Spannung_L3 "Spannung L3" +#define D_METERNR "Zähler Nr" +#define D_METERSID "Service ID" +#define D_GasIN "Zählerstand" +#define D_H2oIN "Zählerstand" +#define D_StL1L2L3 "Ströme L1+L2+L3" +#define D_SpL1L2L3 "Spannung L1+L2+L3/3" + +#else + +#undef D_TPWRIN +#undef D_TPWROUT +#undef D_TPWRCURR +#undef D_TPWRCURR1 +#undef D_TPWRCURR2 +#undef D_TPWRCURR3 +#undef D_Strom_L1 +#undef D_Strom_L2 +#undef D_Strom_L3 +#undef D_Spannung_L1 +#undef D_Spannung_L2 +#undef D_Spannung_L3 +#undef D_METERNR +#undef D_METERSID +#undef D_GasIN +#undef D_H2oIN +#undef D_StL1L2L3 +#undef D_SpL1L2L3 + +#define D_TPWRIN "Total-In" +#define D_TPWROUT "Total-Out" +#define D_TPWRCURR "Current-In/Out" +#define D_TPWRCURR1 "Current-In p1" +#define D_TPWRCURR2 "Current-In p2" +#define D_TPWRCURR3 "Current-In p3" +#define D_Strom_L1 "Current L1" +#define D_Strom_L2 "Current L2" +#define D_Strom_L3 "Current L3" +#define D_Spannung_L1 "Voltage L1" +#define D_Spannung_L2 "Voltage L2" +#define D_Spannung_L3 "Voltage L3" +#define D_METERNR "Meter_number" +#define D_METERSID "Service ID" +#define D_GasIN "Counter" +#define D_H2oIN "Counter" +#define D_StL1L2L3 "Current L1+L2+L3" +#define D_SpL1L2L3 "Voltage L1+L2+L3/3" + +#endif + + + +#define DJ_TPWRIN "Total_in" +#define DJ_TPWROUT "Total_out" +#define DJ_TPWRCURR "Power_curr" +#define DJ_TPWRCURR1 "Power_p1" +#define DJ_TPWRCURR2 "Power_p2" +#define DJ_TPWRCURR3 "Power_p3" +#define DJ_CURR1 "Curr_p1" +#define DJ_CURR2 "Curr_p2" +#define DJ_CURR3 "Curr_p3" +#define DJ_VOLT1 "Volt_p1" +#define DJ_VOLT2 "Volt_p2" +#define DJ_VOLT3 "Volt_p3" +#define DJ_METERNR "Meter_number" +#define DJ_METERSID "Meter_id" +#define DJ_CSUM "Curr_summ" +#define DJ_VAVG "Volt_avg" +#define DJ_COUNTER "Count" + +struct METER_DESC { + uint8_t srcpin; + uint8_t type; + uint16_t flag; + int32_t params; + char prefix[8]; + int8_t trxpin; + uint8_t tsecs; + char *txmem; + uint8_t index; + uint8_t max_index; +}; + + + + + + +#define EHZ161_0 1 +#define EHZ161_1 2 +#define EHZ363 3 +#define EHZH 4 +#define EDL300 5 +#define Q3B 6 +#define COMBO3 7 +#define COMBO2 8 +#define COMBO3a 9 +#define Q3B_V1 10 +#define EHZ363_2 11 +#define COMBO3b 12 +#define WGS_COMBO 13 +#define EBZD_G 14 + + +#define METER EHZ161_1 + + +#if METER==EHZ161_0 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; +const uint8_t meter[]= +"1,1-0:1.8.0*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.0*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"1,1-0:21.7.0*255(@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",0|" +"1,1-0:41.7.0*255(@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",0|" +"1,1-0:61.7.0*255(@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",0|" +"1,=m 3+4+5 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + + + +#if METER==EHZ161_1 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; +const uint8_t meter[]= +"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; +#endif + + + +#if METER==EHZ363 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" + +"1,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" + +"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" + +"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; +#endif + + + +#if METER==EHZH +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + + +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" + +"1,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" + +"1,770701000f0700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; +#endif + + + +#if METER==EDL300 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + + +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" + +"1,77070100020801ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" + +"1,770701000f0700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; +#endif + +#if METER==EBZD_G +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"strom",-1,1,0}}; +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" + +"1,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" + +"1,77070100010801ff@1000," D_TPWRCURR1 ",KWh," DJ_TPWRCURR1 ",4|" + +"1,77070100010802ff@1000," D_TPWRCURR2 ",KWh," DJ_TPWRCURR2 ",4|" + +"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" + +"1,77070100600100ff@#," D_METERNR ",," DJ_METERNR ",0"; +#endif + + + + +#if METER==Q3B +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" + +"1,77070100020801ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" + +"1,77070100010700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; +#endif + +#if METER==COMBO3 + +#undef METERS_USED +#define METERS_USED 3 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, + [1]={14,'s',0,SML_BAUDRATE,"SML",-1,1,0}, + [2]={4,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}}; + + +const uint8_t meter[]= +"1,1-0:1.8.0*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.0*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"1,1-0:21.7.0*255(@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",0|" +"1,1-0:41.7.0*255(@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",0|" +"1,1-0:61.7.0*255(@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",0|" +"1,=m 3+4+5 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" +"2,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"2,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"2,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"3,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"3,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"3,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"3,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + +#if METER==COMBO2 + +#undef METERS_USED +#define METERS_USED 2 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, + [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}}; + + +const uint8_t meter[]= +"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" + +"2,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"2,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"2,=d 6 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"2,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + +#if METER==COMBO3a +#undef METERS_USED +#define METERS_USED 3 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, + [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}, + [2]={1,'o',0,SML_BAUDRATE,"OBIS3",-1,1,0}}; + + +const uint8_t meter[]= +"1,=h --- Zähler Nr 1 ---|" +"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" +"2,=h --- Zähler Nr 2 ---|" +"2,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"2,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"2,=d 6 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"2,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" +"3,=h --- Zähler Nr 3 ---|" +"3,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"3,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"3,=d 10 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"3,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; + +#endif + + + +#if METER==Q3B_V1 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ +[0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; +const uint8_t meter[]= +"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,=d 1 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; +#endif + + + +#if METER==EHZ363_2 +#undef METERS_USED +#define METERS_USED 1 +struct METER_DESC const meter_desc[METERS_USED]={ +[0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + +const uint8_t meter[]= + +"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" + +"1,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" + +"1,77070100010801ff@1000," D_TPWRCURR1 ",KWh," DJ_TPWRCURR1 ",4|" + +"1,77070100010802ff@1000," D_TPWRCURR2 ",KWh," DJ_TPWRCURR2 ",4|" + +"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" + +"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; +#endif + + +#if METER==COMBO3b +#undef METERS_USED +#define METERS_USED 3 +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, + [1]={14,'c',0,50,"Gas"}, + [2]={1,'c',0,10,"Wasser"}}; + + +const uint8_t meter[]= +"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" +"1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" +"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" +"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" + + +"2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",2|" + +"3,1-0:1.8.0*255(@100," D_H2oIN ",cbm," DJ_COUNTER ",2"; +#endif + + +#if METER==WGS_COMBO +#undef METERS_USED +#define METERS_USED 3 + +struct METER_DESC const meter_desc[METERS_USED]={ + [0]={1,'c',0,10,"H20",-1,1,0}, + [1]={4,'c',0,50,"GAS",-1,1,0}, + [2]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; + +const uint8_t meter[]= + + +"1,1-0:1.8.0*255(@10000," D_H2oIN ",cbm," DJ_COUNTER ",4|" + + +"2,=h==================|" +"2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",3|" + +"3,=h==================|" + +"3,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",3|" +"3,=h==================|" + +"3,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",2|" +"3,=h -------------------------------|" +"3,=m 10+11+12 @100," D_StL1L2L3 ",A," DJ_CSUM ",2|" + +"3,=m 13+14+15/#3 @100," D_SpL1L2L3 ",V," DJ_VAVG ",2|" +"3,=h==================|" + +"3,77070100240700ff@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",2|" + +"3,77070100380700ff@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",2|" + +"3,770701004c0700ff@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",2|" +"3,=h -------------------------------|" + +"3,770701001f0700ff@100," D_Strom_L1 ",A," DJ_CURR1 ",2|" + +"3,77070100330700ff@100," D_Strom_L2 ",A," DJ_CURR2 ",2|" + +"3,77070100470700ff@100," D_Strom_L3 ",A," DJ_CURR3 ",2|" +"3,=h -------------------------------|" + +"3,77070100200700ff@100," D_Spannung_L1 ",V," DJ_VOLT1 ",2|" + +"3,77070100340700ff@100," D_Spannung_L2 ",V," DJ_VOLT2 ",2|" + +"3,77070100480700ff@100," D_Spannung_L3 ",V," DJ_VOLT3 ",2|" +"3,=h==================|" + +"3,77070100000009ff@#," D_METERSID ",," DJ_METERSID ",0|" +"3,=h--------------------------------"; +#endif +# 499 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_53_sml.ino" +#define USE_SML_MEDIAN_FILTER + + +#ifndef SML_MAX_VARS +#define SML_MAX_VARS 20 +#endif + + +#define MAX_METERS 5 +double meter_vars[SML_MAX_VARS]; + +#define MAX_DVARS MAX_METERS*2 +double dvalues[MAX_DVARS]; +uint32_t dtimes[MAX_DVARS]; +uint8_t meters_used; + +struct METER_DESC const *meter_desc_p; +const uint8_t *meter_p; +uint8_t meter_spos[MAX_METERS]; + + +TasmotaSerial *meter_ss[MAX_METERS]; + + +#define SML_BSIZ 48 +uint8_t smltbuf[MAX_METERS][SML_BSIZ]; + + +#define METER_ID_SIZE 24 +char meter_id[MAX_METERS][METER_ID_SIZE]; + +#define EBUS_SYNC 0xaa +#define EBUS_ESC 0xa9 + +uint8_t sml_send_blocks; +uint8_t sml_100ms_cnt; +uint8_t sml_desc_cnt; + +#ifdef USE_SML_MEDIAN_FILTER + +#define MEDIAN_SIZE 5 +struct SML_MEDIAN_FILTER { +double buffer[MEDIAN_SIZE]; +int8_t index; +} sml_mf[SML_MAX_VARS]; + +#ifndef FLT_MAX +#define FLT_MAX 99999999 +#endif + +double sml_median_array(double *array,uint8_t len) { + uint8_t ind[len]; + uint8_t mind=0,index=0,flg; + double min=FLT_MAX; + + for (uint8_t hcnt=0; hcntbuffer[mf->index]=in; + mf->index++; + if (mf->index>=MEDIAN_SIZE) mf->index=0; + + return sml_median_array(mf->buffer,MEDIAN_SIZE); +# 603 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_53_sml.ino" +} +#endif + +#ifdef ANALOG_OPTO_SENSOR + +uint8_t ads1115_up; + + +#define SAMPLE_BIT (0x8000) + +#define ADS1115_COMP_QUEUE_SHIFT 0 +#define ADS1115_COMP_LATCH_SHIFT 2 +#define ADS1115_COMP_POLARITY_SHIFT 3 +#define ADS1115_COMP_MODE_SHIFT 4 +#define ADS1115_DATA_RATE_SHIFT 5 +#define ADS1115_MODE_SHIFT 8 +#define ADS1115_PGA_SHIFT 9 +#define ADS1115_MUX_SHIFT 12 + +enum ads1115_comp_queue { + ADS1115_COMP_QUEUE_AFTER_ONE = 0, + ADS1115_COMP_QUEUE_AFTER_TWO = 0x1 << ADS1115_COMP_QUEUE_SHIFT, + ADS1115_COMP_QUEUE_AFTER_FOUR = 0x2 << ADS1115_COMP_QUEUE_SHIFT, + ADS1115_COMP_QUEUE_DISABLE = 0x3 << ADS1115_COMP_QUEUE_SHIFT, + ADS1115_COMP_QUEUE_MASK = 0x3 << ADS1115_COMP_QUEUE_SHIFT, +}; + +enum ads1115_comp_latch { + ADS1115_COMP_LATCH_NO = 0, + ADS1115_COMP_LATCH_YES = 1 << ADS1115_COMP_LATCH_SHIFT, + ADS1115_COMP_LATCH_MASK = 1 << ADS1115_COMP_LATCH_SHIFT, +}; + +enum ads1115_comp_polarity { + ADS1115_COMP_POLARITY_ACTIVE_LOW = 0, + ADS1115_COMP_POLARITY_ACTIVE_HIGH = 1 << ADS1115_COMP_POLARITY_SHIFT, + ADS1115_COMP_POLARITY_MASK = 1 << ADS1115_COMP_POLARITY_SHIFT, +}; + +enum ads1115_comp_mode { + ADS1115_COMP_MODE_WINDOW = 0, + ADS1115_COMP_MODE_HYSTERESIS = 1 << ADS1115_COMP_MODE_SHIFT, + ADS1115_COMP_MODE_MASK = 1 << ADS1115_COMP_MODE_SHIFT, +}; + +enum ads1115_data_rate { + ADS1115_DATA_RATE_8_SPS = 0, + ADS1115_DATA_RATE_16_SPS = 0x1 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_32_SPS = 0x2 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_64_SPS = 0x3 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_128_SPS = 0x4 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_250_SPS = 0x5 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_475_SPS = 0x6 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_860_SPS = 0x7 << ADS1115_DATA_RATE_SHIFT, + ADS1115_DATA_RATE_MASK = 0x7 << ADS1115_DATA_RATE_SHIFT, +}; + +enum ads1115_mode { + ADS1115_MODE_CONTINUOUS = 0, + ADS1115_MODE_SINGLE_SHOT = 1 << ADS1115_MODE_SHIFT, + ADS1115_MODE_MASK = 1 << ADS1115_MODE_SHIFT, +}; + +enum ads1115_pga { + ADS1115_PGA_TWO_THIRDS = 0, + ADS1115_PGA_ONE = 0x1 << ADS1115_PGA_SHIFT, + ADS1115_PGA_TWO = 0x2 << ADS1115_PGA_SHIFT, + ADS1115_PGA_FOUR = 0x3 << ADS1115_PGA_SHIFT, + ADS1115_PGA_EIGHT = 0x4 << ADS1115_PGA_SHIFT, + ADS1115_PGA_SIXTEEN = 0x5 << ADS1115_PGA_SHIFT, + ADS1115_PGA_MASK = 0x7 << ADS1115_PGA_SHIFT, +}; + + +enum ads1115_mux { + ADS1115_MUX_DIFF_AIN0_AIN1 = 0, + ADS1115_MUX_DIFF_AIN0_AIN3 = 0x1 << ADS1115_MUX_SHIFT, + ADS1115_MUX_DIFF_AIN1_AIN3 = 0x2 << ADS1115_MUX_SHIFT, + ADS1115_MUX_DIFF_AIN2_AIN3 = 0x3 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN0 = 0x4 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN1 = 0x5 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN2 = 0x6 << ADS1115_MUX_SHIFT, + ADS1115_MUX_GND_AIN3 = 0x7 << ADS1115_MUX_SHIFT, + ADS1115_MUX_MASK = 0x7 << ADS1115_MUX_SHIFT, +}; + +class ADS1115 { +public: + ADS1115(uint8_t address = 0x48); + + void begin(); + uint8_t trigger_sample(); + uint8_t reset(); + bool is_sample_in_progress(); + int16_t read_sample(); + float sample_to_float(int16_t val); + float read_sample_float(); + + void set_comp_queue(enum ads1115_comp_queue val) { set_config(val, ADS1115_COMP_QUEUE_MASK); } + void set_comp_latching(enum ads1115_comp_latch val) { set_config(val, ADS1115_COMP_LATCH_MASK); } + void set_comp_polarity(enum ads1115_comp_polarity val) { set_config(val, ADS1115_COMP_POLARITY_MASK); } + void set_comp_mode(enum ads1115_comp_mode val) { set_config(val, ADS1115_COMP_MODE_MASK); } + void set_data_rate(enum ads1115_data_rate val) { set_config(val, ADS1115_DATA_RATE_MASK); } + void set_mode(enum ads1115_mode val) { set_config(val, ADS1115_MODE_MASK); } + void set_pga(enum ads1115_pga val) { set_config(val, ADS1115_PGA_MASK); m_voltage_range = val >> ADS1115_PGA_SHIFT; } + void set_mux(enum ads1115_mux val) { set_config(val, ADS1115_MUX_MASK); } + +private: + void set_config(uint16_t val, uint16_t mask) { + m_config = (m_config & ~mask) | val; + } + + uint8_t write_register(uint8_t reg, uint16_t val); + uint16_t read_register(uint8_t reg); + + uint8_t m_address; + uint16_t m_config; + int m_voltage_range; +}; + + +enum ads1115_register { + ADS1115_REGISTER_CONVERSION = 0, + ADS1115_REGISTER_CONFIG = 1, + ADS1115_REGISTER_LOW_THRESH = 2, + ADS1115_REGISTER_HIGH_THRESH = 3, +}; + +#define FACTOR 32768.0 +static float ranges[] = { 6.144 / FACTOR, 4.096 / FACTOR, 2.048 / FACTOR, 1.024 / FACTOR, 0.512 / FACTOR, 0.256 / FACTOR}; + +ADS1115::ADS1115(uint8_t address) +{ + m_address = address; + m_config = ADS1115_COMP_QUEUE_AFTER_ONE | + ADS1115_COMP_LATCH_NO | + ADS1115_COMP_POLARITY_ACTIVE_LOW | + ADS1115_COMP_MODE_WINDOW | + ADS1115_DATA_RATE_128_SPS | + ADS1115_MODE_SINGLE_SHOT | + ADS1115_MUX_GND_AIN0; + set_pga(ADS1115_PGA_ONE); +} + +uint8_t ADS1115::write_register(uint8_t reg, uint16_t val) +{ + Wire.beginTransmission(m_address); + Wire.write(reg); + Wire.write(val>>8); + Wire.write(val & 0xFF); + return Wire.endTransmission(); +} + +uint16_t ADS1115::read_register(uint8_t reg) +{ + Wire.beginTransmission(m_address); + Wire.write(reg); + Wire.endTransmission(); + + uint8_t result = Wire.requestFrom((int)m_address, 2, 1); + if (result != 2) { + return 0; + } + + uint16_t val; + + val = Wire.read() << 8; + val |= Wire.read(); + return val; +} + +void ADS1115::begin() +{ + Wire.begin(); +} + +uint8_t ADS1115::trigger_sample() +{ + return write_register(ADS1115_REGISTER_CONFIG, m_config | SAMPLE_BIT); +} + +uint8_t ADS1115::reset() +{ + Wire.beginTransmission(0); + Wire.write(0x6); + return Wire.endTransmission(); +} + +bool ADS1115::is_sample_in_progress() +{ + uint16_t val = read_register(ADS1115_REGISTER_CONFIG); + return (val & SAMPLE_BIT) == 0; +} + +int16_t ADS1115::read_sample() +{ + return read_register(ADS1115_REGISTER_CONVERSION); +} + +float ADS1115::sample_to_float(int16_t val) +{ + return val * ranges[m_voltage_range]; +} + +float ADS1115::read_sample_float() +{ + return sample_to_float(read_sample()); +} + +ADS1115 adc; + +void ADS1115_init(void) { + + ads1115_up=0; + if (!i2c_flg) return; + + adc.begin(); + adc.set_data_rate(ADS1115_DATA_RATE_128_SPS); + adc.set_mode(ADS1115_MODE_CONTINUOUS); + adc.set_mux(ADS1115_MUX_DIFF_AIN0_AIN3); + adc.set_pga(ADS1115_PGA_TWO); + + int16_t val = adc.read_sample(); + ads1115_up=1; +} + +#endif + +char sml_start; +uint8_t dump2log=0; + +#define SML_SAVAILABLE Serial_available() +#define SML_SREAD Serial_read() +#define SML_SPEAK Serial_peek() + +bool Serial_available() { + uint8_t num=dump2log&7; + if (num<1 || num>meters_used) num=1; + return meter_ss[num-1]->available(); +} + +uint8_t Serial_read() { + uint8_t num=dump2log&7; + if (num<1 || num>meters_used) num=1; + return meter_ss[num-1]->read(); +} + +uint8_t Serial_peek() { + uint8_t num=dump2log&7; + if (num<1 || num>meters_used) num=1; + return meter_ss[num-1]->peek(); +} + +uint8_t sml_logindex; + +void Dump2log(void) { + +int16_t index=0,hcnt=0; +uint32_t d_lastms; +uint8_t dchars[16]; + + + + if (dump2log&8) { + + while (SML_SAVAILABLE) { + log_data[index]=':'; + index++; + log_data[index]=' '; + index++; + d_lastms=millis(); + while ((millis()-d_lastms)<40) { + if (SML_SAVAILABLE) { + uint8_t c=SML_SREAD; + sprintf(&log_data[index],"%02x ",c); + dchars[hcnt]=c; + index+=3; + hcnt++; + if (hcnt>15) { + + log_data[index]='='; + index++; + log_data[index]='>'; + index++; + log_data[index]=' '; + index++; + for (uint8_t ccnt=0; ccnt<16; ccnt++) { + if (isprint(dchars[ccnt])) { + log_data[index]=dchars[ccnt]; + } else { + log_data[index]=' '; + } + index++; + } + break; + } + } + } + if (index>0) { + log_data[index]=0; + AddLog(LOG_LEVEL_INFO); + index=0; + hcnt=0; + } + } + } else { + if (meter_desc_p[(dump2log&7)-1].type=='o') { + + while (SML_SAVAILABLE) { + char c=SML_SREAD&0x7f; + if (c=='\n' || c=='\r') { + log_data[sml_logindex]=0; + AddLog(LOG_LEVEL_INFO); + sml_logindex=2; + log_data[0]=':'; + log_data[1]=' '; + break; + } + log_data[sml_logindex]=c; + if (sml_logindex2) { + log_data[index]=0; + AddLog(LOG_LEVEL_INFO); + } + } + } +} + + +uint8_t *skip_sml(uint8_t *cp,int16_t *res) { + uint8_t len,len1,type; + len=*cp&0xf; + type=*cp&0x70; + if (type==0x70) { + + + cp++; + while (len--) { + len1=*cp&0x0f; + cp+=len1; + } + *res=0; + } else { + + *res=(signed char)*(cp+1); + cp+=len; + } + return cp; +} + + + +double sml_getvalue(unsigned char *cp,uint8_t index) { +uint8_t len,unit,type; +int16_t scaler,result; +int64_t value; +double dval; + + + + cp=skip_sml(cp,&result); + + cp=skip_sml(cp,&result); + + cp=skip_sml(cp,&result); + + cp=skip_sml(cp,&result); + scaler=result; + + type=*cp&0x70; + len=*cp&0x0f; + cp++; + if (type==0x50 || type==0x60) { + + uint64_t uvalue=0; + uint8_t nlen=len; + while (--nlen) { + uvalue<<=8; + uvalue|=*cp++; + } + if (type==0x50) { + + switch (len-1) { + case 1: + + value=(signed char)uvalue; + break; + case 2: + +#ifdef DWS74_BUG + if (scaler==-2) { + value=(uint32_t)uvalue; + } else { + value=(int16_t)uvalue; + } +#else + value=(int16_t)uvalue; +#endif + break; + case 3: + case 4: + + value=(int32_t)uvalue; + break; + case 5: + case 6: + case 7: + case 8: + + value=(int64_t)uvalue; + break; + } + } else { + + value=uvalue; + } + + } else { + if (!(type&0xf0)) { + + + + if (len==9) { + + cp++; + uint32_t s1,s2; + s1=*cp<<16|*(cp+1)<<8|*(cp+2); + cp+=4; + s2=*cp<<16|*(cp+1)<<8|*(cp+2); + sprintf(&meter_id[index][0],"%u-%u",s1,s2); + } else { + + char *str=&meter_id[index][0]; + for (type=0; type= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; + } + return rVal; +} + +uint8_t sb_counter; + + +double CharToDouble(const char *str) +{ + + char strbuf[24]; + + strlcpy(strbuf, str, sizeof(strbuf)); + char *pt = strbuf; + while ((*pt != '\0') && isblank(*pt)) { pt++; } + + signed char sign = 1; + if (*pt == '-') { sign = -1; } + if (*pt == '-' || *pt=='+') { pt++; } + + double left = 0; + if (*pt != '.') { + left = atoi(pt); + while (isdigit(*pt)) { pt++; } + } + + double right = 0; + if (*pt == '.') { + pt++; + right = atoi(pt); + while (isdigit(*pt)) { + pt++; + right /= 10.0; + } + } + + double result = left + right; + if (sign < 0) { + return -result; + } + return result; +} + + + +void ebus_esc(uint8_t *ebus_buffer, unsigned char len) { + short count,count1; + for (count=0; countavailable()) { + meter_ss[meters]->read(); + } +} + + +void sml_shift_in(uint32_t meters,uint32_t shard) { + uint32_t count; + if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p') { + + for (count=0; countread(); + + if (meter_desc_p[meters].type=='o') { + smltbuf[meters][SML_BSIZ-1]=iob&0x7f; + } else if (meter_desc_p[meters].type=='s') { + smltbuf[meters][SML_BSIZ-1]=iob; + } else if (meter_desc_p[meters].type=='r') { + smltbuf[meters][SML_BSIZ-1]=iob; + } else if (meter_desc_p[meters].type=='m' || meter_desc_p[meters].type=='M') { + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=9) { + SML_Decode(meters); + sml_empty_receiver(meters); + meter_spos[meters]=0; + } + } else if (meter_desc_p[meters].type=='p') { + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=7) { + SML_Decode(meters); + sml_empty_receiver(meters); + meter_spos[meters]=0; + } + } else { + if (iob==EBUS_SYNC) { + + + if (meter_spos[meters]>4+5) { + + uint8_t tlen=smltbuf[meters][4]+5; + + if (smltbuf[meters][tlen]=ebus_CalculateCRC(smltbuf[meters],tlen)) { + ebus_esc(smltbuf[meters],tlen); + SML_Decode(meters); + } else { + + + } + } + meter_spos[meters]=0; + return; + } + smltbuf[meters][meter_spos[meters]] = iob; + meter_spos[meters]++; + if (meter_spos[meters]>=SML_BSIZ) { + meter_spos[meters]=0; + } + } + sb_counter++; + if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p') SML_Decode(meters); +} + + + +void SML_Poll(void) { +uint32_t meters; + + for (meters=0; metersavailable()) { + sml_shift_in(meters,0); + } + } + } +} + + +void SML_Decode(uint8_t index) { + const char *mp=(const char*)meter_p; + int8_t mindex; + uint8_t *cp; + uint8_t dindex=0,vindex=0; + delay(0); + while (mp != NULL) { + + + + mindex=((*mp)&7)-1; + + if (mindex<0 || mindex>=meters_used) mindex=0; + mp+=2; + if (*mp=='=' && *(mp+1)=='h') { + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + + if (index!=mindex) goto nextsect; + + + cp=&smltbuf[mindex][0]; + + + if (*mp=='=') { + + mp++; + + if (*mp=='m' && !sb_counter) { + + + mp++; + while (*mp==' ') mp++; + + double dvar; + uint8_t opr; + uint32_t ind; + ind=atoi(mp); + while (*mp>='0' && *mp<='9') mp++; + if (ind<1 || ind>SML_MAX_VARS) ind=1; + dvar=meter_vars[ind-1]; + for (uint8_t p=0;p<5;p++) { + if (*mp=='@') { + + meter_vars[vindex]=dvar; + mp++; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + break; + } + opr=*mp; + mp++; + uint8_t iflg=0; + if (*mp=='#') { + iflg=1; + mp++; + } + ind=atoi(mp); + while (*mp>='0' && *mp<='9') mp++; + if (ind<1 || ind>SML_MAX_VARS) ind=1; + switch (opr) { + case '+': + if (iflg) dvar+=ind; + else dvar+=meter_vars[ind-1]; + break; + case '-': + if (iflg) dvar-=ind; + else dvar-=meter_vars[ind-1]; + break; + case '*': + if (iflg) dvar*=ind; + else dvar*=meter_vars[ind-1]; + break; + case '/': + if (iflg) dvar/=ind; + else dvar/=meter_vars[ind-1]; + break; + } + while (*mp==' ') mp++; + if (*mp=='@') { + + meter_vars[vindex]=dvar; + mp++; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + break; + } + } + } else if (*mp=='d') { + + if (dindex='0' && *mp<='9') mp++; + if (ind<1 || ind>SML_MAX_VARS) ind=1; + uint32_t delay=atoi(mp)*1000; + uint32_t dtime=millis()-dtimes[dindex]; + if (dtime>delay) { + + dtimes[dindex]=millis(); + double vdiff = meter_vars[ind-1]-dvalues[dindex]; + dvalues[dindex]=meter_vars[ind-1]; + meter_vars[vindex]=(double)360000.0*vdiff/((double)dtime/10000.0); + + mp=strchr(mp,'@'); + if (mp) { + mp++; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + } + } + dindex++; + } + } else if (*mp=='h') { + + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + } else { + + uint8_t found=1; + uint32_t ebus_dval=99; + float mbus_dval=99; + while (*mp!='@') { + if (meter_desc_p[mindex].type=='o' || meter_desc_p[mindex].type=='c') { + if (*mp++!=*cp++) { + found=0; + } + } else { + if (meter_desc_p[mindex].type=='s') { + + uint8_t val = hexnibble(*mp++) << 4; + val |= hexnibble(*mp++); + if (val!=*cp++) { + found=0; + } + } else { + + + if (*mp=='x' && *(mp+1)=='x') { + + mp+=2; + cp++; + } else if (!strncmp(mp,"UUuuUUuu",8)) { + uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + ebus_dval=val; + mbus_dval=val; + mp+=8; + cp+=4; + } else if (*mp=='U' && *(mp+1)=='U' && *(mp+2)=='u' && *(mp+3)=='u'){ + uint16_t val = cp[1]|(cp[0]<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } else if (!strncmp(mp,"SSssSSss",8)) { + int32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + ebus_dval=val; + mbus_dval=val; + mp+=8; + cp+=4; + } else if (*mp=='u' && *(mp+1)=='u' && *(mp+2)=='U' && *(mp+3)=='U'){ + uint16_t val = cp[0]|(cp[1]<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } else if (*mp=='u' && *(mp+1)=='u') { + uint8_t val = *cp++; + mbus_dval=val; + ebus_dval=val; + mp+=2; + } else if (*mp=='s' && *(mp+1)=='s' && *(mp+2)=='S' && *(mp+3)=='S') { + int16_t val = *cp|(*(cp+1)<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } else if (*mp=='S' && *(mp+1)=='S' && *(mp+2)=='s' && *(mp+3)=='s') { + int16_t val = cp[1]|(cp[0]<<8); + mbus_dval=val; + ebus_dval=val; + mp+=4; + cp+=2; + } + else if (*mp=='s' && *(mp+1)=='s') { + int8_t val = *cp++; + mbus_dval=val; + ebus_dval=val; + mp+=2; + } + else if (!strncmp(mp,"ffffffff",8)) { + uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); + float *fp=(float*)&val; + ebus_dval=*fp; + mbus_dval=*fp; + mp+=8; + cp+=4; + } + else if (!strncmp(mp,"FFffFFff",8)) { + + uint32_t val= (cp[1]<<0)|(cp[0]<<8)|(cp[3]<<16)|(cp[2]<<24); + float *fp=(float*)&val; + ebus_dval=*fp; + mbus_dval=*fp; + mp+=8; + cp+=4; + } + else if (!strncmp(mp,"eeeeee",6)) { + uint32_t val=(cp[0]<<16)|(cp[1]<<8)|(cp[2]<<0); + mbus_dval=val; + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"vvvvvv",6)) { + mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/10.0); + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"cccccc",6)) { + mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/100.0); + mp+=6; + cp+=3; + } + else if (!strncmp(mp,"pppp",4)) { + mbus_dval=(float)((cp[0]<<8)|cp[1]); + mp+=4; + cp+=2; + } + else { + uint8_t val = hexnibble(*mp++) << 4; + val |= hexnibble(*mp++); + if (val!=*cp++) { + found=0; + } + } + } + } + } + if (found) { + + mp++; + if (*mp=='#') { + + mp++; + if (meter_desc_p[mindex].type=='o') { + for (uint8_t p=0;p>=shift; + ebus_dval&=1; + mp+=2; + } + if (*mp=='i') { + + mp++; + uint8_t mb_index=strtol((char*)mp,(char**)&mp,10); + if (mb_index!=meter_desc_p[mindex].index) { + goto nextsect; + } + uint16_t crc = MBUS_calculateCRC(&smltbuf[mindex][0],7); + if (lowByte(crc)!=smltbuf[mindex][7]) goto nextsect; + if (highByte(crc)!=smltbuf[mindex][8]) goto nextsect; + dval=mbus_dval; + + mp++; + } else { + if (meter_desc_p[mindex].type=='p') { + uint8_t crc = SML_PzemCrc(&smltbuf[mindex][0],6); + if (crc!=smltbuf[mindex][6]) goto nextsect; + dval=mbus_dval; + } else { + dval=ebus_dval; + } + } + + } +#ifdef USE_SML_MEDIAN_FILTER + if (meter_desc_p[mindex].flag&16) { + meter_vars[vindex]=sml_median(&sml_mf[vindex],dval); + } else { + meter_vars[vindex]=dval; + } +#else + meter_vars[vindex]=dval; +#endif + + + double fac=CharToDouble((char*)mp); + meter_vars[vindex]/=fac; + SML_Immediate_MQTT((const char*)mp,vindex,mindex); + } + } + } +nextsect: + + if (vindex=meters_used) lastmind=0; + while (mp != NULL) { + + mindex=((*mp)&7)-1; + if (mindex<0 || mindex>=meters_used) mindex=0; + mp+=2; + if (*mp=='=' && *(mp+1)=='h') { + mp+=2; + + if (json) { + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + + uint8_t i; + for (i=0;isml_counters[index].sml_debounce) { + RtcSettings.pulse_counter[index]++; + InjektCounterValue(sml_counters[index].sml_cnt_old_state,RtcSettings.pulse_counter[index]); + } + } else { + + sml_counters[index].sml_counter_ltime=millis(); + } +} + +void SML_CounterUpd1(void) { + SML_CounterUpd(0); +} + +void SML_CounterUpd2(void) { + SML_CounterUpd(1); +} + +void SML_CounterUpd3(void) { + SML_CounterUpd(2); +} + +void SML_CounterUpd4(void) { + SML_CounterUpd(3); +} + +#ifdef USE_SCRIPT +struct METER_DESC script_meter_desc[MAX_METERS]; +uint8_t *script_meter; +#endif + +#ifndef METER_DEF_SIZE +#define METER_DEF_SIZE 3000 +#endif + +bool Gpio_used(uint8_t gpiopin) { + for (uint16_t i=0;iM",-2,0); + if (meter_script==99) { + + if (script_meter) free(script_meter); + script_meter=0; + uint8_t *tp=0; + uint16_t index=0; + uint8_t section=0; + uint8_t srcpin=0; + char *lp=glob_script_mem.scriptptr; + sml_send_blocks=0; + while (lp) { + if (!section) { + if (*lp=='>' && *(lp+1)=='M') { + lp+=2; + meters_used=strtol(lp,0,10); + section=1; + uint32_t mlen=0; + for (uint32_t cnt=0;cnt') { + if (*(tp-1)=='|') *(tp-1)=0; + break; + } + if (*lp=='+') { + + + lp++; + index=*lp&7; + lp+=2; + if (index<1 || index>meters_used) goto next_line; + index--; + srcpin=strtol(lp,&lp,10); + if (Gpio_used(srcpin)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("gpio rx double define!")); +dddef_exit: + if (script_meter) free(script_meter); + script_meter=0; + meters_used=METERS_USED; + goto init10; + } + script_meter_desc[index].srcpin=srcpin; + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].type=*lp; + lp+=2; + script_meter_desc[index].flag=strtol(lp,&lp,10); + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].params=strtol(lp,&lp,10); + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].prefix[7]=0; + for (uint32_t cnt=0; cnt<8; cnt++) { + if (*lp==SCRIPT_EOL || *lp==',') { + script_meter_desc[index].prefix[cnt]=0; + break; + } + script_meter_desc[index].prefix[cnt]=*lp++; + } + if (*lp==',') { + lp++; + script_meter_desc[index].trxpin=strtol(lp,&lp,10); + if (Gpio_used(script_meter_desc[index].trxpin)) { + AddLog_P(LOG_LEVEL_INFO, PSTR("gpio tx double define!")); + goto dddef_exit; + } + if (*lp!=',') goto next_line; + lp++; + script_meter_desc[index].tsecs=strtol(lp,&lp,10); + if (*lp==',') { + lp++; + char txbuff[256]; + uint32_t txlen=0,tx_entries=1; + for (uint32_t cnt=0; cntmeters_used) goto next_line; + while (1) { + if (*lp==SCRIPT_EOL) { + if (*(tp-1)!='|') *tp++='|'; + goto next_line; + } + *tp++=*lp++; + index++; + if (index>=METER_DEF_SIZE) break; + } + } + + } + +next_line: + if (*lp==SCRIPT_EOL) { + lp++; + } else { + lp = strchr(lp, SCRIPT_EOL); + if (!lp) break; + lp++; + } + } + *tp=0; + meter_desc_p=script_meter_desc; + meter_p=script_meter; + } +#endif + +init10: + typedef void (*function)(); + function counter_callbacks[] = {SML_CounterUpd1,SML_CounterUpd2,SML_CounterUpd3,SML_CounterUpd4}; + uint8_t cindex=0; + + for (byte i = 0; i < MAX_COUNTERS; i++) { + RtcSettings.pulse_counter[i]=Settings.pulse_counter[i]; + sml_counters[i].sml_cnt_last_ts=millis(); + } + for (uint8_t meters=0; metersbegin(meter_desc_p[meters].params)) { + meter_ss[meters]->flush(); + } + if (meter_ss[meters]->hardwareSerial()) { + if (meter_desc_p[meters].type=='M') { + Serial.begin(meter_desc_p[meters].params, SERIAL_8E1); + } + ClaimSerial(); + } + + } + } + +} + + +#ifdef USE_SML_SCRIPT_CMD +uint32_t SML_SetBaud(uint32_t meter, uint32_t br) { + if (meter<1 || meter>meters_used) return 0; + meter--; + if (!meter_ss[meter]) return 0; + if (meter_ss[meter]->begin(br)) { + meter_ss[meter]->flush(); + } + if (meter_ss[meter]->hardwareSerial()) { + if (meter_desc_p[meter].type=='M') { + Serial.begin(br, SERIAL_8E1); + } + } + return 1; +} + +uint32_t SML_Write(uint32_t meter,char *hstr) { + if (meter<1 || meter>meters_used) return 0; + meter--; + if (!meter_ss[meter]) return 0; + SML_Send_Seq(meter,hstr); + return 1; +} +#endif + + +void SetDBGLed(uint8_t srcpin, uint8_t ledpin) { + pinMode(ledpin, OUTPUT); + if (digitalRead(srcpin)) { + digitalWrite(ledpin,LOW); + } else { + digitalWrite(ledpin,HIGH); + } +} + + +void SML_Counter_Poll(void) { +uint16_t meters,cindex=0; +uint32_t ctime=millis(); + + for (meters=0; meters0) { + if (ctime-sml_counters[cindex].sml_cnt_last_ts>meter_desc_p[meters].params) { + sml_counters[cindex].sml_cnt_last_ts=ctime; + + if (meter_desc_p[meters].flag&2) { + +#ifdef ANALOG_OPTO_SENSOR + if (ads1115_up) { + int16_t val = adc.read_sample(); + if (val>sml_counters[cindex].ana_max) sml_counters[cindex].ana_max=val; + if (val10) { + sml_counters[cindex].sml_cnt_last_ts=ctime; +#ifdef DEBUG_CNT_LED1 + if (cindex==0) SetDBGLed(meter_desc_p[meters].srcpin,DEBUG_CNT_LED1); +#endif +#ifdef DEBUG_CNT_LED2 + if (cindex==1) SetDBGLed(meter_desc_p[meters].srcpin,DEBUG_CNT_LED2); +#endif + } + } + cindex++; + } + } +} + +#ifdef USE_SCRIPT +char *SML_Get_Sequence(char *cp,uint32_t index) { + if (!index) return cp; + uint32_t cindex=0; + while (cp) { + cp=strchr(cp,','); + if (cp) { + cp++; + cindex++; + if (cindex==index) { + return cp; + } + } + } +} + +void SML_Check_Send(void) { + sml_100ms_cnt++; + char *cp; + for (uint32_t cnt=sml_desc_cnt; cnt=0 && script_meter_desc[cnt].txmem) { + if ((sml_100ms_cnt%script_meter_desc[cnt].tsecs)==0) { + if (script_meter_desc[cnt].max_index>1) { + script_meter_desc[cnt].index++; + if (script_meter_desc[cnt].index>=script_meter_desc[cnt].max_index) { + script_meter_desc[cnt].index=0; + sml_desc_cnt++; + } + cp=SML_Get_Sequence(script_meter_desc[cnt].txmem,script_meter_desc[cnt].index); + + } else { + cp=script_meter_desc[cnt].txmem; + + sml_desc_cnt++; + } + + SML_Send_Seq(cnt,cp); + if (sml_desc_cnt>=meters_used) { + sml_desc_cnt=0; + } + break; + } + } else { + sml_desc_cnt++; + } + + if (sml_desc_cnt>=meters_used) { + sml_desc_cnt=0; + } + } +} + +uint8_t sml_hexnibble(char chr) { + uint8_t rVal = 0; + if (isdigit(chr)) { + rVal = chr - '0'; + } else { + if (chr >= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; + if (chr >= 'a' && chr <= 'f') rVal = chr + 10 - 'a'; + } + return rVal; +} + + +void SML_Send_Seq(uint32_t meter,char *seq) { + uint8_t sbuff[32]; + uint8_t *ucp=sbuff,slen=0; + char *cp=seq; + while (*cp) { + if (!*cp || !*(cp+1)) break; + if (*cp==',') break; + uint8_t iob=(sml_hexnibble(*cp) << 4) | sml_hexnibble(*(cp+1)); + cp+=2; + *ucp++=iob; + slen++; + if (slen>=sizeof(sbuff)) break; + } + if (script_meter_desc[meter].type=='m' || script_meter_desc[meter].type=='M') { + *ucp++=0; + *ucp++=2; + + uint16_t crc = MBUS_calculateCRC(sbuff,6); + *ucp++=lowByte(crc); + *ucp++=highByte(crc); + slen+=4; + } + if (script_meter_desc[meter].type=='o') { + for (uint32_t cnt=0;cntwrite(sbuff,slen); +} +#endif + +uint16_t MBUS_calculateCRC(uint8_t *frame, uint8_t num) { + uint16_t crc, flag; + crc = 0xFFFF; + for (uint32_t i = 0; i < num; i++) { + crc ^= frame[i]; + for (uint32_t j = 8; j; j--) { + if ((crc & 0x0001) != 0) { + crc >>= 1; + crc ^= 0xA001; + } else { + crc >>= 1; + } + } + } + return crc; +} + +uint8_t SML_PzemCrc(uint8_t *data, uint8_t len) { + uint16_t crc = 0; + for (uint32_t i = 0; i < len; i++) crc += *data++; + return (uint8_t)(crc & 0xFF); +} + + +uint8_t CalcEvenParity(uint8_t data) { +uint8_t parity=0; + + while(data) { + parity^=(data &1); + data>>=1; + } + return parity; +} +# 2361 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_53_sml.ino" +bool XSNS_53_cmd(void) { + bool serviced = true; + if (XdrvMailbox.data_len > 0) { + char *cp=XdrvMailbox.data; + if (*cp=='d') { + + cp++; + uint8_t index=atoi(cp); + if ((index&7)>meters_used) index=1; + if (index>0 && meter_desc_p[(index&7)-1].type=='c') { + index=0; + } + dump2log=index; + ResponseTime_P(PSTR(",\"SML\":{\"CMD\":\"dump: %d\"}}"),dump2log); + } else if (*cp=='c') { + + cp++; + uint8_t index=*cp&7; + if (index<1 || index>MAX_COUNTERS) index=1; + cp++; + while (*cp==' ') cp++; + if (isdigit(*cp)) { + uint32_t cval=atoi(cp); + while (isdigit(*cp)) cp++; + RtcSettings.pulse_counter[index-1]=cval; + uint8_t cindex=0; + for (uint8_t meters=0; metersaddress, INA226_REG_CALIBRATION, si->calibrationValue); + +} + + + + + + +bool Ina226TestPresence(uint8_t device) +{ + + + + uint16_t config = I2cRead16( slaveInfo[device].address, INA226_REG_CONFIG ); + + + if (config != slaveInfo[device].config) + return false; + + return true; + +} + +void Ina226ResetActive(void) +{ + Ina226SlaveInfo_t *p = slaveInfo; + + for (uint32_t i = 0; i < INA226_MAX_ADDRESSES; i++) { + p = &slaveInfo[i]; + + uint8_t addr = p->address; + if (addr) { + I2cResetActive(addr); + } + } +} + + + + + +void Ina226Init() +{ + uint32_t i; + + slavesFound = 0; + + Ina226SlaveInfo_t *p = slaveInfo; +# 215 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_54_ina226.ino" + for (i = 0; i < 4; i++){ + *p = {0}; + } + + + + + + for (i = 0; i < INA226_MAX_ADDRESSES; i++){ + uint8_t addr = pgm_read_byte(probeAddresses + i); + + if (I2cActive(addr)) { continue; } + + + + + if (!Settings.ina226_i_fs[i]) + continue; + + + + + + + if (!I2cWrite16( addr, INA226_REG_CONFIG, INA226_CONFIG_RESET)){ + + AddLog_P2( LOG_LEVEL_DEBUG, "No INA226 at address: %02X", addr); + continue; + } + + + + uint16_t config = I2cRead16( addr, INA226_REG_CONFIG ); + + + if (INA226_RES_CONFIG != config) + continue; + + + config = INA226_DEF_CONFIG; + + + if (!I2cWrite16( addr, INA226_REG_CONFIG, config)) + continue; + + + p = &slaveInfo[i]; + + p->address = addr; + + p->config = config; + + + p->i_lsb = (((float) Settings.ina226_i_fs[i])/10.0f)/32768.0f; + + + + uint32_t r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[i]); + + + + p->calibrationValue = ((uint16_t) (0.00512/(p->i_lsb * r_shunt_uohms/1000000.0f))); + + p->present = true; + + + Ina226SetCalibration(i); + + I2cSetActiveFound(addr, Ina226Str); + + slavesFound++; + } +} + + + + + +float Ina226ReadBus_v(uint8_t device) +{ + uint8_t addr = slaveInfo[device].address; + int16_t reg_bus_v = I2cReadS16( addr, INA226_REG_BUSVOLTAGE); + + float result = ((float) reg_bus_v) * 0.00125f; + + return result; + +} + + + + + +float Ina226ReadShunt_i(uint8_t device) +{ + uint8_t addr = slaveInfo[device].address; + int16_t reg_shunt_i = I2cReadS16( addr, INA226_REG_CURRENT); + + float result = ((float) reg_shunt_i) * slaveInfo[device].i_lsb; + + return result; +} + + + + + +float Ina226ReadPower_w(uint8_t device) +{ + uint8_t addr = slaveInfo[device].address; + int16_t reg_shunt_i = I2cReadS16( addr, INA226_REG_POWER); + + float result = ((float) reg_shunt_i) * (slaveInfo[device].i_lsb * 25.0); + + return result; +} + + + + + + +void Ina226Read(uint8_t device) +{ + + voltages[device] = Ina226ReadBus_v(device); + currents[device] = Ina226ReadShunt_i(device); + powers[device] = Ina226ReadPower_w(device); + + + + +} + + + + + +void Ina226EverySecond() +{ + + for (uint8_t device = 0; device < INA226_MAX_ADDRESSES; device++){ + + if (slavesFound && slaveInfo[device].present && Ina226TestPresence(device)){ + Ina226Read(device); + } + else { + powers[device] = currents[device] = voltages[device] = 0.0f; + + + + + + + slaveInfo[device].present = false; + } + } +} + + + + + +bool Ina226CommandSensor() +{ + bool serviced = true; + bool show_config = false; + char param_str[64]; + char *cp, *params[4]; + uint8_t i, param_count, device, p1 = XdrvMailbox.payload; + uint32_t r_shunt_uohms; + uint16_t compact_r_shunt_uohms; + + + + + + if (XdrvMailbox.data_len > 62){ + return false; + } + + strncpy(param_str, XdrvMailbox.data, XdrvMailbox.data_len + 1); + param_str[XdrvMailbox.data_len] = 0; + + + for (cp = param_str, i = 0, param_count = 0; *cp && (i < XdrvMailbox.data_len + 1) && (param_count <= 3); i++) + if (param_str[i] == ' ' || param_str[i] == ',' || param_str[i] == 0){ + param_str[i] = 0; + params[param_count] = cp; + + param_count++; + cp = param_str + i + 1; + } + + + if (p1 < 10 || p1 >= 50){ + + switch (p1){ + case 1: + Ina226ResetActive(); + Ina226Init(); + Response_P(PSTR("{\"Sensor54-Command-Result\":{\"SlavesFound\":%d}}"),slavesFound); + break; + + case 2: + restart_flag = 2; + Response_P(PSTR("{\"Sensor54-Command-Result\":{\"Restart_flag\":%d}}"),restart_flag); + break; + + default: + serviced = false; + } + } + else if (p1 < 50){ + + device = (p1 / 10) - 1; + switch (p1 % 10){ + case 0: + show_config = true; + break; + + case 1: + r_shunt_uohms = (uint32_t) ((CharToFloat(params[1])) * 1000000.0f); + + + + if (r_shunt_uohms > 32767){ + uint32_t r_shunt_mohms = r_shunt_uohms/1000UL; + Settings.ina226_r_shunt[device] = (uint16_t) (r_shunt_mohms | 0x8000); + } + else + Settings.ina226_r_shunt[device] = (uint16_t) r_shunt_uohms; + + + show_config = true; + break; + + case 2: + Settings.ina226_i_fs[device] = (uint16_t) ((CharToFloat(params[1])) * 10.0f); + + show_config = true; + break; + + + default: + serviced = false; + break; + } + } + else + serviced = false; + + if (show_config) { + char shunt_r_str[16]; + char fs_i_str[16]; + + + r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[device]); + dtostrfd(((float)r_shunt_uohms)/1000000.0f, 6, shunt_r_str); + + dtostrfd(((float)Settings.ina226_i_fs[device])/10.0f, 1, fs_i_str); + + Response_P(PSTR("{\"Sensor54-device-settings-%d\":{\"SHUNT_R\":%s,\"FS_I\":%s}}"), + device + 1, shunt_r_str, fs_i_str); + } + + return serviced; +} + + + + + +#ifdef USE_WEBSERVER +const char HTTP_SNS_INA226_DATA[] PROGMEM = + "{s}%s " D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" + "{s}%s " D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" + "{s}%s " D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; +#endif + +void Ina226Show(bool json) +{ + int i, num_found; + for (num_found = 0, i = 0; i < INA226_MAX_ADDRESSES; i++) { + + if (!slaveInfo[i].present) + continue; + + num_found++; + + char voltage[16]; + dtostrfd(voltages[i], Settings.flag2.voltage_resolution, voltage); + char current[16]; + dtostrfd(currents[i], Settings.flag2.current_resolution, current); + char power[16]; + dtostrfd(powers[i], Settings.flag2.wattage_resolution, power); + char name[16]; + snprintf_P(name, sizeof(name), PSTR("INA226%c%d"),IndexSeparator(), i + 1); + + + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"Id\":%d,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), + name, i, voltage, current, power); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, voltage); + DomoticzSensor(DZ_CURRENT, current); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_INA226_DATA, name, voltage, name, current, name, power); +#endif + } + + } + +} +# 546 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_54_ina226.ino" +bool Xsns54(byte callback_id) +{ + if (!I2cEnabled(XI2C_35)) { return false; } + + + bool result = false; + + + switch (callback_id) { + case FUNC_EVERY_SECOND: + Ina226EverySecond(); + break; + case FUNC_JSON_APPEND: + Ina226Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Ina226Show(0); + break; +#endif + case FUNC_COMMAND_SENSOR: + if (XSNS_54 == XdrvMailbox.index) { + result = Ina226CommandSensor(); + } + break; + case FUNC_INIT: + Ina226Init(); + break; + } + + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_55_hih_series.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_55_hih_series.ino" +#ifdef USE_I2C +#ifdef USE_HIH6 +# 33 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_55_hih_series.ino" +#define XSNS_55 55 +#define XI2C_36 36 + +#define HIH6_ADDR 0x27 + +struct HIH6 { + float temperature = 0; + float humidity = 0; + uint8_t valid = 0; + uint8_t type = 0; + char types[4] = "HIH"; +} Hih6; + +bool Hih6Read(void) +{ + Wire.beginTransmission(HIH6_ADDR); + if (Wire.endTransmission() != 0) { return false; } + + delay(40); + + uint8_t data[4]; + Wire.requestFrom(HIH6_ADDR, 4); + if (4 == Wire.available()) { + data[0] = Wire.read(); + data[1] = Wire.read(); + data[2] = Wire.read(); + data[3] = Wire.read(); + } else { return false; } + + + + Hih6.humidity = ConvertHumidity(((float)(((data[0] & 0x3F) << 8) | data[1]) * 100.0) / 16383.0); + + int temp = ((data[2] << 8) | (data[3] & 0xFC)) / 4; + Hih6.temperature = ConvertTemp(((float)temp / 16384.0) * 165.0 - 40.0); + + Hih6.valid = SENSOR_MAX_MISS; + return true; +} + + + +void Hih6Detect(void) +{ + if (I2cActive(HIH6_ADDR)) { return; } + + if (uptime < 2) { delay(20); } + Hih6.type = Hih6Read(); + if (Hih6.type) { + I2cSetActiveFound(HIH6_ADDR, Hih6.types); + } +} + +void Hih6EverySecond(void) +{ + if (uptime &1) { + + if (!Hih6Read()) { + AddLogMissed(Hih6.types, Hih6.valid); + } + } +} + +void Hih6Show(bool json) +{ + if (Hih6.valid) { + char temperature[33]; + dtostrfd(Hih6.temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(Hih6.humidity, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, Hih6.types, temperature, humidity); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, Hih6.temperature); + KnxSensor(KNX_HUMIDITY, Hih6.humidity); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, Hih6.types, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, Hih6.types, humidity); +#endif + } + } +} + + + + + +bool Xsns55(uint8_t function) +{ + if (!I2cEnabled(XI2C_36)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Hih6Detect(); + } + else if (Hih6.type) { + switch (function) { + case FUNC_EVERY_SECOND: + Hih6EverySecond(); + break; + case FUNC_JSON_APPEND: + Hih6Show(1); + break; + #ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Hih6Show(0); + break; + #endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_56_hpma.ino" +# 21 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_56_hpma.ino" +#ifdef USE_HPMA +# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_56_hpma.ino" +#define XSNS_56 56 + +#include +#include + +TasmotaSerial *HpmaSerial; +HPMA115S0 *hpma115S0; + +uint8_t hpma_type = 1; +uint8_t hpma_valid = 0; + +struct hpmadata { + unsigned int pm10; + unsigned int pm2_5; +} hpma_data; + + + +void HpmaSecond(void) +{ + unsigned int pm2_5, pm10; + + + + + if (hpma115S0->ReadParticleMeasurement(&pm2_5, &pm10)) { + hpma_data.pm2_5 = pm2_5; + hpma_data.pm10 = pm10; + hpma_valid = 1; + } + +} + +void HpmaInit(void) +{ + hpma_type = 0; + if (pin[GPIO_HPMA_RX] < 99 && pin[GPIO_HPMA_TX] < 99) { + HpmaSerial = new TasmotaSerial(pin[GPIO_HPMA_RX], pin[GPIO_HPMA_TX], 1); + hpma115S0 = new HPMA115S0(*HpmaSerial); + + if (HpmaSerial->begin(9600)) { + if (HpmaSerial->hardwareSerial()) { + ClaimSerial(); + } + hpma_type = 1; + hpma115S0->Init(); + hpma115S0->StartParticleMeasurement(); + } + } +} + +#ifdef USE_WEBSERVER +const char HTTP_HPMA_SNS[] PROGMEM = + "{s}HPMA " D_ENVIRONMENTAL_CONCENTRATION "2.5 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" + "{s}HPMA " D_ENVIRONMENTAL_CONCENTRATION "10 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; +#endif + +void HpmaShow(bool json) +{ + if (hpma_valid) { + char pm10[33]; + snprintf_P(pm10, 33, PSTR("%d"), hpma_data.pm10); + char pm2_5[33]; + snprintf_P(pm2_5, 33, PSTR("%d"), hpma_data.pm2_5); + + if (json) { + ResponseAppend_P(PSTR(",\"HPMA\":{\"PM2.5\":%d,\"PM10\":%d}"), hpma_data.pm2_5, hpma_data.pm10); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { + DomoticzSensor(DZ_VOLTAGE, pm2_5); + DomoticzSensor(DZ_CURRENT, pm10); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_HPMA_SNS, pm2_5, pm10); +#endif + } + } +} + + + + + +bool Xsns56(uint8_t function) +{ + bool result = false; + + if (hpma_type) { + switch (function) { + case FUNC_INIT: + HpmaInit(); + break; + case FUNC_EVERY_SECOND: + HpmaSecond(); + break; + case FUNC_COMMAND_SENSOR: + if (XSNS_56 == XdrvMailbox.index) { + return true; + } + break; + case FUNC_JSON_APPEND: + HpmaShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HpmaShow(0); + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_57_tsl2591.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_57_tsl2591.ino" +#ifdef USE_I2C +#ifdef USE_TSL2591 + + + + + + + +#define XSNS_57 57 +#define XI2C_40 40 + +#define TSL2591_ADDRESS 0x29 + +#include + +Adafruit_TSL2591 tsl = Adafruit_TSL2591(); + +uint8_t tsl2591_type = 0; +uint8_t tsl2591_valid = 0; +float tsl2591_lux = 0; + +void Tsl2591Init(void) +{ + + if (I2cSetDevice(0x29)) { + if (tsl.begin()) { + tsl.setGain(TSL2591_GAIN_MED); + tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS); + tsl2591_type = 1; + I2cSetActiveFound(TSL2591_ADDRESS, "TSL2591"); + } + } +} + +bool Tsl2591Read(void) +{ + uint32_t lum = tsl.getFullLuminosity(); + uint16_t ir, full; + ir = lum >> 16; + full = lum & 0xFFFF; + tsl2591_lux = tsl.calculateLux(full, ir); + tsl2591_valid = 1; +} + +void Tsl2591EverySecond(void) +{ + Tsl2591Read(); +} + +#ifdef USE_WEBSERVER +const char HTTP_SNS_TSL2591[] PROGMEM = + "{s}TSL2591 " D_ILLUMINANCE "{m}%s " D_UNIT_LUX "{e}"; +#endif + +void Tsl2591Show(bool json) +{ + if (tsl2591_valid) { + char lux_str[10]; + dtostrf(tsl2591_lux, sizeof(lux_str)-1, 3, lux_str); + if (json) { + ResponseAppend_P(PSTR(",\"TSL2591\":{\"" D_JSON_ILLUMINANCE "\":%s}"), lux_str); +#ifdef USE_DOMOTICZ + if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, tsl2591_lux); } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TSL2591, lux_str); +#endif + } + } +} + + + + + +bool Xsns57(uint8_t function) +{ + if (!I2cEnabled(XI2C_40)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Tsl2591Init(); + } + else if (tsl2591_type) { + switch (function) { + case FUNC_EVERY_SECOND: + Tsl2591EverySecond(); + break; + case FUNC_JSON_APPEND: + Tsl2591Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Tsl2591Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_58_dht12.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_58_dht12.ino" +#ifdef USE_I2C +#ifdef USE_DHT12 + + + + + + +#define XSNS_58 58 +#define XI2C_41 41 + +#define DHT12_ADDR 0x5C + +struct DHT12 { + float temperature = NAN; + float humidity = NAN; + uint8_t valid = 0; + uint8_t count = 0; + char name[6] = "DHT12"; +} Dht12; + +bool Dht12Read(void) +{ + if (Dht12.valid) { Dht12.valid--; } + + Wire.beginTransmission(DHT12_ADDR); + Wire.write(0); + if (Wire.endTransmission() != 0) { return false; } + + delay(50); + + Wire.requestFrom(DHT12_ADDR, 5); + delay(5); + uint8_t humidity = Wire.read(); + uint8_t humidityTenth = Wire.read(); + uint8_t temp = Wire.read(); + uint8_t tempTenth = Wire.read(); + uint8_t checksum = Wire.read(); + + Dht12.humidity = ConvertHumidity( (float) humidity + (float) humidityTenth/(float) 10.0 ); + Dht12.temperature = ConvertTemp( (float) temp + (float) tempTenth/(float) 10.0 ); + + if (isnan(Dht12.temperature) || isnan(Dht12.humidity)) { return false; } + + Dht12.valid = SENSOR_MAX_MISS; + return true; +} + + + +void Dht12Detect(void) +{ + if (I2cActive(DHT12_ADDR)) { return; } + + if (Dht12Read()) { + I2cSetActiveFound(DHT12_ADDR, Dht12.name); + Dht12.count = 1; + } +} + +void Dht12EverySecond(void) +{ + if (uptime &1) { + + if (!Dht12Read()) { + AddLogMissed(Dht12.name, Dht12.valid); + } + } +} + +void Dht12Show(bool json) +{ + if (Dht12.valid) { + char temperature[33]; + dtostrfd(Dht12.temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(Dht12.humidity, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, Dht12.name, temperature, humidity); +#ifdef USE_DOMOTICZ + if ((0 == tele_period)) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, Dht12.temperature); + KnxSensor(KNX_HUMIDITY, Dht12.humidity); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, Dht12.name, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, Dht12.name, humidity); +#endif + } + } +} + + + + + +bool Xsns58(uint8_t function) +{ + if (!I2cEnabled(XI2C_41)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Dht12Detect(); + } + else if (Dht12.count) { + switch (function) { + case FUNC_EVERY_SECOND: + Dht12EverySecond(); + break; + case FUNC_JSON_APPEND: + Dht12Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + Dht12Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_59_ds1624.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_59_ds1624.ino" +#ifdef USE_I2C +#ifdef USE_DS1624 + + + + + + + +#define XSNS_59 59 +#define XI2C_42 42 + +#define DS1624_MEM_REGISTER 0x17 +#define DS1624_CONF_REGISTER 0xAC +#define DS1624_TEMP_REGISTER 0xAA +#define DS1624_START_REGISTER 0xEE +#define DS1624_STOP_REGISTER 0x22 + +#define DS1621_COUNTER_REGISTER 0xA8 +#define DS1621_SLOPE_REGISTER 0xA9 + +#define DS1621_CFG_1SHOT (1<<0) +#define DS1621_CFG_DONE (1<<7) + +enum { + DS1624_TYPE_DS1624, + DS1624_TYPE_DS1621 +}; + +#define DS1624_MAX_SENSORS 8 + +bool ds1624_init = false; + +struct { + float value; + uint8_t type; + int errcnt; + int misscnt; + bool valid; + char name[9]; +} ds1624_sns[DS1624_MAX_SENSORS]; + +uint32_t DS1624_Idx2Addr(uint32_t idx) { + return 0x48 + idx; +} + +int DS1624_Restart(uint8_t config, uint32_t idx) { + uint32_t addr = DS1624_Idx2Addr(idx); + if ((config & 1) == 1) { + config &= ~(DS1621_CFG_DONE|DS1621_CFG_1SHOT); + I2cWrite8(addr, DS1624_CONF_REGISTER, config); + delay(10); + AddLog_P2(LOG_LEVEL_ERROR, "%s addr %x is reset, reconfig: %x", ds1624_sns[idx].name, addr, config); + } + I2cValidRead(addr, DS1624_START_REGISTER, 1); +} + +void DS1624_HotPlugUp(uint32_t idx) +{ + uint32_t addr = DS1624_Idx2Addr(idx); + + if (I2cActive(addr)) { return; } + if (!I2cSetDevice(addr)) { return; } + + uint8_t config; + if (I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) { + uint8_t tmp; + ds1624_sns[idx].type = (I2cValidRead8(&tmp, addr, DS1624_MEM_REGISTER)) ? DS1624_TYPE_DS1624 : DS1624_TYPE_DS1621; + + snprintf_P(ds1624_sns[idx].name, sizeof(ds1624_sns[idx].name), PSTR("DS162%c%c%d"), + (ds1624_sns[idx].type == DS1624_TYPE_DS1621) ? '1' : '4', IndexSeparator(), idx); + I2cSetActiveFound(addr, ds1624_sns[idx].name); + + ds1624_sns[idx].valid = true; + ds1624_sns[idx].errcnt = 0; + ds1624_sns[idx].misscnt = 0; + DS1624_Restart(config,idx); + AddLog_P2(LOG_LEVEL_INFO, "Hot Plug %s addr %x config: %x", ds1624_sns[idx].name, addr, config); + } +} + +void DS1624_HotPlugDown(int idx) +{ + uint32_t addr = DS1624_Idx2Addr(idx); + if (!I2cActive(addr)) { return; } + I2cResetActive(addr); + ds1624_sns[idx].valid = false; + AddLog_P2(LOG_LEVEL_INFO, "Hot UnPlug %s", ds1624_sns[idx].name); +} + +bool DS1624GetTemp(float *value, int idx) +{ + uint32_t addr = DS1624_Idx2Addr(idx); + + uint8_t config; + if (!I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) { + ds1624_sns[idx].misscnt++; + AddLog_P2(LOG_LEVEL_INFO, "%s device missing (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].misscnt); + return false; + } + ds1624_sns[idx].misscnt=0; + if (config & (DS1621_CFG_1SHOT|DS1621_CFG_DONE)) { + ds1624_sns[idx].errcnt++; + AddLog_P2(LOG_LEVEL_INFO, "%s config error, restart... (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].errcnt); + DS1624_Restart(config, idx); + return false; + } + + uint16_t t; + if (!I2cValidRead16(&t, DS1624_Idx2Addr(idx), DS1624_TEMP_REGISTER)) { return false; } + if (ds1624_sns[idx].type == DS1624_TYPE_DS1624) { + *value = ((float)(int8_t)(t>>8)) + ((t>>4)&0xf)*0.0625; + } else { + + *value = ((float)(int8_t)(t>>8)); + uint8_t remain; + if (!I2cValidRead8(&remain, addr, DS1621_COUNTER_REGISTER)) { return true; } + uint8_t perc; + if (!I2cValidRead8(&perc, addr, DS1621_SLOPE_REGISTER)) { return true; } + float fix=(float)(perc - remain)/(float)perc; + *value+=fix; + } + ds1624_sns[idx].errcnt=0; + config &= ~(DS1621_CFG_DONE); + I2cWrite8(addr, DS1624_CONF_REGISTER, config); + return true; +} + +void DS1624HotPlugScan(void) +{ + uint16_t t; + + for (uint32_t idx = 0; idx < DS1624_MAX_SENSORS; idx++) { + uint32_t addr = DS1624_Idx2Addr(idx); + if (I2cActive(addr) && !ds1624_sns[idx].valid) { + continue; + } + if (ds1624_sns[idx].valid) { + if ((ds1624_sns[idx].misscnt>2)||(ds1624_sns[idx].errcnt>2)) { + DS1624_HotPlugDown(idx); + continue; + } + } + DS1624_HotPlugUp(idx); + } +} + +void DS1624EverySecond(void) +{ + float t; + for (uint32_t i = 0; i < DS1624_MAX_SENSORS; i++) { + if (!ds1624_sns[i].valid) { continue; } + if (!DS1624GetTemp(&t, i)) { continue; } + ds1624_sns[i].value = ConvertTemp(t); + } +} + +void DS1624Show(bool json) +{ + char temperature[33]; + bool once = true; + + for (uint32_t i = 0; i < DS1624_MAX_SENSORS; i++) { + if (!ds1624_sns[i].valid) { continue; } + + dtostrfd(ds1624_sns[i].value, Settings.flag2.temperature_resolution, temperature); + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"), ds1624_sns[i].name, temperature); + if ((0 == tele_period) && once) { +#ifdef USE_DOMOTICZ + DomoticzSensor(DZ_TEMP, temperature); +#endif +#ifdef USE_KNX + KnxSensor(KNX_TEMPERATURE, temperature); +#endif + once = false; + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, ds1624_sns[i].name, temperature, TempUnit()); +#endif + } + } +} + + + + + +bool Xsns59(uint8_t function) +{ + if (!I2cEnabled(XI2C_42)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + if (!ds1624_init) { + memset(ds1624_sns, 0, sizeof(ds1624_sns)); + ds1624_init = true; + DS1624HotPlugScan(); + } + } + switch (function) { + case FUNC_HOTPLUG_SCAN: + DS1624HotPlugScan(); + break; + case FUNC_EVERY_SECOND: + DS1624EverySecond(); + break; + case FUNC_JSON_APPEND: + DS1624Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DS1624Show(0); + break; +#endif + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_60_GPS.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_60_GPS.ino" +#ifdef USE_GPS +# 113 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_60_GPS.ino" +#define XSNS_60 60 + +#include "NTPServer.h" +#include "NTPPacket.h" + + + + + +#define D_CMND_UBX "UBX" + +const char S_JSON_UBX_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_UBX "%s\":%d}"; + +const char kUBXTypes[] PROGMEM = "UBX"; + +#define UBX_LAT_LON_THRESHOLD 1000 + +#define UBX_SERIAL_BUFFER_SIZE 256 +#define UBX_TCP_PORT 1234 + + + + + +const char UBLOX_INIT[] PROGMEM = { + + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x24, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x2B, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x32, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x39, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x04,0x40, + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x05,0x47, + + + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xDC, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xB9, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xC0, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x92, + + + + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x13,0xBE, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x14,0xC5, + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x01,0x00,0x00,0x00,0x00,0x32,0x97, + + + + + + +}; + +char UBX_name[4]; + +struct UBX_t { + const char UBX_HEADER[2] = { 0xB5, 0x62 }; + const char NAV_POSLLH_HEADER[2] = { 0x01, 0x02 }; + const char NAV_STATUS_HEADER[2] = { 0x01, 0x03 }; + const char NAV_TIME_HEADER[2] = { 0x01, 0x21 }; + + struct entry_t { + int32_t lat; + int32_t lon; + uint32_t time; + }; + + union { + entry_t values; + uint8_t bytes[sizeof(entry_t)]; + } rec_buffer; + + struct POLL_MSG { + uint8_t cls; + uint8_t id; + uint16_t zero; + }; + + struct NAV_POSLLH { + uint8_t cls; + uint8_t id; + uint16_t len; + uint32_t iTOW; + int32_t lon; + int32_t lat; + int32_t alt; + int32_t hMSL; + uint32_t hAcc; + uint32_t vAcc; + }; + + struct NAV_STATUS { + uint8_t cls; + uint8_t id; + uint16_t len; + uint32_t iTOW; + uint8_t gpsFix; + uint8_t flags; + uint8_t fixStat; + uint8_t flags2; + uint32_t ttff; + uint32_t msss; + }; + + struct NAV_TIME_UTC { + uint8_t cls; + uint8_t id; + uint16_t len; + uint32_t iTOW; + uint32_t tAcc; + int32_t nano; + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t min; + uint8_t sec; + struct { + uint8_t UTC:1; + uint8_t WKN:1; + uint8_t TOW:1; + uint8_t padding:5; + } valid; + }; + + struct CFG_RATE { + uint8_t cls; + uint8_t id; + uint16_t len; + uint16_t measRate; + uint16_t navRate; + uint16_t timeRef; + char CK[2]; + }; + + struct { + uint32_t last_iTOW; + int32_t last_alt; + uint32_t last_hAcc; + uint32_t last_vAcc; + uint8_t gpsFix; + uint8_t non_empty_loops; + uint16_t log_interval; + } state; + + struct { + uint32_t init:1; + uint32_t filter_noise:1; + uint32_t send_when_new:1; + uint32_t send_UI_only:1; + uint32_t runningNTP:1; + uint32_t forceUTCupdate:1; + uint32_t runningVPort:1; + + } mode; + + union { + NAV_POSLLH navPosllh; + NAV_STATUS navStatus; + NAV_TIME_UTC navTime; + POLL_MSG pollMsg; + CFG_RATE cfgRate; + } Message; + + uint8_t TCPbuf[UBX_SERIAL_BUFFER_SIZE]; + size_t TCPbufSize; +} UBX; + +enum UBXMsgType { + MT_NONE, + MT_NAV_POSLLH, + MT_NAV_STATUS, + MT_NAV_TIME, + MT_POLL +}; + +#ifdef USE_FLOG +FLOG *Flog = nullptr; +#endif +TasmotaSerial *UBXSerial; + +NtpServer timeServer(PortUdp); + +WiFiServer vPortServer(UBX_TCP_PORT); +WiFiClient vPortClient; + + + + + +void UBXcalcChecksum(char* CK, size_t msgSize) +{ + memset(CK, 0, 2); + for (int i = 0; i < msgSize; i++) { + CK[0] += ((char*)(&UBX.Message))[i]; + CK[1] += CK[0]; + } +} + +bool UBXcompareMsgHeader(const char* msgHeader) +{ + char* ptr = (char*)(&UBX.Message); + return ptr[0] == msgHeader[0] && ptr[1] == msgHeader[1]; +} + +void UBXinitCFG(void) +{ + for (uint32_t i = 0; i < sizeof(UBLOX_INIT); i++) { + UBXSerial->write( pgm_read_byte(UBLOX_INIT+i) ); + } + DEBUG_SENSOR_LOG(PSTR("UBX: turn off NMEA")); +} + +void UBXTriggerTele(void) +{ + mqtt_data[0] = '\0'; + if (MqttShowSensor()) { + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +#ifdef USE_RULES + RulesTeleperiod(); +#endif + } +} + + + +void UBXDetect(void) +{ + UBX.mode.init = 0; + if ((pin[GPIO_GPS_RX] < 99) && (pin[GPIO_GPS_TX] < 99)) { + UBXSerial = new TasmotaSerial(pin[GPIO_GPS_RX], pin[GPIO_GPS_TX], 1, 0, UBX_SERIAL_BUFFER_SIZE); + if (UBXSerial->begin(9600)) { + DEBUG_SENSOR_LOG(PSTR("UBX: started serial")); + if (UBXSerial->hardwareSerial()) { + ClaimSerial(); + DEBUG_SENSOR_LOG(PSTR("UBX: claim HW")); + } + } + } + else { + return; + } + + UBXinitCFG(); + UBX.mode.init = 1; + +#ifdef USE_FLOG + if (!Flog) { + Flog = new FLOG; + Flog->init(); + } +#endif + + UBX.state.log_interval = 10; + UBX.mode.send_UI_only = true; + UBXTriggerTele(); +} + +uint32_t UBXprocessGPS() +{ + static uint32_t fpos = 0; + static char checksum[2]; + static uint8_t currentMsgType = MT_NONE; + static size_t payloadSize = sizeof(UBX.Message); + + + uint32_t data_bytes = 0; + while ( UBXSerial->available() ) { + data_bytes++; + byte c = UBXSerial->read(); + if (UBX.mode.runningVPort){ + UBX.TCPbuf[data_bytes-1] = c; + UBX.TCPbufSize = data_bytes; + } + if ( fpos < 2 ) { + + if ( c == UBX.UBX_HEADER[fpos] ) { + fpos++; + } else { + fpos = 0; + } + } else { + + + + + + if ( (fpos-2) < payloadSize ) { + ((char*)(&UBX.Message))[fpos-2] = c; + } + fpos++; + + if ( fpos == 4 ) { + + + if ( UBXcompareMsgHeader(UBX.NAV_POSLLH_HEADER) ) { + currentMsgType = MT_NAV_POSLLH; + payloadSize = sizeof(UBX_t::NAV_POSLLH); + DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_POSLLH")); + } + else if ( UBXcompareMsgHeader(UBX.NAV_STATUS_HEADER) ) { + currentMsgType = MT_NAV_STATUS; + payloadSize = sizeof(UBX_t::NAV_STATUS); + DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_STATUS")); + } + else if ( UBXcompareMsgHeader(UBX.NAV_TIME_HEADER) ) { + currentMsgType = MT_NAV_TIME; + payloadSize = sizeof(UBX_t::NAV_TIME_UTC); + DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_TIME_UTC")); + } + else { + + fpos = 0; + continue; + } + } + + if ( fpos == (payloadSize+2) ) { + + + UBXcalcChecksum(checksum, payloadSize); + } + else if ( fpos == (payloadSize+3) ) { + + + if ( c != checksum[0] ) { + + fpos = 0; + } + } + else if ( fpos == (payloadSize+4) ) { + + + fpos = 0; + if ( c == checksum[1] ) { + + return currentMsgType; + } + } + else if ( fpos > (payloadSize+4) ) { + + + fpos = 0; + } + } + } + + if (data_bytes!=0) { + UBX.state.non_empty_loops++; + DEBUG_SENSOR_LOG(PSTR("UBX: got %u bytes, non-empty-loop: %u"), data_bytes, UBX.state.non_empty_loops); + } else { + UBX.state.non_empty_loops = 0; + } + return MT_NONE; +} + + + + + +#ifdef USE_FLOG +void UBXsendHeader(void) +{ + WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); + WebServer->sendHeader(F("Content-Disposition"), F("attachment; filename=TASMOTA.gpx")); + WSSend(200, CT_STREAM, F( + "\r\n" + "\r\n" + "\r\n\r\n")); +} + +void UBXsendRecord(uint8_t *buf) +{ + char record[100]; + char stime[32]; + UBX_t::entry_t *entry = (UBX_t::entry_t*)buf; + snprintf_P(stime, sizeof(stime), GetDT(entry->time).c_str()); + char lat[12]; + char lon[12]; + dtostrfd((double)entry->lat/10000000.0f,7,lat); + dtostrfd((double)entry->lon/10000000.0f,7,lon); + snprintf_P(record, sizeof(record),PSTR("\n\t\n\n"),lat ,lon, stime); + + WebServer->sendContent_P(record); +} + +void UBXsendFooter(void) +{ + WebServer->sendContent(F("\n\n")); + WebServer->sendContent(""); + Rtc.user_time_entry = false; +} + + + +void UBXsendFile(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + Flog->startDownload(sizeof(UBX.rec_buffer),UBXsendHeader,UBXsendRecord,UBXsendFooter); +} +#endif + + + +void UBXSetRate(uint16_t interval) +{ + UBX.Message.cfgRate.cls = 0x06; + UBX.Message.cfgRate.id = 0x08; + UBX.Message.cfgRate.len = 6; + uint32_t measRate = (1000*(uint32_t)interval); + if (measRate > 0xffff) { + measRate = 0xffff; + } + UBX.Message.cfgRate.measRate = (uint16_t)measRate; + UBX.Message.cfgRate.navRate = 1; + UBX.Message.cfgRate.timeRef = 1; + UBXcalcChecksum(UBX.Message.cfgRate.CK, sizeof(UBX.Message.cfgRate)-sizeof(UBX.Message.cfgRate.CK)); + DEBUG_SENSOR_LOG(PSTR("UBX: requested interval: %u seconds measRate: %u ms"), interval, UBX.Message.cfgRate.measRate); + UBXSerial->write(UBX.UBX_HEADER[0]); + UBXSerial->write(UBX.UBX_HEADER[1]); + for (uint32_t i =0; iwrite(((uint8_t*)(&UBX.Message.cfgRate))[i]); + DEBUG_SENSOR_LOG(PSTR("UBX: cfgRate byte %u: %x"), i, ((uint8_t*)(&UBX.Message.cfgRate))[i]); + } + UBX.state.log_interval = 10*interval; +} + +void UBXSelectMode(uint16_t mode) +{ + DEBUG_SENSOR_LOG(PSTR("UBX: set mode to %u"),mode); + switch(mode){ +#ifdef USE_FLOG + case 0: + Flog->mode = 0; + break; + case 1: + Flog->mode = 1; + break; + case 2: + UBX.mode.filter_noise = true; + break; + case 3: + UBX.mode.filter_noise = false; + break; + case 4: + Flog->startRecording(true); + AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - appending")); + break; + case 5: + Flog->startRecording(false); + AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - new log")); + break; + case 6: + if(Flog->recording == true){ + Flog->stopRecording(); + } + AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: stop recording")); + break; +#endif + case 7: + UBX.mode.send_when_new = 1; + break; + case 8: + UBX.mode.send_when_new = 0; + break; + case 9: + if (timeServer.beginListening()) { + UBX.mode.runningNTP = true; + } + break; + case 10: + UBX.mode.runningNTP = false; + break; + case 11: + UBX.mode.forceUTCupdate = true; + break; + case 12: + UBX.mode.forceUTCupdate = false; + break; + case 13: + Settings.latitude = UBX.rec_buffer.values.lat/10; + Settings.longitude = UBX.rec_buffer.values.lon/10; + break; + case 14: + vPortServer.begin(); + UBX.mode.runningVPort = 1; + break; + case 15: + + UBX.mode.runningVPort = 0; + break; + default: + if (mode>1000 && mode <1066) { + + UBXSetRate(mode-1000); + } + break; + } + UBX.mode.send_UI_only = true; + UBXTriggerTele(); +} + + + +bool UBXHandlePOSLLH() +{ + DEBUG_SENSOR_LOG(PSTR("UBX: iTOW: %u"),UBX.Message.navPosllh.iTOW); + if (UBX.state.gpsFix>1) { + if (UBX.mode.filter_noise) { + if ((UBX.Message.navPosllh.lat-UBX.rec_buffer.values.lat6) { + if(UBX.mode.runningVPort) return; + UBXinitCFG(); + AddLog_P(LOG_LEVEL_ERROR, PSTR("UBX: possible device-reset, will re-init")); + UBXSerial->flush(); + UBX.state.non_empty_loops = 0; + } +} + + + +void UBXLoop50msec(void) +{ + + if (UBX.mode.runningVPort){ + if(!vPortClient.connected()) { + vPortClient = vPortServer.available(); + } + while(vPortClient.available()) { + byte _newByte = vPortClient.read(); + UBXSerial->write(_newByte); + } + + if (UBX.TCPbufSize!=0){ + vPortClient.write((char*)UBX.TCPbuf, UBX.TCPbufSize); + UBX.TCPbufSize = 0; + } + } + + if(UBX.mode.runningNTP){ + timeServer.processOneRequest(Rtc.utc_time, UBX.state.last_iTOW%1000); + } +} + +void UBXLoop(void) +{ + static uint16_t counter; + static bool new_position; + + uint32_t msgType = UBXprocessGPS(); + + switch(msgType){ + case MT_NAV_POSLLH: + new_position = UBXHandlePOSLLH(); + break; + case MT_NAV_STATUS: + UBXHandleSTATUS(); + break; + case MT_NAV_TIME: + UBXHandleTIME(); + break; + default: + UBXHandleOther(); + break; + } + +#ifdef USE_FLOG + if (counter>UBX.state.log_interval) { + if (Flog->recording && new_position) { + UBX.rec_buffer.values.time = Rtc.local_time; + Flog->addToBuffer(UBX.rec_buffer.bytes, sizeof(UBX.rec_buffer.bytes)); + counter = 0; + } + } +#endif + + counter++; +} + + + + +#ifdef USE_WEBSERVER + + +#ifdef USE_FLOG +#ifdef DEBUG_TASMOTA_SENSOR + const char HTTP_SNS_FLOGVER[] PROGMEM = "{s}
{m}
{e}{s} FLOG with %u sectors: {m}%u bytes{e}" + "{s} FLOG next sector for REC: {m} %u {e}" + "{s} %u sector(s) with data at sector: {m} %u {e}"; + const char HTTP_SNS_FLOGREC[] PROGMEM = "{s} RECORDING (bytes in buffer) {m}%u{e}"; +#endif + + const char HTTP_SNS_FLOG[] PROGMEM = "{s}
{m}
{e}{s} Flash-Log {m} %s{e}"; + const char kFLOG_STATE0[] PROGMEM = "ready"; + const char kFLOG_STATE1[] PROGMEM = "recording"; + const char * kFLOG_STATE[] ={kFLOG_STATE0, kFLOG_STATE1}; + + const char HTTP_BTN_FLOG_DL[] PROGMEM = ""; + +#endif + const char HTTP_SNS_NTPSERVER[] PROGMEM = "{s} NTP server {m}active{e}"; + + const char HTTP_SNS_GPS[] PROGMEM = "{s} GPS latitude {m}%s{e}" + "{s} GPS longitude {m}%s{e}" + "{s} GPS altitude {m}%s m{e}" + "{s} GPS hor. Accuracy {m}%s m{e}" + "{s} GPS vert. Accuracy {m}%s m{e}" + "{s} GPS sat-fix status {m}%s{e}"; + + const char kGPSFix0[] PROGMEM = "no fix"; + const char kGPSFix1[] PROGMEM = "dead reckoning only"; + const char kGPSFix2[] PROGMEM = "2D-fix"; + const char kGPSFix3[] PROGMEM = "3D-fix"; + const char kGPSFix4[] PROGMEM = "GPS + dead reckoning combined"; + const char kGPSFix5[] PROGMEM = "Time only fix"; + const char * kGPSFix[] PROGMEM ={kGPSFix0, kGPSFix1, kGPSFix2, kGPSFix3, kGPSFix4, kGPSFix5}; + + + + +#endif + + + +void UBXShow(bool json) +{ + char lat[12]; + char lon[12]; + char alt[12]; + char hAcc[12]; + char vAcc[12]; + dtostrfd((double)UBX.rec_buffer.values.lat/10000000.0f,7,lat); + dtostrfd((double)UBX.rec_buffer.values.lon/10000000.0f,7,lon); + dtostrfd((double)UBX.state.last_alt/1000.0f,3,alt); + dtostrfd((double)UBX.state.last_vAcc/1000.0f,3,hAcc); + dtostrfd((double)UBX.state.last_hAcc/1000.0f,3,vAcc); + + if (json) { + ResponseAppend_P(PSTR(",\"GPS\":{")); + if (UBX.mode.send_UI_only) { + uint32_t i = UBX.state.log_interval / 10; + ResponseAppend_P(PSTR("\"fil\":%u,\"int\":%u}"), UBX.mode.filter_noise, i); + } else { + ResponseAppend_P(PSTR("\"lat\":%s,\"lon\":%s,\"alt\":%s,\"hAcc\":%s,\"vAcc\":%s}"), lat, lon, alt, hAcc, vAcc); + } +#ifdef USE_FLOG + ResponseAppend_P(PSTR(",\"FLOG\":{\"rec\":%u,\"mode\":%u,\"sec\":%u}"), Flog->recording, Flog->mode, Flog->sectors_left); +#endif + UBX.mode.send_UI_only = false; +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_GPS, lat, lon, alt, hAcc, vAcc, kGPSFix[UBX.state.gpsFix]); + +#ifdef DEBUG_TASMOTA_SENSOR +#ifdef USE_FLOG + WSContentSend_PD(HTTP_SNS_FLOGVER, Flog->num_sectors, Flog->size, Flog->current_sector, Flog->sectors_left, Flog->sector.header.physical_start_sector); + if (Flog->recording) { + WSContentSend_PD(HTTP_SNS_FLOGREC, Flog->sector.header.buf_pointer - 8); + } +#endif +#endif +#ifdef USE_FLOG + if (Flog->ready) { + WSContentSend_P(HTTP_SNS_FLOG,kFLOG_STATE[Flog->recording]); + } + if (!Flog->recording && Flog->found_saved_data) { + WSContentSend_P(HTTP_BTN_FLOG_DL); + } +#endif + if (UBX.mode.runningNTP) { + WSContentSend_P(HTTP_SNS_NTPSERVER); + } +#endif + } +} + + + + + +bool UBXCmd(void) +{ + bool serviced = true; + if (XdrvMailbox.data_len > 0) { + UBXSelectMode(XdrvMailbox.payload); + Response_P(S_JSON_UBX_COMMAND_NVALUE, XdrvMailbox.command, XdrvMailbox.payload); + } + return serviced; +} + + + + + +bool Xsns60(uint8_t function) +{ + bool result = false; + + if (FUNC_INIT == function) { + UBXDetect(); + } + + if (UBX.mode.init) { + switch (function) { + case FUNC_COMMAND_SENSOR: + if (XSNS_60 == XdrvMailbox.index) { + result = UBXCmd(); + } + break; + case FUNC_EVERY_50_MSECOND: + UBXLoop50msec(); + break; + case FUNC_EVERY_100_MSECOND: +#ifdef USE_FLOG + if (!Flog->running_download) +#endif + { + UBXLoop(); + } + break; +#ifdef USE_FLOG + case FUNC_WEB_ADD_HANDLER: + WebServer->on("/UBX", UBXsendFile); + break; +#endif + case FUNC_JSON_APPEND: + UBXShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: +#ifdef USE_FLOG + if (!Flog->running_download) +#endif + { + UBXShow(0); + } + break; +#endif + } + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +# 35 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +#ifdef USE_SPI +#ifdef USE_NRF24 +#ifdef USE_MIBLE + +#ifdef DEBUG_TASMOTA_SENSOR + #define MINRF_LOG_BUFFER(x) MINRFshowBuffer(x); +#else + #define MINRF_LOG_BUFFER(x) +#endif +# 53 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +#define XSNS_61 61 + +#include + +#define FLORA 1 +#define MJ_HT_V1 2 +#define LYWSD02 3 +#define LYWSD03 4 + +uint8_t kMINRFSlaveID[4][3] = { 0xC4,0x7C,0x8D, + 0x58,0x2D,0x34, + 0xE7,0x2E,0x00, + 0xA4,0xC1,0x38, + }; + +const char kMINRFSlaveType1[] PROGMEM = "Flora"; +const char kMINRFSlaveType2[] PROGMEM = "MJ_HT_V1"; +const char kMINRFSlaveType3[] PROGMEM = "LYWSD02"; +const char kMINRFSlaveType4[] PROGMEM = "LYWSD03"; +const char * kMINRFSlaveType[] PROGMEM = {kMINRFSlaveType1,kMINRFSlaveType2,kMINRFSlaveType3,kMINRFSlaveType4}; + + +const uint32_t kMINRFFloPDU[3] = {0x3eaa857d,0xef3b8730,0x71da7b46}; +const uint32_t kMINRFMJPDU[3] = {0x4760cd66,0xdbcc0cd3,0x33048df5}; +const uint32_t kMINRFL2PDU[3] = {0x3eaa057d,0xef3b0730,0x71da7646}; + +const uint32_t kMINRFL3PDU[3] = {0x4760cb78,0xdbcc0acd,0x33048beb}; + + +const uint8_t kMINRFlsfrList_A[3] = {0x4b,0x17,0x23}; +const uint8_t kMINRFlsfrList_B[3] = {0x21,0x72,0x43}; + + +#pragma pack(1) +struct MJ_HT_V1Header_t { + uint8_t padding[3]; + uint8_t mesSize; + uint8_t padding2; + uint16_t uuid; + uint16_t type; + uint8_t padding3[2]; + uint8_t counter; + uint8_t serial[6]; + uint8_t mode; + uint8_t padding5; + uint8_t effectiveDataLength; + }; + +struct FlowerHeader_t { + uint8_t padding[4]; + uint8_t padding2; + uint16_t uuid; + uint8_t mesSize; + uint8_t padding22; + uint16_t uuid2; + uint16_t type; + uint8_t padding3[2]; + uint8_t counter; + uint8_t serial[6]; + uint8_t padding4; + uint8_t mode; + }; + +union floraPacket_t { + struct { + uint16_t idWord; + uint8_t padding; + uint8_t serial[6]; + uint8_t padding4; + uint8_t mode; + uint8_t valueTen; + uint8_t effectiveDataLength; + uint16_t data; + } T; + struct { + uint16_t idWord; + uint8_t padding; + uint8_t serial[6]; + uint8_t padding4; + uint8_t mode; + uint8_t valueTen; + uint8_t effectiveDataLength; + uint32_t data:24; + } L; + struct { + uint8_t padding[3]; + uint8_t serial[6]; + uint8_t padding4; + uint8_t mode; + uint8_t valueTen; + uint8_t effectiveDataLength; + uint8_t data; + } M; + struct { + uint8_t padding[3]; + uint8_t serial[6]; + uint8_t padding4; + uint8_t mode; + uint8_t valueTen; + uint8_t effectiveDataLength; + uint16_t data; + } F; +}; + +union MJ_HT_V1Packet_t { + struct { + uint16_t idWord; + uint8_t padding; + uint8_t serial[6]; + uint8_t mode; + uint8_t valueTen; + uint8_t effectiveDataLength; + uint16_t temp; + uint16_t hum; + } TH; + struct { + uint8_t padding[3]; + uint8_t serial[6]; + uint8_t mode; + uint8_t valueTen; + uint8_t effectiveDataLength; + uint8_t battery; + } B; + +}; + +union LYWSD02Packet_t { + struct { + uint16_t idWord; + uint8_t padding; + uint8_t serial[6]; + uint8_t padding4; + uint8_t mode; + uint8_t valueTen; + uint8_t effectiveDataLength; + uint16_t data; + } TH; +}; + +struct bleAdvPacket_t { + uint8_t pduType; + uint8_t payloadSize; + uint8_t mac[6]; + union { + uint8_t payload[24]; + MJ_HT_V1Header_t header; + FlowerHeader_t flowerHeader; + struct { + uint8_t padding[21]; + uint16_t temp; + uint8_t hum_lb; + } TH; + struct { + uint8_t padding[21]; + uint16_t temp; + } T; + struct { + uint8_t padding[21]; + uint16_t hum; + } H; + struct { + uint8_t padding[21]; + uint8_t battery; + } B; + struct { + uint8_t padding[2]; + uint8_t mode; + uint16_t size; + uint16_t data; + } F_T; + struct { + uint8_t padding[2]; + uint8_t mode; + uint16_t size; + uint16_t data; + uint8_t data2; + } F_L; + struct { + uint8_t padding[2]; + uint8_t mode; + uint16_t size; + uint8_t data; + } F_M; + struct { + uint8_t padding[2]; + uint8_t mode; + uint16_t size; + uint16_t data; + } F_F; + }; +}; + +union FIFO_t{ + bleAdvPacket_t bleAdv; + floraPacket_t floraPacket; + MJ_HT_V1Packet_t MJ_HT_V1Packet; + LYWSD02Packet_t LYWSD02Packet; + uint8_t raw[32]; +}; + +#pragma pack(0) + +struct { + const uint8_t channel[3] = {37,38,39}; + const uint8_t frequency[3] = { 2,26,80}; + + uint16_t timer; + uint8_t currentChan=0; + FIFO_t buffer; + uint8_t packetMode; + +#ifdef DEBUG_TASMOTA_SENSOR + uint8_t streamBuffer[sizeof(buffer)]; + uint8_t lsfrBuffer[sizeof(buffer)]; +#endif + +} MINRF; + +struct mi_sensor_t{ + uint8_t type; + uint8_t serial[6]; + uint8_t showedUp; + float temp; + union { + struct { + float moisture; + float fertility; + uint32_t lux; + }; + struct { + float hum; + uint8_t bat; + }; + }; +}; + +std::vector MIBLEsensors; + + + + +bool MINRFinitBLE(uint8_t _mode) +{ + if (MINRF.timer%1000 == 0){ + NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]); + NRF24radio.setAutoAck(false); + NRF24radio.setDataRate(RF24_1MBPS); + NRF24radio.disableCRC(); + NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] ); + NRF24radio.setRetries(0,0); + NRF24radio.setPALevel(RF24_PA_MIN); + NRF24radio.setAddressWidth(4); + + + NRF24radio.powerUp(); + } + if(NRF24radio.isChipConnected()){ + + MINRFchangePacketModeTo(_mode); + return true; + } + + return false; +} + +void MINRFhopChannel() +{ + MINRF.currentChan++; + if(MINRF.currentChan >= sizeof(MINRF.channel)) { + MINRF.currentChan = 0; + } + NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] ); +} + + + + + + + +bool MINRFreceivePacket(void) +{ + if(!NRF24radio.available()) { + return false; + } + while(NRF24radio.available()) { + + + NRF24radio.read( &MINRF.buffer, sizeof(MINRF.buffer) ); +#ifdef DEBUG_TASMOTA_SENSOR + memcpy(&MINRF.streamBuffer, &MINRF.buffer, sizeof(MINRF.buffer)); +#endif + MINRFswapbuf( sizeof(MINRF.buffer) ); + + + + switch (MINRF.packetMode) { + case 0: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40); + break; + case 1: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); + break; + case 2: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); + break; + case 3: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); + break; + case 4: + MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); + break; + } + + + } + + return true; +} + +#ifdef DEBUG_TASMOTA_SENSOR +void MINRFshowBuffer(uint8_t (&buf)[32]){ + + + + + DEBUG_SENSOR_LOG(PSTR("MINRF: Buffer: %02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x ") + ,buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9],buf[10],buf[11], + buf[12],buf[13],buf[14],buf[15],buf[16],buf[17],buf[18],buf[19],buf[20],buf[21],buf[22],buf[23], + buf[24],buf[25],buf[26],buf[27],buf[28],buf[29],buf[30],buf[31] + ); +} +#endif + + + + + + +void MINRFswapbuf(uint8_t len) +{ + uint8_t* buf = (uint8_t*)&MINRF.buffer; + while(len--) { + uint8_t a = *buf; + uint8_t v = 0; + if (a & 0x80) v |= 0x01; + if (a & 0x40) v |= 0x02; + if (a & 0x20) v |= 0x04; + if (a & 0x10) v |= 0x08; + if (a & 0x08) v |= 0x10; + if (a & 0x04) v |= 0x20; + if (a & 0x02) v |= 0x40; + if (a & 0x01) v |= 0x80; + *(buf++) = v; + } +} +# 420 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +void MINRFwhiten(uint8_t *buf, uint8_t len, uint8_t lfsr) +{ + while(len--) { + uint8_t res = 0; + + for (uint8_t i = 1; i; i <<= 1) { + if (lfsr & 0x01) { + lfsr ^= 0x88; + res |= i; + } + lfsr >>= 1; + } + *(buf++) ^= res; +#ifdef DEBUG_TASMOTA_SENSOR + MINRF.lsfrBuffer[31-len] = lfsr; +#endif + } +} + +void MINRFreverseMAC(uint8_t _mac[]){ + uint8_t _reversedMAC[6]; + for (uint8_t i=0; i<6; i++){ + _reversedMAC[5-i] = _mac[i]; + } + memcpy(_mac,_reversedMAC, sizeof(_reversedMAC)); +} + + + + + + +void MINRFchangePacketModeTo(uint8_t _mode) { + uint32_t (_nextchannel) = MINRF.currentChan+1; + if (_nextchannel>2) _nextchannel=0; + + switch(_mode){ + case 0: + NRF24radio.openReadingPipe(0,0x6B7D9171); + break; + case 1: + NRF24radio.openReadingPipe(0,kMINRFFloPDU[_nextchannel]); + break; + case 2: + NRF24radio.openReadingPipe(0,kMINRFMJPDU[_nextchannel]); + break; + case 3: + NRF24radio.openReadingPipe(0,kMINRFL2PDU[_nextchannel]); + break; + case 4: + if(kMINRFL3PDU[_nextchannel]==0xffffffff) break; + NRF24radio.openReadingPipe(0,kMINRFL3PDU[_nextchannel]); + break; + } + + MINRF.packetMode = _mode; +} +# 485 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_61_MI_NRF24.ino" +uint32_t MINRFgetSensorSlot(uint8_t (&_serial)[6], uint8_t _type){ + if(_type==0xff){ + DEBUG_SENSOR_LOG(PSTR("MINRF: will test MAC-type")); + for (uint32_t i=0;i<4;i++){ + if(memcmp(_serial,kMINRFSlaveID+i,3)==0){ + DEBUG_SENSOR_LOG(PSTR("MINRF: MAC is type %u"), i); + _type = i+1; + } + else { + DEBUG_SENSOR_LOG(PSTR("MINRF: MAC-type is unknown")); + } + } + } + if(_type==0xff) return _type; + + DEBUG_SENSOR_LOG(PSTR("MINRF: vector size %u"), MIBLEsensors.size()); + for(uint32_t i=0; i6000){ + DEBUG_SENSOR_LOG(PSTR("MINRF: check for FAKE sensors")); + MINRFpurgeFakeSensors(); + MINRF.timer=0; + } + MINRF.timer++; + + if (!MINRFreceivePacket()){ + + } + + else if(MINRF.buffer.bleAdv.header.uuid==0xfe95){ + MINRF_LOG_BUFFER(MINRF.streamBuffer); + MINRF_LOG_BUFFER(MINRF.lsfrBuffer); + MINRF_LOG_BUFFER(MINRF.buffer.raw); + DEBUG_SENSOR_LOG(PSTR("MINRF: Type: %x"), MINRF.buffer.bleAdv.header.type); + switch(MINRF.buffer.bleAdv.header.type){ + case 0x2050: + DEBUG_SENSOR_LOG(PSTR("MINRF: MJ_HT_V1 Packet")); + break; + case 0x1613:case 0x1614:case 0x1615: + DEBUG_SENSOR_LOG(PSTR("MINRF: Flora Packet")); + break; + default: + DEBUG_SENSOR_LOG(PSTR("MINRF: unknown Packet")); + break; + } + } + else if (MINRF.packetMode == FLORA){ + MINRFhandleFloraPacket(); + } + else if (MINRF.packetMode == MJ_HT_V1){ + MINRFhandleMJ_HT_V1Packet(); + } + else if (MINRF.packetMode == LYWSD02){ + MINRFhandleLYWSD02Packet(); + } + else if (MINRF.packetMode == LYWSD03){ + MINRFhandleLYWSD03Packet(); + } + + + if (MINRF.packetMode == LYWSD03){ + MINRFinitBLE(1); + } + else { + MINRFinitBLE(++MINRF.packetMode); + } + + MINRFhopChannel(); + NRF24radio.startListening(); +} + + + + +const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; +const char HTTP_MINRF_MAC[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; +const char HTTP_MINRF_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%sus/cm{e}"; +const char HTTP_MINRF_HL[] PROGMEM = "{s}
{m}
{e}"; + +void MINRFShow(bool json) +{ + if (json) { + for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { + if(MIBLEsensors.at(i).showedUp < 3){ + DEBUG_SENSOR_LOG(PSTR("MINRF: sensor not fully registered yet")); + break; + } + char slave[33]; + sprintf_P(slave,"%s-%02x%02x%02x",kMINRFSlaveType[MIBLEsensors.at(i).type-1],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]); + char temperature[33]; + dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature); + + ResponseAppend_P(PSTR(",\"%s\":{"),slave); + if(MIBLEsensors.at(i).temp!=-1000.0f){ + ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature); + } + if (MIBLEsensors.at(i).type==FLORA){ + char lux[33]; + char moisture[33]; + char fertility[33]; + dtostrfd((float)MIBLEsensors.at(i).lux, 0, lux); + dtostrfd(MIBLEsensors.at(i).moisture, 0, moisture); + dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility); + if(MIBLEsensors.at(i).lux!=0xffff){ + ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%s"), lux); + } + if(MIBLEsensors.at(i).moisture!=-1000.0f){ + ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%s"), moisture); + } + if(MIBLEsensors.at(i).fertility!=-1000.0f){ + ResponseAppend_P(PSTR(",\"Fertility\":%s"), fertility); + } + } + if (MIBLEsensors.at(i).type>FLORA){ + char humidity[33]; + dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity); + if(MIBLEsensors.at(i).hum!=-1.0f){ + ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity); + } + if(MIBLEsensors.at(i).bat!=0xff){ + ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors.at(i).bat); + } + } + ResponseAppend_P(PSTR("}")); + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_NRF24, NRF24type, NRF24.chipType); + for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { + if(MIBLEsensors.at(i).showedUp < 3){ + DEBUG_SENSOR_LOG(PSTR("MINRF: sensor not fully registered yet")); + break; + } + WSContentSend_PD(HTTP_MINRF_HL); + WSContentSend_PD(HTTP_MINRF_MAC, kMINRFSlaveType[MIBLEsensors.at(i).type-1], D_MAC_ADDRESS, MIBLEsensors.at(i).serial[0], MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]); + if(MIBLEsensors.at(i).temp!=-1000.0f){ + char temperature[33]; + dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature); + WSContentSend_PD(HTTP_SNS_TEMP, kMINRFSlaveType[MIBLEsensors.at(i).type-1], temperature, TempUnit()); + } + if (MIBLEsensors.at(i).type==FLORA){ + if(MIBLEsensors.at(i).lux!=0x00ffffff){ + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kMINRFSlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).lux); + } + if(MIBLEsensors.at(i).moisture!=-1000.0f){ + WSContentSend_PD(HTTP_SNS_MOISTURE, kMINRFSlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).moisture); + } + if(MIBLEsensors.at(i).fertility!=-1000.0f){ + char fertility[33]; + dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility); + WSContentSend_PD(HTTP_MINRF_FLORA_DATA, kMINRFSlaveType[MIBLEsensors.at(i).type-1], fertility); + } + } + if (MIBLEsensors.at(i).type>FLORA){ + if(MIBLEsensors.at(i).hum!=-1.0f){ + char humidity[33]; + dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity); + WSContentSend_PD(HTTP_SNS_HUM, kMINRFSlaveType[MIBLEsensors.at(i).type-1], humidity); + } + if(MIBLEsensors.at(i).bat!=0xff){ + WSContentSend_PD(HTTP_BATTERY, kMINRFSlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).bat); + } + } + } +#endif + } +} + + + + + +bool Xsns61(uint8_t function) +{ + bool result = false; + + if (NRF24.chipType) { + switch (function) { + case FUNC_INIT: + MINRFinitBLE(1); + AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: started")); + break; + case FUNC_EVERY_50_MSECOND: + MINRF_EVERY_50_MSECOND(); + break; + case FUNC_JSON_APPEND: + MINRFShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MINRFShow(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_62_MI_HM10.ino" +# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_62_MI_HM10.ino" +#ifdef USE_HM10 + +#define XSNS_62 62 + +#include +#include + +TasmotaSerial *HM10Serial; +#define HM10_BAUDRATE 115200 + +#define HM10_MAX_TASK_NUMBER 12 +uint8_t HM10_TASK_LIST[HM10_MAX_TASK_NUMBER+1][2]; + +#define HM10_MAX_RX_BUF 512 +char HM10_RX_STRING[HM10_MAX_RX_BUF] = {0}; + +struct { + uint8_t current_task_delay; + uint8_t last_command; + uint16_t firmware; + uint32_t period; + uint32_t serialSpeed; + union { + uint32_t time; + uint8_t timebuf[4]; + }; + struct { + uint32_t init:1; + uint32_t pending_task:1; + uint32_t connected:1; + uint32_t subscribed:1; + uint32_t awaitingHT:1; + uint32_t awaitingB:1; + + } mode; + struct { + uint8_t sensor; + + } state; +} HM10; + +#pragma pack(1) +struct { + uint16_t temp; + uint8_t hum; +} LYWSD0x_HT; +#pragma pack(0) + +struct mi_sensor_t{ + uint8_t type; + uint8_t serial[6]; + uint8_t showedUp; + float temp; + union { + struct { + float moisture; + float fertility; + uint16_t lux; + }; + struct { + float hum; + uint8_t bat; + }; + }; +}; + +std::vector MIBLEsensors; + + + + + +#define D_CMND_HM10 "HM10" + +const char S_JSON_HM10_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_HM10 "%s\":%d}"; +const char S_JSON_HM10_COMMAND[] PROGMEM = "{\"" D_CMND_HM10 "%s%s\"}"; +const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time"; + +#define FLORA 1 +#define MJ_HT_V1 2 +#define LYWSD02 3 +#define LYWSD03MMC 4 + +uint8_t kHM10SlaveID[4][3] = { 0xC4,0x7C,0x8D, + 0x58,0x2D,0x34, + 0xE7,0x2E,0x00, + 0xA4,0xC1,0x38, + }; + +const char kHM10SlaveType1[] PROGMEM = "Flora"; +const char kHM10SlaveType2[] PROGMEM = "MJ_HT_V1"; +const char kHM10SlaveType3[] PROGMEM = "LYWSD02"; +const char kHM10SlaveType4[] PROGMEM = "LYWSD03"; +const char * kHM10SlaveType[] PROGMEM = {kHM10SlaveType1,kHM10SlaveType2,kHM10SlaveType3,kHM10SlaveType4}; + + + + + +enum HM10_Commands { + CMND_HM10_DISC_SCAN, + CMND_HM10_AT, + CMND_HM10_PERIOD, + CMND_HM10_BAUD, + CMND_HM10_TIME + }; + + + + + +#define TASK_HM10_NOTASK 0 +#define TASK_HM10_ROLE1 1 +#define TASK_HM10_IMME1 2 +#define TASK_HM10_RENEW 3 +#define TASK_HM10_RESET 4 +#define TASK_HM10_DISC 5 +#define TASK_HM10_CONN 6 +#define TASK_HM10_VERSION 7 +#define TASK_HM10_NAME 8 +#define TASK_HM10_FEEDBACK 9 +#define TASK_HM10_DISCONN 10 +#define TASK_HM10_SUB_L3 11 +#define TASK_HM10_READ_HT 12 +#define TASK_HM10_FINDALLCHARS 13 +#define TASK_HM10_UN_L3 14 +#define TASK_HM10_DELAY_SUB 15 +#define TASK_HM10_READ_BT_L3 16 +#define TASK_HM10_SUB_L2 17 +#define TASK_HM10_UN_L2 18 +#define TASK_HM10_READ_BT_L2 19 +#define TASK_HM10_TIME_L2 20 + +#define TASK_HM10_DONE 99 + + + + + +void HM10_Launchtask(uint8_t task, uint8_t slot, uint8_t delay){ + HM10_TASK_LIST[slot][0] = task; + HM10_TASK_LIST[slot][1] = delay; + HM10_TASK_LIST[slot+1][0] = TASK_HM10_NOTASK; + HM10.current_task_delay = HM10_TASK_LIST[0][1]; +} + +void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot){ + HM10.last_command = HM10_TASK_LIST[slot][0]; + HM10_TASK_LIST[slot][0] = task; +} + + + + + +void HM10_Reset(void) { HM10_Launchtask(TASK_HM10_DISCONN,0,1); + HM10_Launchtask(TASK_HM10_ROLE1,1,1); + HM10_Launchtask(TASK_HM10_IMME1,2,1); + HM10_Launchtask(TASK_HM10_RESET,3,1); + HM10_Launchtask(TASK_HM10_VERSION,4,10); + HM10_Launchtask(TASK_HM10_DISC,5,50); + } + +void HM10_Discovery_Scan(void) { + HM10_Launchtask(TASK_HM10_DISCONN,0,1); + HM10_Launchtask(TASK_HM10_DISC,1,1); + } + +void HM10_Read_LYWSD03(void) { + HM10_Launchtask(TASK_HM10_CONN,0,1); + HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); + HM10_Launchtask(TASK_HM10_SUB_L3,2,20); + HM10_Launchtask(TASK_HM10_UN_L3,3,80); + HM10_Launchtask(TASK_HM10_READ_BT_L3,4,5); + HM10_Launchtask(TASK_HM10_DISCONN,5,5); + } + +void HM10_Read_LYWSD02(void) { + HM10_Launchtask(TASK_HM10_CONN,0,1); + HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); + HM10_Launchtask(TASK_HM10_SUB_L2,2,20); + HM10_Launchtask(TASK_HM10_UN_L2,3,80); + HM10_Launchtask(TASK_HM10_READ_BT_L2,4,5); + HM10_Launchtask(TASK_HM10_DISCONN,5,5); + } + +void HM10_Time_LYWSD02(void) { + HM10_Launchtask(TASK_HM10_DISCONN,0,0); + HM10_Launchtask(TASK_HM10_CONN,1,5); + HM10_Launchtask(TASK_HM10_FEEDBACK,2,35); + HM10_Launchtask(TASK_HM10_TIME_L2,3,20); + HM10_Launchtask(TASK_HM10_DISCONN,4,5); + } +# 231 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_62_MI_HM10.ino" +uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint8_t _type){ + if(_type==0xff){ + DEBUG_SENSOR_LOG(PSTR("MIBLE: will test MAC-type")); + for (uint32_t i=0;i<4;i++){ + if(memcmp(_serial,kHM10SlaveID+i,3)==0){ + DEBUG_SENSOR_LOG(PSTR("MIBLE: MAC is type %u"), i); + _type = i+1; + } + else { + DEBUG_SENSOR_LOG(PSTR("MIBLE: MAC-type is unknown")); + } + } + } + if(_type==0xff) return _type; + + DEBUG_SENSOR_LOG(PSTR("MIBLE: vector size %u"), MIBLEsensors.size()); + for(uint32_t i=0; ibegin(HM10.serialSpeed)) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start serial communication fixed to 115200 baud"),D_CMND_HM10); + if (HM10Serial->hardwareSerial()) { + ClaimSerial(); + DEBUG_SENSOR_LOG(PSTR("HM10: claim HW")); + } + HM10_Reset(); + HM10.mode.pending_task = 1; + HM10.mode.init = 1; + HM10.period = Settings.tele_period; + DEBUG_SENSOR_LOG(PSTR("%s_TASK_LIST initialized, now return to main loop"),D_CMND_HM10); + } + return; +} +# 315 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_62_MI_HM10.ino" +void HM10MACStringToBytes(const char* string, uint8_t _mac[]) { + uint32_t index = 0; + while (index < 12) { + char c = string[index]; + uint32_t value = 0; + if(c >= '0' && c <= '9') + value = (c - '0'); + else if (c >= 'A' && c <= 'F') + value = (10 + (c - 'A')); + _mac[(index/2)] += value << (((index + 1) % 2) * 4); + + index++; + } + DEBUG_SENSOR_LOG(PSTR("HM10: MAC-array: %x%x%x%x%x%x"),_mac[0],_mac[1],_mac[2],_mac[3],_mac[4],_mac[5]); +} + + + + + + +void HM10ParseResponse(char *buf) { + if (!strncmp(buf,"OK",2)) { + DEBUG_SENSOR_LOG(PSTR("HM10: got OK")); + } + if (!strncmp(buf,"HMSoft",6)) { + const char* _fw = "000"; + memcpy((void *)_fw,(void *)(buf+8),3); + HM10.firmware = atoi(_fw); + DEBUG_SENSOR_LOG(PSTR("HM10: Firmware: %d"), HM10.firmware); + return; + } + char * _pos = strstr(buf, "IS0:"); + if(_pos) { + const char* _mac = "000000000000"; + memcpy((void *)_mac,(void *)(_pos+4),12); + DEBUG_SENSOR_LOG(PSTR("HM10: found Mac: %s"), _mac); + uint8_t _newMacArray[6] = {0}; + HM10MACStringToBytes(_mac, _newMacArray); + DEBUG_SENSOR_LOG(PSTR("HM10: MAC-array: %x%x%x%x%x%x"),_newMacArray[0],_newMacArray[1],_newMacArray[2],_newMacArray[3],_newMacArray[4],_newMacArray[5]); + MIBLEgetSensorSlot(_newMacArray, 0xff); + } + if (strstr(buf, "LOST")){ + HM10.mode.connected = false; + } + else { + DEBUG_SENSOR_LOG(PSTR("HM10: empty response")); + } +} + +void HM10readTempHum(char *_buf){ + DEBUG_SENSOR_LOG(PSTR("HM10: raw data: %x%x%x%x%x%x%x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0] != 0 && _buf[1] != 0){ + memcpy(&LYWSD0x_HT,(void *)_buf,3); + DEBUG_SENSOR_LOG(PSTR("HM10: Temperature * 100: %u, Humidity: %u"),LYWSD0x_HT.temp,LYWSD0x_HT.hum); + uint32_t _slot = HM10.state.sensor; + + DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + static float _tempFloat; + _tempFloat=(float)(LYWSD0x_HT.temp)/100.0f; + if(_tempFloat<60){ + MIBLEsensors.at(_slot).temp=_tempFloat; + HM10.mode.awaitingHT = false; + HM10.current_task_delay = 0; + } + _tempFloat=(float)LYWSD0x_HT.hum; + if(_tempFloat<100){ + MIBLEsensors.at(_slot).hum = _tempFloat; + DEBUG_SENSOR_LOG(PSTR("LYWSD03: hum updated")); + } + } +} + +bool HM10readBat(char *_buf){ + DEBUG_SENSOR_LOG(PSTR("HM10: raw data: %x%x%x%x%x%x%x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); + if(_buf[0] != 0){ + DEBUG_SENSOR_LOG(PSTR("HM10: Battery: %u"),_buf[0]); + uint32_t _slot = HM10.state.sensor; + DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); + if(_buf[0]<101){ + MIBLEsensors.at(_slot).bat=_buf[0]; + return true; + } + } + return false; +} + + + + + +bool HM10SerialHandleFeedback(){ + bool success = false; + uint32_t i = 0; + char ret[HM10_MAX_RX_BUF] = {0}; + + + while(HM10Serial->available()) { + + if(iread(); + } + i++; + success = true; + } + if(HM10.mode.awaitingHT) { + if (HM10.mode.connected) HM10readTempHum(ret); + } + else if(HM10.mode.awaitingB) { + if (HM10.mode.connected) { + if (HM10readBat(ret)){ + HM10.mode.awaitingB = false; + HM10.current_task_delay = 0; + } + } + } + else if(success) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s response: %s"),D_CMND_HM10, (char *)ret); + HM10ParseResponse(ret); + } + else { + + } + return success; +} + + + + + +void HM10_TaskEvery100ms(){ + if (HM10.current_task_delay == 0) { + uint8_t i = 0; + bool runningTaskLoop = true; + while (runningTaskLoop) { + switch(HM10_TASK_LIST[i][0]) { + case TASK_HM10_ROLE1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s set role to 1"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+ROLE1"); + break; + case TASK_HM10_IMME1: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s set imme to 1"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+IMME1"); + break; + case TASK_HM10_DISC: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start discovery"),D_CMND_HM10); + HM10.current_task_delay = 35; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+DISC?"); + break; + case TASK_HM10_VERSION: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read version"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+VERR?"); + break; + case TASK_HM10_NAME: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read name"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+NAME?"); + break; + case TASK_HM10_CONN: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s connect"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + char _con[20]; + sprintf_P(_con,"AT+CON%02x%02x%02x%02x%02x%02x",MIBLEsensors.at(HM10.state.sensor).serial[0],MIBLEsensors.at(HM10.state.sensor).serial[1],MIBLEsensors.at(HM10.state.sensor).serial[2],MIBLEsensors.at(HM10.state.sensor).serial[3],MIBLEsensors.at(HM10.state.sensor).serial[4],MIBLEsensors.at(HM10.state.sensor).serial[5]); + HM10Serial->write(_con); + HM10.mode.awaitingB = false; + HM10.mode.connected = true; + break; + case TASK_HM10_DISCONN: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s disconnect"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT"); + break; + case TASK_HM10_RESET: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s Reset Device"),D_CMND_HM10); + HM10Serial->write("AT+RESET"); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + break; + case TASK_HM10_SUB_L3: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s subscribe"),D_CMND_HM10); + HM10.current_task_delay = 25; + HM10_TaskReplaceInSlot(TASK_HM10_DELAY_SUB,i); + runningTaskLoop = false; + HM10Serial->write("AT+NOTIFY_ON0037"); + break; + case TASK_HM10_UN_L3: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s un-subscribe"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaitingHT = false; + HM10Serial->write("AT+NOTIFYOFF0037"); + break; + case TASK_HM10_SUB_L2: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s subscribe"),D_CMND_HM10); + HM10.current_task_delay = 25; + HM10_TaskReplaceInSlot(TASK_HM10_DELAY_SUB,i); + runningTaskLoop = false; + HM10Serial->write("AT+NOTIFY_ON003C"); + break; + case TASK_HM10_UN_L2: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s un-subscribe"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.mode.awaitingHT = false; + HM10Serial->write("AT+NOTIFYOFF003C"); + break; + case TASK_HM10_TIME_L2: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s set time"),D_CMND_HM10); + HM10.current_task_delay = 5; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10.time = Rtc.utc_time; + HM10Serial->write("AT+SEND_DATAWR002F"); + HM10Serial->write(HM10.timebuf,4); + HM10Serial->write(Rtc.time_timezone / 60); + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s Time-string: %x%x%x%x%x"),D_CMND_HM10, HM10.timebuf[0],HM10.timebuf[1],HM10.timebuf[2],HM10.timebuf[3],(Rtc.time_timezone /60)); + break; + case TASK_HM10_READ_HT: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read handle 0036"),D_CMND_HM10); + HM10.current_task_delay = 0; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0036?"); + HM10.mode.awaitingHT = true; + break; + case TASK_HM10_READ_BT_L3: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read handle 003A"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA003A?"); + HM10.mode.awaitingB = true; + break; + case TASK_HM10_READ_BT_L2: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read handle 0043"),D_CMND_HM10); + HM10.current_task_delay = 2; + HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); + runningTaskLoop = false; + HM10Serial->write("AT+READDATA0043?"); + HM10.mode.awaitingB = true; + break; + + + + + + + + case TASK_HM10_FEEDBACK: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s get response"),D_CMND_HM10); + HM10SerialHandleFeedback(); + HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; + HM10_TASK_LIST[i][0] = TASK_HM10_DONE; + runningTaskLoop = false; + break; + case TASK_HM10_DELAY_SUB: + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start reading"),D_CMND_HM10); + HM10SerialHandleFeedback(); + HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; + HM10_TASK_LIST[i][0] = TASK_HM10_DONE; + HM10.mode.awaitingHT = true; + runningTaskLoop = false; + break; + case TASK_HM10_DONE: + + + if(HM10_TASK_LIST[i+1][0] == TASK_HM10_NOTASK) { + DEBUG_SENSOR_LOG(PSTR("%sno Tasks left"),D_CMND_HM10); + DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK_DONE current slot %u"),D_CMND_HM10, i); + for (uint8_t j = 0; j < HM10_MAX_TASK_NUMBER+1; j++) { + DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK cleanup slot %u"),D_CMND_HM10, j); + HM10_TASK_LIST[j][0] = TASK_HM10_NOTASK; + HM10_TASK_LIST[j][1] = 0; + } + runningTaskLoop = false; + HM10.mode.pending_task = 0; + break; + } + } + i++; + } + } + else { + HM10.current_task_delay--; + } +} + + + + + + +void HM10EverySecond(){ + if(HM10.firmware == 0) return; + if(HM10.mode.pending_task == 1) return; + if (MIBLEsensors.size()==0) return; + + static uint32_t _counter = 0; + static uint32_t _nextSensorSlot = 0; + if(_counter==0) { + HM10.state.sensor = _nextSensorSlot; + _nextSensorSlot++; + if(MIBLEsensors.at(HM10.state.sensor).type==LYWSD03MMC) { + HM10.mode.pending_task = 1; + HM10_Read_LYWSD03(); + } + if(MIBLEsensors.at(HM10.state.sensor).type==LYWSD02) { + HM10.mode.pending_task = 1; + HM10_Read_LYWSD02(); + } + if (HM10.state.sensor==MIBLEsensors.size()-1) { + _nextSensorSlot= 0; + _counter++; + } + DEBUG_SENSOR_LOG(PSTR("%s active sensor now: %u"),D_CMND_HM10, HM10.state.sensor); + } + else _counter++; + if (_counter>HM10.period) _counter = 0; +} + +bool HM10Cmd(void) { + char command[CMDSZ]; + bool serviced = true; + uint8_t disp_len = strlen(D_CMND_HM10); + + if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_HM10), disp_len)) { + uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kHM10_Commands); + switch (command_code) { + case CMND_HM10_PERIOD: + if (XdrvMailbox.data_len > 0) { + if (command_code == CMND_HM10_PERIOD) { HM10.period = XdrvMailbox.payload; } + } + else { + if (command_code == CMND_HM10_PERIOD) XdrvMailbox.payload = HM10.period; + } + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_BAUD: + if (XdrvMailbox.data_len > 0) { + if (command_code == CMND_HM10_BAUD) { + HM10.serialSpeed = XdrvMailbox.payload; + HM10Serial->begin(HM10.serialSpeed); + } + } + else { + if (command_code == CMND_HM10_BAUD) XdrvMailbox.payload = HM10.serialSpeed; + } + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_TIME: + if (XdrvMailbox.data_len > 0) { + if(MIBLEsensors.size()>XdrvMailbox.payload){ + if(MIBLEsensors.at(XdrvMailbox.payload).type == LYWSD02){ + HM10.state.sensor = XdrvMailbox.payload; + HM10_Time_LYWSD02(); + } + } + } + Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); + break; + case CMND_HM10_AT: + HM10Serial->write("AT"); + if (strlen(XdrvMailbox.data)!=0) { + HM10Serial->write("+"); + HM10Serial->write(XdrvMailbox.data); + Response_P(S_JSON_HM10_COMMAND, ":AT+",XdrvMailbox.data); + } + else Response_P(S_JSON_HM10_COMMAND, ":AT",XdrvMailbox.data); + break; + case CMND_HM10_DISC_SCAN: + if (command_code == CMND_HM10_DISC_SCAN) { HM10_Discovery_Scan(); } + Response_P(S_JSON_HM10_COMMAND, command, ""); + break; + default: + + serviced = false; + break; + } + } else { + return false; + } + return serviced; +} + + + + + + +const char HTTP_HM10[] PROGMEM = "{s}HM10" " Firmware " "{m}%u{e}"; +const char HTTP_HM10_SERIAL[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; +const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; +const char HTTP_HM10_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%sus/cm{e}"; +const char HTTP_HM10_HL[] PROGMEM = "{s}
{m}
{e}"; + +void HM10Show(bool json) +{ + if (json) { + for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { + char slave[33]; + sprintf_P(slave,"%s-%02x%02x%02x",kHM10SlaveType[MIBLEsensors.at(i).type-1],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]); + char temperature[33]; + dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature); + + ResponseAppend_P(PSTR(",\"%s\":{"),slave); + if(MIBLEsensors.at(i).temp!=-1000.0f){ + ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature); + } + if (MIBLEsensors.at(i).type==FLORA){ + char lux[33]; + char moisture[33]; + char fertility[33]; + dtostrfd((float)MIBLEsensors.at(i).lux, 0, lux); + dtostrfd(MIBLEsensors.at(i).moisture, 0, moisture); + dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility); + if(MIBLEsensors.at(i).lux!=0xffff){ + ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%s"), lux); + } + if(MIBLEsensors.at(i).moisture!=-1000.0f){ + ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%s"), moisture); + } + if(MIBLEsensors.at(i).fertility!=-1000.0f){ + ResponseAppend_P(PSTR(",\"Fertility\":%s"), fertility); + } + } + if (MIBLEsensors.at(i).type>FLORA){ + char humidity[33]; + dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity); + if(MIBLEsensors.at(i).hum!=-1.0f){ + ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity); + } + if(MIBLEsensors.at(i).bat!=0xff){ + ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors.at(i).bat); + } + } + ResponseAppend_P(PSTR("}")); + } +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_HM10, HM10.firmware); + for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { + WSContentSend_PD(HTTP_HM10_HL); + WSContentSend_PD(HTTP_HM10_SERIAL, kHM10SlaveType[MIBLEsensors.at(i).type-1], D_MAC_ADDRESS, MIBLEsensors.at(i).serial[0], MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]); + if(MIBLEsensors.at(i).temp!=-1000.0f){ + char temperature[33]; + dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature); + WSContentSend_PD(HTTP_SNS_TEMP, kHM10SlaveType[MIBLEsensors.at(i).type-1], temperature, TempUnit()); + } + if (MIBLEsensors.at(i).type==FLORA){ + if(MIBLEsensors.at(i).lux!=0xffff){ + WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).lux); + } + if(MIBLEsensors.at(i).moisture!=-1000.0f){ + WSContentSend_PD(HTTP_SNS_MOISTURE, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).moisture); + } + if(MIBLEsensors.at(i).fertility!=-1000.0f){ + char fertility[33]; + dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility); + WSContentSend_PD(HTTP_HM10_FLORA_DATA, kHM10SlaveType[MIBLEsensors.at(i).type-1], fertility); + } + } + if (MIBLEsensors.at(i).type>FLORA){ + if(MIBLEsensors.at(i).hum!=-1.0f){ + char humidity[33]; + dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity); + WSContentSend_PD(HTTP_SNS_HUM, kHM10SlaveType[MIBLEsensors.at(i).type-1], humidity); + } + if(MIBLEsensors.at(i).bat!=0xff){ + WSContentSend_PD(HTTP_BATTERY, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).bat); + } + } + } +#endif + } +} + + + + + +bool Xsns62(uint8_t function) +{ + bool result = false; + + if ((pin[GPIO_HM10_RX] < 99) && (pin[GPIO_HM10_TX] < 99)) { + switch (function) { + case FUNC_INIT: + HM10SerialInit(); + break; + case FUNC_EVERY_50_MSECOND: + HM10SerialHandleFeedback(); + break; + case FUNC_EVERY_100_MSECOND: + if (HM10_TASK_LIST[0][0] != TASK_HM10_NOTASK) { + HM10_TaskEvery100ms(); + } + break; + case FUNC_EVERY_SECOND: + HM10EverySecond(); + break; + case FUNC_COMMAND: + result = HM10Cmd(); + break; + case FUNC_JSON_APPEND: + HM10Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + HM10Show(0); + break; +#endif + } + } + return result; +} +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_64_aht10.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_64_aht10.ino" +#ifdef USE_I2C +#ifdef USE_AHT10 + + + + + + +#define XSNS_64 64 +#define XI2C_43 43 + +#define AHT10_ADDR 0x38 + +unsigned char eSensorCalibrateCmd[3] = {0xE1, 0x08, 0x00}; +unsigned char eSensorNormalCmd[3] = {0xA8, 0x00, 0x00}; +unsigned char eSensorMeasureCmd[3] = {0xAC, 0x33, 0x00}; +unsigned char eSensorResetCmd = 0xBA; + +struct AHT10 { + float humidity = NAN; + float temperature = NAN; + uint8_t valid = 0; + uint8_t count = 0; + char name[6] = "AHT10"; +} AHT10; + +bool begin() +{ + Wire.begin(AHT10_ADDR); + Wire.beginTransmission(AHT10_ADDR); + Wire.write(eSensorCalibrateCmd, 3); + Wire.endTransmission(); + delay(500); + + if((readStatus() & 0x68) == 0x08) + return true; + else + { + return false; + } +} + +bool AHT10Read(void) +{ + unsigned long result, temp[6]; + + if (AHT10.valid) { AHT10.valid--; } + + Wire.beginTransmission(AHT10_ADDR); + Wire.write(eSensorMeasureCmd, 3); + Wire.endTransmission(); + delay(100); + + Wire.requestFrom(AHT10_ADDR, 6); + for(unsigned char i = 0; Wire.available() > 0; i++) + { + temp[i] = Wire.read(); + } + + AHT10.humidity = (((temp[1] << 16) | (temp[2] << 8) | temp[3]) >> 4)* 100 / 1048576; + AHT10.temperature = ((200 * (((temp[3] & 0x0F) << 16) | (temp[4] << 8) | temp[5])) / 1048576) - 50; + + if (isnan(AHT10.temperature) || isnan(AHT10.humidity)) { return false; } + + AHT10.valid = SENSOR_MAX_MISS; + return true; +} + + +unsigned char readStatus(void) +{ + unsigned char result = 0; + + Wire.requestFrom(AHT10_ADDR, 1); + result = Wire.read(); + return result; +} + +void AHT10Detect(void) +{ + if (I2cActive(AHT10_ADDR)) + { + return; + } + + if (begin()) + { + I2cSetActiveFound(AHT10_ADDR, AHT10.name); + AHT10.count = 1; + } +} + +void AHT10EverySecond(void) +{ + if (uptime &1) { + + if (!AHT10Read()) { + AddLogMissed(AHT10.name, AHT10.valid); + } + } +} + +void AHT10Show(bool json) +{ + if (AHT10.valid) { + char temperature[33]; + dtostrfd(AHT10.temperature, Settings.flag2.temperature_resolution, temperature); + char humidity[33]; + dtostrfd(AHT10.humidity, Settings.flag2.humidity_resolution, humidity); + + if (json) { + ResponseAppend_P(JSON_SNS_TEMPHUM, AHT10.name, temperature, humidity); +#ifdef USE_DOMOTICZ + if ((0 == tele_period)) { + DomoticzTempHumSensor(temperature, humidity); + } +#endif +#ifdef USE_KNX + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, AHT10.temperature); + KnxSensor(KNX_HUMIDITY, AHT10.humidity); + } +#endif +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, AHT10.name, temperature, TempUnit()); + WSContentSend_PD(HTTP_SNS_HUM, AHT10.name, humidity); +#endif + } + } +} + + + + + +bool Xsns64(uint8_t function) +{ + if (!I2cEnabled(XI2C_43)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + AHT10Detect(); + } + else if (AHT10.count) { + switch (function) { + case FUNC_EVERY_SECOND: + AHT10EverySecond(); + break; + case FUNC_JSON_APPEND: + AHT10Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + AHT10Show(0); + break; +#endif + } + } + return result; +} + +#endif +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_91_prometheus.ino" +# 22 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_91_prometheus.ino" +#ifdef USE_PROMETHEUS + + + + +#define XSNS_91 91 + +void HandleMetrics(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + + AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR("Prometheus")); + + WSContentBegin(200, CT_PLAIN); + + + char parameter[FLOATSZ]; + + if (global_temperature != 9999) { + dtostrfd(global_temperature, Settings.flag2.temperature_resolution, parameter); + WSContentSend_P(PSTR("# TYPE global_temperature gauge\nglobal_temperature %s\n"), parameter); + } + if (global_humidity != 0) { + dtostrfd(global_humidity, Settings.flag2.humidity_resolution, parameter); + WSContentSend_P(PSTR("# TYPE global_humidity gauge\nglobal_humidity %s\n"), parameter); + } + if (global_pressure != 0) { + dtostrfd(global_pressure, Settings.flag2.pressure_resolution, parameter); + WSContentSend_P(PSTR("# TYPE global_pressure gauge\nglobal_pressure %s\n"), parameter); + } + +#ifdef USE_ENERGY_SENSOR + dtostrfd(Energy.voltage[0], Settings.flag2.voltage_resolution, parameter); + WSContentSend_P(PSTR("# TYPE voltage gauge\nvoltage %s\n"), parameter); + dtostrfd(Energy.current[0], Settings.flag2.current_resolution, parameter); + WSContentSend_P(PSTR("# TYPE current gauge\ncurrent %s\n"), parameter); + dtostrfd(Energy.active_power[0], Settings.flag2.wattage_resolution, parameter); + WSContentSend_P(PSTR("# TYPE active_power gauge\nactive_power %s\n"), parameter); + dtostrfd(Energy.daily, Settings.flag2.energy_resolution, parameter); + WSContentSend_P(PSTR("# TYPE energy_daily gauge\nenergy_daily %s\n"), parameter); + dtostrfd(Energy.total, Settings.flag2.energy_resolution, parameter); + WSContentSend_P(PSTR("# TYPE energy_total counter\nenergy_total %s\n"), parameter); +#endif +# 80 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_91_prometheus.ino" + WSContentEnd(); +} + + + + + +bool Xsns91(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_WEB_ADD_HANDLER: + WebServer->on("/metrics", HandleMetrics); + break; + } + return result; +} + +#endif +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_interface.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_interface.ino" +#ifdef XFUNC_PTR_IN_ROM +bool (* const xsns_func_ptr[])(uint8_t) PROGMEM = { +#else +bool (* const xsns_func_ptr[])(uint8_t) = { +#endif + +#ifdef XSNS_01 + &Xsns01, +#endif + +#ifdef XSNS_02 + &Xsns02, +#endif + +#ifdef XSNS_03 + &Xsns03, +#endif + +#ifdef XSNS_04 + &Xsns04, +#endif + +#ifdef XSNS_05 + &Xsns05, +#endif + +#ifdef XSNS_06 + &Xsns06, +#endif + +#ifdef XSNS_07 + &Xsns07, +#endif + +#ifdef XSNS_08 + &Xsns08, +#endif + +#ifdef XSNS_09 + &Xsns09, +#endif + +#ifdef XSNS_10 + &Xsns10, +#endif + +#ifdef XSNS_11 + &Xsns11, +#endif + +#ifdef XSNS_12 + &Xsns12, +#endif + +#ifdef XSNS_13 + &Xsns13, +#endif + +#ifdef XSNS_14 + &Xsns14, +#endif + +#ifdef XSNS_15 + &Xsns15, +#endif + +#ifdef XSNS_16 + &Xsns16, +#endif + +#ifdef XSNS_17 + &Xsns17, +#endif + +#ifdef XSNS_18 + &Xsns18, +#endif + +#ifdef XSNS_19 + &Xsns19, +#endif + +#ifdef XSNS_20 + &Xsns20, +#endif + +#ifdef XSNS_21 + &Xsns21, +#endif + +#ifdef XSNS_22 + &Xsns22, +#endif + +#ifdef XSNS_23 + &Xsns23, +#endif + +#ifdef XSNS_24 + &Xsns24, +#endif + +#ifdef XSNS_25 + &Xsns25, +#endif + +#ifdef XSNS_26 + &Xsns26, +#endif + +#ifdef XSNS_27 + &Xsns27, +#endif + +#ifdef XSNS_28 + &Xsns28, +#endif + +#ifdef XSNS_29 + &Xsns29, +#endif + +#ifdef XSNS_30 + &Xsns30, +#endif + +#ifdef XSNS_31 + &Xsns31, +#endif + +#ifdef XSNS_32 + &Xsns32, +#endif + +#ifdef XSNS_33 + &Xsns33, +#endif + +#ifdef XSNS_34 + &Xsns34, +#endif + +#ifdef XSNS_35 + &Xsns35, +#endif + +#ifdef XSNS_36 + &Xsns36, +#endif + +#ifdef XSNS_37 + &Xsns37, +#endif + +#ifdef XSNS_38 + &Xsns38, +#endif + +#ifdef XSNS_39 + &Xsns39, +#endif + +#ifdef XSNS_40 + &Xsns40, +#endif + +#ifdef XSNS_41 + &Xsns41, +#endif + +#ifdef XSNS_42 + &Xsns42, +#endif + +#ifdef XSNS_43 + &Xsns43, +#endif + +#ifdef XSNS_44 + &Xsns44, +#endif + +#ifdef XSNS_45 + &Xsns45, +#endif + +#ifdef XSNS_46 + &Xsns46, +#endif + +#ifdef XSNS_47 + &Xsns47, +#endif + +#ifdef XSNS_48 + &Xsns48, +#endif + +#ifdef XSNS_49 + &Xsns49, +#endif + +#ifdef XSNS_50 + &Xsns50, +#endif + +#ifdef XSNS_51 + &Xsns51, +#endif + +#ifdef XSNS_52 + &Xsns52, +#endif + +#ifdef XSNS_53 + &Xsns53, +#endif + +#ifdef XSNS_54 + &Xsns54, +#endif + +#ifdef XSNS_55 + &Xsns55, +#endif + +#ifdef XSNS_56 + &Xsns56, +#endif + +#ifdef XSNS_57 + &Xsns57, +#endif + +#ifdef XSNS_58 + &Xsns58, +#endif + +#ifdef XSNS_59 + &Xsns59, +#endif + +#ifdef XSNS_60 + &Xsns60, +#endif + +#ifdef XSNS_61 + &Xsns61, +#endif + +#ifdef XSNS_62 + &Xsns62, +#endif + +#ifdef XSNS_63 + &Xsns63, +#endif + +#ifdef XSNS_64 + &Xsns64, +#endif + +#ifdef XSNS_65 + &Xsns65, +#endif + +#ifdef XSNS_66 + &Xsns66, +#endif + +#ifdef XSNS_67 + &Xsns67, +#endif + +#ifdef XSNS_68 + &Xsns68, +#endif + +#ifdef XSNS_69 + &Xsns69, +#endif + +#ifdef XSNS_70 + &Xsns70, +#endif + +#ifdef XSNS_71 + &Xsns71, +#endif + +#ifdef XSNS_72 + &Xsns72, +#endif + +#ifdef XSNS_73 + &Xsns73, +#endif + +#ifdef XSNS_74 + &Xsns74, +#endif + +#ifdef XSNS_75 + &Xsns75, +#endif + +#ifdef XSNS_76 + &Xsns76, +#endif + +#ifdef XSNS_77 + &Xsns77, +#endif + +#ifdef XSNS_78 + &Xsns78, +#endif + +#ifdef XSNS_79 + &Xsns79, +#endif + +#ifdef XSNS_80 + &Xsns80, +#endif + +#ifdef XSNS_81 + &Xsns81, +#endif + +#ifdef XSNS_82 + &Xsns82, +#endif + +#ifdef XSNS_83 + &Xsns83, +#endif + +#ifdef XSNS_84 + &Xsns84, +#endif + +#ifdef XSNS_85 + &Xsns85, +#endif + +#ifdef XSNS_86 + &Xsns86, +#endif + +#ifdef XSNS_87 + &Xsns87, +#endif + +#ifdef XSNS_88 + &Xsns88, +#endif + +#ifdef XSNS_89 + &Xsns89, +#endif + +#ifdef XSNS_90 + &Xsns90, +#endif + +#ifdef XSNS_91 + &Xsns91, +#endif + +#ifdef XSNS_92 + &Xsns92, +#endif + +#ifdef XSNS_93 + &Xsns93, +#endif + +#ifdef XSNS_94 + &Xsns94, +#endif + +#ifdef XSNS_95 + &Xsns95, +#endif + +#ifdef XSNS_96 + &Xsns96, +#endif + +#ifdef XSNS_97 + &Xsns97, +#endif + +#ifdef XSNS_98 + &Xsns98, +#endif + +#ifdef XSNS_99 + &Xsns99 +#endif +}; + +const uint8_t xsns_present = sizeof(xsns_func_ptr) / sizeof(xsns_func_ptr[0]); + + + + + +#ifdef XFUNC_PTR_IN_ROM +const uint8_t kXsnsList[] PROGMEM = { +#else +const uint8_t kXsnsList[] = { +#endif + +#ifdef XSNS_01 + XSNS_01, +#endif + +#ifdef XSNS_02 + XSNS_02, +#endif + +#ifdef XSNS_03 + XSNS_03, +#endif + +#ifdef XSNS_04 + XSNS_04, +#endif + +#ifdef XSNS_05 + XSNS_05, +#endif + +#ifdef XSNS_06 + XSNS_06, +#endif + +#ifdef XSNS_07 + XSNS_07, +#endif + +#ifdef XSNS_08 + XSNS_08, +#endif + +#ifdef XSNS_09 + XSNS_09, +#endif + +#ifdef XSNS_10 + XSNS_10, +#endif + +#ifdef XSNS_11 + XSNS_11, +#endif + +#ifdef XSNS_12 + XSNS_12, +#endif + +#ifdef XSNS_13 + XSNS_13, +#endif + +#ifdef XSNS_14 + XSNS_14, +#endif + +#ifdef XSNS_15 + XSNS_15, +#endif + +#ifdef XSNS_16 + XSNS_16, +#endif + +#ifdef XSNS_17 + XSNS_17, +#endif + +#ifdef XSNS_18 + XSNS_18, +#endif + +#ifdef XSNS_19 + XSNS_19, +#endif + +#ifdef XSNS_20 + XSNS_20, +#endif + +#ifdef XSNS_21 + XSNS_21, +#endif + +#ifdef XSNS_22 + XSNS_22, +#endif + +#ifdef XSNS_23 + XSNS_23, +#endif + +#ifdef XSNS_24 + XSNS_24, +#endif + +#ifdef XSNS_25 + XSNS_25, +#endif + +#ifdef XSNS_26 + XSNS_26, +#endif + +#ifdef XSNS_27 + XSNS_27, +#endif + +#ifdef XSNS_28 + XSNS_28, +#endif + +#ifdef XSNS_29 + XSNS_29, +#endif + +#ifdef XSNS_30 + XSNS_30, +#endif + +#ifdef XSNS_31 + XSNS_31, +#endif + +#ifdef XSNS_32 + XSNS_32, +#endif + +#ifdef XSNS_33 + XSNS_33, +#endif + +#ifdef XSNS_34 + XSNS_34, +#endif + +#ifdef XSNS_35 + XSNS_35, +#endif + +#ifdef XSNS_36 + XSNS_36, +#endif + +#ifdef XSNS_37 + XSNS_37, +#endif + +#ifdef XSNS_38 + XSNS_38, +#endif + +#ifdef XSNS_39 + XSNS_39, +#endif + +#ifdef XSNS_40 + XSNS_40, +#endif + +#ifdef XSNS_41 + XSNS_41, +#endif + +#ifdef XSNS_42 + XSNS_42, +#endif + +#ifdef XSNS_43 + XSNS_43, +#endif + +#ifdef XSNS_44 + XSNS_44, +#endif + +#ifdef XSNS_45 + XSNS_45, +#endif + +#ifdef XSNS_46 + XSNS_46, +#endif + +#ifdef XSNS_47 + XSNS_47, +#endif + +#ifdef XSNS_48 + XSNS_48, +#endif + +#ifdef XSNS_49 + XSNS_49, +#endif + +#ifdef XSNS_50 + XSNS_50, +#endif + +#ifdef XSNS_51 + XSNS_51, +#endif + +#ifdef XSNS_52 + XSNS_52, +#endif + +#ifdef XSNS_53 + XSNS_53, +#endif + +#ifdef XSNS_54 + XSNS_54, +#endif + +#ifdef XSNS_55 + XSNS_55, +#endif + +#ifdef XSNS_56 + XSNS_56, +#endif + +#ifdef XSNS_57 + XSNS_57, +#endif + +#ifdef XSNS_58 + XSNS_58, +#endif + +#ifdef XSNS_59 + XSNS_59, +#endif + +#ifdef XSNS_60 + XSNS_60, +#endif + +#ifdef XSNS_61 + XSNS_61, +#endif + +#ifdef XSNS_62 + XSNS_62, +#endif + +#ifdef XSNS_63 + XSNS_63, +#endif + +#ifdef XSNS_64 + XSNS_64, +#endif + +#ifdef XSNS_65 + XSNS_65, +#endif + +#ifdef XSNS_66 + XSNS_66, +#endif + +#ifdef XSNS_67 + XSNS_67, +#endif + +#ifdef XSNS_68 + XSNS_68, +#endif + +#ifdef XSNS_69 + XSNS_69, +#endif + +#ifdef XSNS_70 + XSNS_70, +#endif + +#ifdef XSNS_71 + XSNS_71, +#endif + +#ifdef XSNS_72 + XSNS_72, +#endif + +#ifdef XSNS_73 + XSNS_73, +#endif + +#ifdef XSNS_74 + XSNS_74, +#endif + +#ifdef XSNS_75 + XSNS_75, +#endif + +#ifdef XSNS_76 + XSNS_76, +#endif + +#ifdef XSNS_77 + XSNS_77, +#endif + +#ifdef XSNS_78 + XSNS_78, +#endif + +#ifdef XSNS_79 + XSNS_79, +#endif + +#ifdef XSNS_80 + XSNS_80, +#endif + +#ifdef XSNS_81 + XSNS_81, +#endif + +#ifdef XSNS_82 + XSNS_82, +#endif + +#ifdef XSNS_83 + XSNS_83, +#endif + +#ifdef XSNS_84 + XSNS_84, +#endif + +#ifdef XSNS_85 + XSNS_85, +#endif + +#ifdef XSNS_86 + XSNS_86, +#endif + +#ifdef XSNS_87 + XSNS_87, +#endif + +#ifdef XSNS_88 + XSNS_88, +#endif + +#ifdef XSNS_89 + XSNS_89, +#endif + +#ifdef XSNS_90 + XSNS_90, +#endif + +#ifdef XSNS_91 + XSNS_91, +#endif + +#ifdef XSNS_92 + XSNS_92, +#endif + +#ifdef XSNS_93 + XSNS_93, +#endif + +#ifdef XSNS_94 + XSNS_94, +#endif + +#ifdef XSNS_95 + XSNS_95, +#endif + +#ifdef XSNS_96 + XSNS_96, +#endif + +#ifdef XSNS_97 + XSNS_97, +#endif + +#ifdef XSNS_98 + XSNS_98, +#endif + +#ifdef XSNS_99 + XSNS_99 +#endif +}; + + + +bool XsnsEnabled(uint32_t sns_index) +{ + if (sns_index < sizeof(kXsnsList)) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t index = pgm_read_byte(kXsnsList + sns_index); +#else + uint32_t index = kXsnsList[sns_index]; +#endif + return bitRead(Settings.sensors[index / 32], index % 32); + } + return true; +} + +void XsnsSensorState(void) +{ + ResponseAppend_P(PSTR("\"")); + for (uint32_t i = 0; i < sizeof(kXsnsList); i++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t sensorid = pgm_read_byte(kXsnsList + i); +#else + uint32_t sensorid = kXsnsList[i]; +#endif + bool disabled = false; + if (sensorid < MAX_XSNS_DRIVERS) { + disabled = !bitRead(Settings.sensors[sensorid / 32], sensorid % 32); + } + ResponseAppend_P(PSTR("%s%s%d"), (i) ? "," : "", (disabled) ? "!" : "", sensorid); + } + ResponseAppend_P(PSTR("\"")); +} + + + + + +bool XsnsNextCall(uint8_t Function, uint8_t &xsns_index) +{ + xsns_index++; + if (xsns_index == xsns_present) { xsns_index = 0; } + +#ifndef USE_DEBUG_DRIVER + if (FUNC_WEB_SENSOR == Function) { +#endif + uint32_t max_disabled = xsns_present; + while (!XsnsEnabled(xsns_index) && max_disabled--) { + xsns_index++; + if (xsns_index == xsns_present) { xsns_index = 0; } + } +#ifndef USE_DEBUG_DRIVER + } +#endif + + return xsns_func_ptr[xsns_index](Function); +} + +bool XsnsCall(uint8_t Function) +{ + bool result = false; + + DEBUG_TRACE_LOG(PSTR("SNS: %d"), Function); + +#ifdef PROFILE_XSNS_EVERY_SECOND + uint32_t profile_start_millis = millis(); +#endif + + for (uint32_t x = 0; x < xsns_present; x++) { +#ifdef USE_DEBUG_DRIVER + if (XsnsEnabled(x)) { +#endif + + if ((FUNC_WEB_SENSOR == Function) && !XsnsEnabled(x)) { continue; } + +#ifdef PROFILE_XSNS_SENSOR_EVERY_SECOND + uint32_t profile_start_millis = millis(); +#endif + result = xsns_func_ptr[x](Function); + +#ifdef PROFILE_XSNS_SENSOR_EVERY_SECOND + uint32_t profile_millis = millis() - profile_start_millis; + if (profile_millis) { + if (FUNC_EVERY_SECOND == Function) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PRF: At %08u XsnsCall %d to Sensor %d took %u mS"), uptime, Function, x, profile_millis); + } + } +#endif + + if (result && ((FUNC_COMMAND == Function) || + (FUNC_PIN_STATE == Function) || + (FUNC_COMMAND_SENSOR == Function) + )) { + break; + } +#ifdef USE_DEBUG_DRIVER + } +#endif + } + +#ifdef PROFILE_XSNS_EVERY_SECOND + uint32_t profile_millis = millis() - profile_start_millis; + if (profile_millis) { + if (FUNC_EVERY_SECOND == Function) { + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PRF: At %08u XsnsCall %d took %u mS"), uptime, Function, profile_millis); + } + } +#endif + + return result; +} +# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xx2c_interface.ino" +# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xx2c_interface.ino" +#ifdef USE_I2C + +#ifdef XFUNC_PTR_IN_ROM +const uint8_t kI2cList[] PROGMEM = { +#else +const uint8_t kI2cList[] = { +#endif + +#ifdef XI2C_01 + XI2C_01, +#endif + +#ifdef XI2C_02 + XI2C_02, +#endif + +#ifdef XI2C_03 + XI2C_03, +#endif + +#ifdef XI2C_04 + XI2C_04, +#endif + +#ifdef XI2C_05 + XI2C_05, +#endif + +#ifdef XI2C_06 + XI2C_06, +#endif + +#ifdef XI2C_07 + XI2C_07, +#endif + +#ifdef XI2C_08 + XI2C_08, +#endif + +#ifdef XI2C_09 + XI2C_09, +#endif + +#ifdef XI2C_10 + XI2C_10, +#endif + +#ifdef XI2C_11 + XI2C_11, +#endif + +#ifdef XI2C_12 + XI2C_12, +#endif + +#ifdef XI2C_13 + XI2C_13, +#endif + +#ifdef XI2C_14 + XI2C_14, +#endif + +#ifdef XI2C_15 + XI2C_15, +#endif + +#ifdef XI2C_16 + XI2C_16, +#endif + +#ifdef XI2C_17 + XI2C_17, +#endif + +#ifdef XI2C_18 + XI2C_18, +#endif + +#ifdef XI2C_19 + XI2C_19, +#endif + +#ifdef XI2C_20 + XI2C_20, +#endif + +#ifdef XI2C_21 + XI2C_21, +#endif + +#ifdef XI2C_22 + XI2C_22, +#endif + +#ifdef XI2C_23 + XI2C_23, +#endif + +#ifdef XI2C_24 + XI2C_24, +#endif + +#ifdef XI2C_25 + XI2C_25, +#endif + +#ifdef XI2C_26 + XI2C_26, +#endif + +#ifdef XI2C_27 + XI2C_27, +#endif + +#ifdef XI2C_28 + XI2C_28, +#endif + +#ifdef XI2C_29 + XI2C_29, +#endif + +#ifdef XI2C_30 + XI2C_30, +#endif + +#ifdef XI2C_31 + XI2C_31, +#endif + +#ifdef XI2C_32 + XI2C_32, +#endif + +#ifdef XI2C_33 + XI2C_33, +#endif + +#ifdef XI2C_34 + XI2C_34, +#endif + +#ifdef XI2C_35 + XI2C_35, +#endif + +#ifdef XI2C_36 + XI2C_36, +#endif + +#ifdef XI2C_37 + XI2C_37, +#endif + +#ifdef XI2C_38 + XI2C_38, +#endif + +#ifdef XI2C_39 + XI2C_39, +#endif + +#ifdef XI2C_40 + XI2C_40, +#endif + +#ifdef XI2C_41 + XI2C_41, +#endif + +#ifdef XI2C_42 + XI2C_42, +#endif + +#ifdef XI2C_43 + XI2C_43, +#endif + +#ifdef XI2C_44 + XI2C_44, +#endif + +#ifdef XI2C_45 + XI2C_45, +#endif + +#ifdef XI2C_46 + XI2C_46, +#endif + +#ifdef XI2C_47 + XI2C_47, +#endif + +#ifdef XI2C_48 + XI2C_48, +#endif + +#ifdef XI2C_49 + XI2C_49, +#endif + +#ifdef XI2C_50 + XI2C_50, +#endif + +#ifdef XI2C_51 + XI2C_51, +#endif + +#ifdef XI2C_52 + XI2C_52, +#endif + +#ifdef XI2C_53 + XI2C_53, +#endif + +#ifdef XI2C_54 + XI2C_54, +#endif + +#ifdef XI2C_55 + XI2C_55, +#endif + +#ifdef XI2C_56 + XI2C_56, +#endif + +#ifdef XI2C_57 + XI2C_57, +#endif + +#ifdef XI2C_58 + XI2C_58, +#endif + +#ifdef XI2C_59 + XI2C_59, +#endif + +#ifdef XI2C_60 + XI2C_60, +#endif + +#ifdef XI2C_61 + XI2C_61, +#endif + +#ifdef XI2C_62 + XI2C_62, +#endif + +#ifdef XI2C_63 + XI2C_63, +#endif + +#ifdef XI2C_64 + XI2C_64, +#endif + +#ifdef XI2C_65 + XI2C_65, +#endif + +#ifdef XI2C_66 + XI2C_66, +#endif + +#ifdef XI2C_67 + XI2C_67, +#endif + +#ifdef XI2C_68 + XI2C_68, +#endif + +#ifdef XI2C_69 + XI2C_69, +#endif + +#ifdef XI2C_70 + XI2C_70, +#endif + +#ifdef XI2C_71 + XI2C_71, +#endif + +#ifdef XI2C_72 + XI2C_72, +#endif + +#ifdef XI2C_73 + XI2C_73, +#endif + +#ifdef XI2C_74 + XI2C_74, +#endif + +#ifdef XI2C_75 + XI2C_75, +#endif + +#ifdef XI2C_76 + XI2C_76, +#endif + +#ifdef XI2C_77 + XI2C_77, +#endif + +#ifdef XI2C_78 + XI2C_78, +#endif + +#ifdef XI2C_79 + XI2C_79, +#endif + +#ifdef XI2C_80 + XI2C_80, +#endif + +#ifdef XI2C_81 + XI2C_81, +#endif + +#ifdef XI2C_82 + XI2C_82, +#endif + +#ifdef XI2C_83 + XI2C_83, +#endif + +#ifdef XI2C_84 + XI2C_84, +#endif + +#ifdef XI2C_85 + XI2C_85, +#endif + +#ifdef XI2C_86 + XI2C_86, +#endif + +#ifdef XI2C_87 + XI2C_87, +#endif + +#ifdef XI2C_88 + XI2C_88, +#endif + +#ifdef XI2C_89 + XI2C_89, +#endif + +#ifdef XI2C_90 + XI2C_90, +#endif + +#ifdef XI2C_91 + XI2C_91, +#endif + +#ifdef XI2C_92 + XI2C_92, +#endif + +#ifdef XI2C_93 + XI2C_93, +#endif + +#ifdef XI2C_94 + XI2C_94, +#endif + +#ifdef XI2C_95 + XI2C_95, +#endif + +#ifdef XI2C_96 + XI2C_96 +#endif +}; + + + +bool I2cEnabled(uint32_t i2c_index) +{ + return (i2c_flg && bitRead(Settings.i2c_drivers[i2c_index / 32], i2c_index % 32)); +} + +void I2cDriverState(void) +{ + ResponseAppend_P(PSTR("\"")); + for (uint32_t i = 0; i < sizeof(kI2cList); i++) { +#ifdef XFUNC_PTR_IN_ROM + uint32_t i2c_driver_id = pgm_read_byte(kI2cList + i); +#else + uint32_t i2c_driver_id = kI2cList[i]; +#endif + bool disabled = false; + if (i2c_driver_id < MAX_I2C_DRIVERS) { + disabled = !bitRead(Settings.i2c_drivers[i2c_driver_id / 32], i2c_driver_id % 32); + } + ResponseAppend_P(PSTR("%s%s%d"), (i) ? "," : "", (disabled) ? "!" : "", i2c_driver_id); + } + ResponseAppend_P(PSTR("\"")); +} + +#endif \ No newline at end of file From 2e0a06c19aae33fe030245ece2c938c5e0fdda24 Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Tue, 18 Feb 2020 09:49:53 +0100 Subject: [PATCH 05/13] structual changes structual changes --- tasmota/xsns_64_aht10.ino | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tasmota/xsns_64_aht10.ino b/tasmota/xsns_64_aht10.ino index 15dbbb5c1..00a9a262c 100644 --- a/tasmota/xsns_64_aht10.ino +++ b/tasmota/xsns_64_aht10.ino @@ -56,12 +56,12 @@ bool begin() else { return false; - } + } } bool AHT10Read(void) { - unsigned long result, temp[6]; + unsigned long result_t, result_h, temp[6]; if (AHT10.valid) { AHT10.valid--; } @@ -69,15 +69,17 @@ bool AHT10Read(void) Wire.write(eSensorMeasureCmd, 3); Wire.endTransmission(); delay(100); - - Wire.requestFrom(AHT10_ADDR, 6); + + Wire.requestFrom(AHT10_ADDR, 6); for(unsigned char i = 0; Wire.available() > 0; i++) { temp[i] = Wire.read(); - } + } + result_h = ((temp[1] << 16) | (temp[2] << 8) | temp[3]) >> 4; + result_t = ((temp[3] & 0x0F) << 16) | (temp[4] << 8) | temp[5]; - AHT10.humidity = (((temp[1] << 16) | (temp[2] << 8) | temp[3]) >> 4)* 100 / 1048576; - AHT10.temperature = ((200 * (((temp[3] & 0x0F) << 16) | (temp[4] << 8) | temp[5])) / 1048576) - 50; + AHT10.humidity = result_h * 100 / 1048576; + AHT10.temperature = ((200 * result_t) / 1048576) - 50; if (isnan(AHT10.temperature) || isnan(AHT10.humidity)) { return false; } @@ -97,12 +99,12 @@ unsigned char readStatus(void) void AHT10Detect(void) { - if (I2cActive(AHT10_ADDR)) - { - return; + if (I2cActive(AHT10_ADDR)) + { + return; } - if (begin()) + if (begin()) { I2cSetActiveFound(AHT10_ADDR, AHT10.name); AHT10.count = 1; From b423c9716a7bf4318d99f9abe5e5780f900cbeb7 Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Tue, 18 Feb 2020 09:56:23 +0100 Subject: [PATCH 06/13] Update xsns_64_aht10.ino --- tasmota/xsns_64_aht10.ino | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tasmota/xsns_64_aht10.ino b/tasmota/xsns_64_aht10.ino index 00a9a262c..f45ca3f4f 100644 --- a/tasmota/xsns_64_aht10.ino +++ b/tasmota/xsns_64_aht10.ino @@ -91,7 +91,6 @@ bool AHT10Read(void) unsigned char readStatus(void) { unsigned char result = 0; - Wire.requestFrom(AHT10_ADDR, 1); result = Wire.read(); return result; @@ -100,9 +99,7 @@ unsigned char readStatus(void) void AHT10Detect(void) { if (I2cActive(AHT10_ADDR)) - { - return; - } + {return;} if (begin()) { From e2ede9ed5543c36d03210d09abe0dff51c70da0c Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Tue, 18 Feb 2020 20:08:22 +0100 Subject: [PATCH 07/13] Add Reset Command --- tasmota/xsns_64_aht10.ino | 42 +++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/tasmota/xsns_64_aht10.ino b/tasmota/xsns_64_aht10.ino index f45ca3f4f..7405c5537 100644 --- a/tasmota/xsns_64_aht10.ino +++ b/tasmota/xsns_64_aht10.ino @@ -43,21 +43,6 @@ struct AHT10 { char name[6] = "AHT10"; } AHT10; -bool begin() -{ - Wire.begin(AHT10_ADDR); - Wire.beginTransmission(AHT10_ADDR); - Wire.write(eSensorCalibrateCmd, 3); - Wire.endTransmission(); - delay(500); - - if((readStatus() & 0x68) == 0x08) - return true; - else - { - return false; - } -} bool AHT10Read(void) { @@ -88,7 +73,21 @@ bool AHT10Read(void) } /********************************************************************************************/ -unsigned char readStatus(void) +bool aht10init() +{ + Wire.begin(AHT10_ADDR); + Wire.beginTransmission(AHT10_ADDR); + Wire.write(eSensorCalibrateCmd, 3); + Wire.endTransmission(); + delay(500); + + if((aht10ReadStatus() & 0x68) == 0x08) { + return true;} + else + { return false; } +} + +unsigned char aht10ReadStatus(void) { unsigned char result = 0; Wire.requestFrom(AHT10_ADDR, 1); @@ -96,12 +95,21 @@ unsigned char readStatus(void) return result; } +void aht10Reset(void) +{ + Wire.beginTransmission(AHT10_ADDR); + Wire.write(eSensorResetCmd); + Wire.endTransmission(); + delay(20); +} + +/********************************************************************************************/ void AHT10Detect(void) { if (I2cActive(AHT10_ADDR)) {return;} - if (begin()) + if (aht10init()) { I2cSetActiveFound(AHT10_ADDR, AHT10.name); AHT10.count = 1; From 81b88a71a084ff83e6e975a1114d320ac16b38f3 Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Tue, 18 Feb 2020 20:13:12 +0100 Subject: [PATCH 08/13] Update xsns_64_aht10.ino Correct text --- tasmota/xsns_64_aht10.ino | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tasmota/xsns_64_aht10.ino b/tasmota/xsns_64_aht10.ino index 7405c5537..95d5a3e2b 100644 --- a/tasmota/xsns_64_aht10.ino +++ b/tasmota/xsns_64_aht10.ino @@ -73,7 +73,7 @@ bool AHT10Read(void) } /********************************************************************************************/ -bool aht10init() +bool AHT10Init() { Wire.begin(AHT10_ADDR); Wire.beginTransmission(AHT10_ADDR); @@ -81,13 +81,13 @@ bool aht10init() Wire.endTransmission(); delay(500); - if((aht10ReadStatus() & 0x68) == 0x08) { + if((AHT10ReadStatus() & 0x68) == 0x08) { return true;} else { return false; } } -unsigned char aht10ReadStatus(void) +unsigned char AHT10ReadStatus(void) { unsigned char result = 0; Wire.requestFrom(AHT10_ADDR, 1); @@ -95,7 +95,7 @@ unsigned char aht10ReadStatus(void) return result; } -void aht10Reset(void) +void AHT10Reset(void) { Wire.beginTransmission(AHT10_ADDR); Wire.write(eSensorResetCmd); @@ -109,7 +109,7 @@ void AHT10Detect(void) if (I2cActive(AHT10_ADDR)) {return;} - if (aht10init()) + if (AHT10Init()) { I2cSetActiveFound(AHT10_ADDR, AHT10.name); AHT10.count = 1; From 7f72d266f8eb5a38013c0cca20e76c3412958aa0 Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Wed, 19 Feb 2020 09:37:36 +0100 Subject: [PATCH 09/13] prepare for merging --- I2CDEVICES.md | 2 +- tasmota/my_user_config.h | 2 +- tasmota/support_features.ino | 4 +- tasmota/tasmota.ino.cpp | 77284 ---------------- .../{xsns_64_aht10.ino => xsns_63_aht1x.ino} | 15 +- 5 files changed, 12 insertions(+), 77295 deletions(-) delete mode 100644 tasmota/tasmota.ino.cpp rename tasmota/{xsns_64_aht10.ino => xsns_63_aht1x.ino} (94%) diff --git a/I2CDEVICES.md b/I2CDEVICES.md index be92d03dc..b607b3241 100644 --- a/I2CDEVICES.md +++ b/I2CDEVICES.md @@ -64,4 +64,4 @@ Index | Define | Driver | Device | Address(es) | Description 41 | USE_DHT12 | xsns_58 | DHT12 | 0x5C | Temperature and humidity sensor 42 | USE_DS1624 | xsns_59 | DS1621 | 0x48 - 0x4F | Temperature sensor 42 | USE_DS1624 | xsns_59 | DS1624 | 0x48 - 0x4F | Temperature sensor - 43 | USE_AHT10 | xsns_64 | AHT10 | 0x38 | Temperature and humidity sensor + 43 | USE_AHT1x | xsns_63 | AHT10/15 | 0x38 | Temperature and humidity sensor diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 04395065a..7f7fa3a45 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -486,7 +486,7 @@ // #define USE_HIH6 // [I2cDriver36] Enable Honeywell HIH Humidity and Temperature sensor (I2C address 0x27) (+0k6) // #define USE_DHT12 // [I2cDriver41] Enable DHT12 humidity and temperature sensor (I2C address 0x5C) (+0k7 code) // #define USE_DS1624 // [I2cDriver42] Enable DS1624, DS1621 temperature sensor (I2C addresses 0x48 - 0x4F) (+1k2 code) - #define USE_AHT10 // [I2cDriver43] Enable AHT10 humidity and temperature sensor (I2C address 0x38) (+0k8 code) + #define USE_AHT1x // [I2cDriver43] Enable AHT10/15 humidity and temperature sensor (I2C address 0x38) (+0k8 code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 933db0960..648235f12 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -513,8 +513,8 @@ void GetFeatures(void) #ifdef USE_LE01MR feature5 |= 0x08000000; // xnrg_13_fif_le01mr.ino #endif -#ifdef USE_AHT10 - feature5 |= 0x10000000; // xsns_64_aht10.ino +#ifdef USE_AHT1x + feature5 |= 0x10000000; // xsns_63_aht1x.ino #endif // feature5 |= 0x10000000; // feature5 |= 0x20000000; diff --git a/tasmota/tasmota.ino.cpp b/tasmota/tasmota.ino.cpp deleted file mode 100644 index ddd9b7b19..000000000 --- a/tasmota/tasmota.ino.cpp +++ /dev/null @@ -1,77284 +0,0 @@ -# 1 "C:\\Users\\martin\\AppData\\Local\\Temp\\tmpeqetuoko" -#include -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota.ino" -# 29 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota.ino" -#include -#include "tasmota_version.h" -#include "tasmota.h" -#include "my_user_config.h" -#ifdef USE_MQTT_TLS - #include -#endif -#include "tasmota_post.h" -#include "i18n.h" -#include "tasmota_template.h" - -#ifdef ARDUINO_ESP8266_RELEASE_2_4_0 -#include "lwip/init.h" -#if LWIP_VERSION_MAJOR != 1 - #error Please use stable lwIP v1.4 -#endif -#endif - - -#include -#include -#include -#include -#ifdef USE_ARDUINO_OTA - #include - #ifndef USE_DISCOVERY - #define USE_DISCOVERY - #endif -#endif -#ifdef USE_DISCOVERY - #include -#endif -#ifdef USE_I2C - #include -#endif -#ifdef USE_SPI - #include -#endif - - -#include "settings.h" - - - - - -WiFiUDP PortUdp; - -unsigned long feature_drv1; -unsigned long feature_drv2; -unsigned long feature_sns1; -unsigned long feature_sns2; -unsigned long feature5; -unsigned long feature6; -unsigned long serial_polling_window = 0; -unsigned long state_second = 0; -unsigned long state_50msecond = 0; -unsigned long state_100msecond = 0; -unsigned long state_250msecond = 0; -unsigned long pulse_timer[MAX_PULSETIMERS] = { 0 }; -unsigned long blink_timer = 0; -unsigned long backlog_delay = 0; -power_t power = 0; -power_t last_power = 0; -power_t blink_power; -power_t blink_mask = 0; -power_t blink_powersave; -power_t latching_power = 0; -power_t rel_inverted = 0; -int serial_in_byte_counter = 0; -int ota_state_flag = 0; -int ota_result = 0; -int restart_flag = 0; -int wifi_state_flag = WIFI_RESTART; -int blinks = 201; -uint32_t uptime = 0; -uint32_t loop_load_avg = 0; -uint32_t global_update = 0; -uint32_t web_log_index = 1; -float global_temperature = 9999; -float global_humidity = 0; -float global_pressure = 0; -uint16_t tele_period = 9999; -uint16_t blink_counter = 0; -uint16_t seriallog_timer = 0; -uint16_t syslog_timer = 0; -int16_t save_data_counter; -RulesBitfield rules_flag; -uint8_t mqtt_cmnd_blocked = 0; -uint8_t mqtt_cmnd_blocked_reset = 0; -uint8_t state_250mS = 0; -uint8_t latching_relay_pulse = 0; -uint8_t sleep; -uint8_t blinkspeed = 1; -uint8_t pin[GPIO_MAX]; -uint8_t active_device = 1; -uint8_t leds_present = 0; -uint8_t led_inverted = 0; -uint8_t led_power = 0; -uint8_t ledlnk_inverted = 0; -uint8_t pwm_inverted = 0; -uint8_t energy_flg = 0; -uint8_t light_flg = 0; -uint8_t light_type = 0; -uint8_t serial_in_byte; -uint8_t ota_retry_counter = OTA_ATTEMPTS; -uint8_t devices_present = 0; -uint8_t seriallog_level; -uint8_t syslog_level; -uint8_t my_module_type; -uint8_t my_adc0; -uint8_t last_source = 0; -uint8_t shutters_present = 0; -uint8_t prepped_loglevel = 0; - -bool serial_local = false; -bool fallback_topic_flag = false; -bool backlog_mutex = false; -bool interlock_mutex = false; -bool stop_flash_rotate = false; -bool blinkstate = false; - -bool pwm_present = false; -bool i2c_flg = false; -bool spi_flg = false; -bool soft_spi_flg = false; -bool ntp_force_sync = false; -bool is_8285 = false; -myio my_module; -gpio_flag my_module_flag; -StateBitfield global_state; -char my_version[33]; -char my_image[33]; -char my_hostname[33]; -char mqtt_client[TOPSZ]; -char mqtt_topic[TOPSZ]; -char serial_in_buffer[INPUT_BUFFER_SIZE]; -char mqtt_data[MESSZ]; -char log_data[LOGSZ]; -char web_log[WEB_LOG_SIZE] = {'\0'}; -#ifdef SUPPORT_IF_STATEMENT - #include - LinkedList backlog; - #define BACKLOG_EMPTY (backlog.size() == 0) -#else - uint8_t backlog_index = 0; - uint8_t backlog_pointer = 0; - String backlog[MAX_BACKLOG]; - #define BACKLOG_EMPTY (backlog_pointer == backlog_index) -#endif -void setup(void); -void BacklogLoop(void); -void loop(void); -uint16_t SendMail(char *buffer); -uint32_t GetRtcSettingsCrc(void); -void RtcSettingsSave(void); -void RtcSettingsLoad(void); -bool RtcSettingsValid(void); -uint32_t GetRtcRebootCrc(void); -void RtcRebootSave(void); -void RtcRebootReset(void); -void RtcRebootLoad(void); -bool RtcRebootValid(void); -void SetFlashModeDout(void); -bool VersionCompatible(void); -void SettingsBufferFree(void); -bool SettingsBufferAlloc(void); -uint16_t GetCfgCrc16(uint8_t *bytes, uint32_t size); -uint16_t GetSettingsCrc(void); -uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size); -uint32_t GetSettingsCrc32(void); -void SettingsSaveAll(void); -void UpdateQuickPowerCycle(bool update); -uint32_t GetSettingsTextLen(void); -bool SettingsUpdateText(uint32_t index, const char* replace_me); -char* SettingsText(uint32_t index); -uint32_t GetSettingsAddress(void); -void SettingsSave(uint8_t rotate); -void SettingsLoad(void); -void EspErase(uint32_t start_sector, uint32_t end_sector); -void SettingsErase(uint8_t type); -void SettingsSdkErase(void); -void SettingsDefault(void); -void SettingsDefaultSet1(void); -void SettingsDefaultSet2(void); -void SettingsResetStd(void); -void SettingsResetDst(void); -void SettingsDefaultWebColor(void); -void SettingsEnableAllI2cDrivers(void); -void SettingsDelta(void); -void OsWatchTicker(void); -void OsWatchInit(void); -void OsWatchLoop(void); -bool OsWatchBlockedLoop(void); -uint32_t ResetReason(void); -String GetResetReason(void); -size_t strchrspn(const char *str1, int character); -char* subStr(char* dest, char* str, const char *delim, int index); -float CharToFloat(const char *str); -int TextToInt(char *str); -char* ulltoa(unsigned long long value, char *str, int radix); -char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween); -char* Uint64toHex(uint64_t value, char *str, uint16_t bits); -char* dtostrfd(double number, unsigned char prec, char *s); -char* Unescape(char* buffer, uint32_t* size); -char* RemoveSpace(char* p); -char* ReplaceCommaWithDot(char* p); -char* LowerCase(char* dest, const char* source); -char* UpperCase(char* dest, const char* source); -char* UpperCase_P(char* dest, const char* source); -char* Trim(char* p); -char* RemoveAllSpaces(char* p); -char* NoAlNumToUnderscore(char* dest, const char* source); -char IndexSeparator(void); -void SetShortcutDefault(void); -uint8_t Shortcut(void); -bool ValidIpAddress(const char* str); -bool ParseIp(uint32_t* addr, const char* str); -uint32_t ParseParameters(uint32_t count, uint32_t *params); -bool NewerVersion(char* version_str); -char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option); -char* GetPowerDevice(char* dest, uint32_t idx, size_t size); -void GetEspHardwareType(void); -String GetDeviceHardware(void); -float ConvertTemp(float c); -float ConvertTempToCelsius(float c); -char TempUnit(void); -float ConvertHumidity(float h); -float ConvertPressure(float p); -String PressureUnit(void); -void ResetGlobalValues(void); -uint32_t SqrtInt(uint32_t num); -uint32_t RoundSqrtInt(uint32_t num); -char* GetTextIndexed(char* destination, size_t destination_size, uint32_t index, const char* haystack); -int GetCommandCode(char* destination, size_t destination_size, const char* needle, const char* haystack); -int GetStateNumber(char *state_text); -String GetSerialConfig(void); -void SetSerialBegin(); -void SetSerialConfig(uint32_t serial_config); -void SetSerialBaudrate(uint32_t baudrate); -void SetSerial(uint32_t baudrate, uint32_t serial_config); -void ClaimSerial(void); -void SerialSendRaw(char *codes); -uint32_t GetHash(const char *buffer, size_t size); -void ShowSource(uint32_t source); -void WebHexCode(uint32_t i, const char* code); -uint32_t WebColor(uint32_t i); -char* ResponseGetTime(uint32_t format, char* time_str); -int Response_P(const char* format, ...); -int ResponseTime_P(const char* format, ...); -int ResponseAppend_P(const char* format, ...); -int ResponseAppendTimeFormat(uint32_t format); -int ResponseAppendTime(void); -int ResponseJsonEnd(void); -int ResponseJsonEndEnd(void); -void DigitalWrite(uint32_t gpio_pin, uint32_t state); -uint8_t ModuleNr(void); -bool ValidTemplateModule(uint32_t index); -bool ValidModule(uint32_t index); -String AnyModuleName(uint32_t index); -String ModuleName(void); -void ModuleGpios(myio *gp); -gpio_flag ModuleFlag(void); -void ModuleDefault(uint32_t module); -void SetModuleType(void); -bool FlashPin(uint32_t pin); -uint8_t ValidPin(uint32_t pin, uint32_t gpio); -bool ValidGPIO(uint32_t pin, uint32_t gpio); -bool ValidAdc(void); -bool GetUsedInModule(uint32_t val, uint8_t *arr); -bool JsonTemplate(const char* dataBuf); -void TemplateJson(void); -inline int32_t TimeDifference(uint32_t prev, uint32_t next); -int32_t TimePassedSince(uint32_t timestamp); -bool TimeReached(uint32_t timer); -void SetNextTimeInterval(unsigned long& timer, const unsigned long step); -int32_t TimePassedSinceUsec(uint32_t timestamp); -bool TimeReachedUsec(uint32_t timer); -bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size); -bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg); -bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg); -bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg); -bool I2cValidRead16LE(uint16_t *data, uint8_t addr, uint8_t reg); -bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg); -bool I2cValidRead24(int32_t *data, uint8_t addr, uint8_t reg); -uint8_t I2cRead8(uint8_t addr, uint8_t reg); -uint16_t I2cRead16(uint8_t addr, uint8_t reg); -int16_t I2cReadS16(uint8_t addr, uint8_t reg); -uint16_t I2cRead16LE(uint8_t addr, uint8_t reg); -int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg); -int32_t I2cRead24(uint8_t addr, uint8_t reg); -bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size); -bool I2cWrite8(uint8_t addr, uint8_t reg, uint16_t val); -bool I2cWrite16(uint8_t addr, uint8_t reg, uint16_t val); -int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len); -int8_t I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len); -void I2cScan(char *devs, unsigned int devs_len); -void I2cSetActiveFound(uint32_t addr, const char *types); -bool I2cActive(uint32_t addr); -bool I2cSetDevice(uint32_t addr); -void SetSeriallog(uint32_t loglevel); -void SetSyslog(uint32_t loglevel); -void GetLog(uint32_t idx, char** entry_pp, size_t* len_p); -void Syslog(void); -void AddLog(uint32_t loglevel); -void AddLog_P(uint32_t loglevel, const char *formatP); -void AddLog_P(uint32_t loglevel, const char *formatP, const char *formatP2); -void PrepLog_P2(uint32_t loglevel, PGM_P formatP, ...); -void AddLog_P2(uint32_t loglevel, PGM_P formatP, ...); -void AddLog_Debug(PGM_P formatP, ...); -void AddLogBuffer(uint32_t loglevel, uint8_t *buffer, uint32_t count); -void AddLogSerial(uint32_t loglevel); -void AddLogMissed(char *sensor, uint32_t misses); -void ButtonPullupFlag(uint8 button_bit); -void ButtonInvertFlag(uint8 button_bit); -void ButtonInit(void); -uint8_t ButtonSerial(uint8_t serial_in_byte); -void ButtonHandler(void); -void ButtonLoop(void); -void ResponseCmndNumber(int value); -void ResponseCmndFloat(float value, uint32_t decimals); -void ResponseCmndIdxNumber(int value); -void ResponseCmndChar(const char* value); -void ResponseCmndStateText(uint32_t value); -void ResponseCmndDone(void); -void ResponseCmndIdxChar(const char* value); -void ResponseCmndAll(uint32_t text_index, uint32_t count); -void ExecuteCommand(const char *cmnd, uint32_t source); -void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len); -void CmndBacklog(void); -void CmndDelay(void); -void CmndPower(void); -void CmndStatus(void); -void CmndState(void); -void CmndTempOffset(void); -void CmndSleep(void); -void CmndUpgrade(void); -void CmndOtaUrl(void); -void CmndSeriallog(void); -void CmndRestart(void); -void CmndPowerOnState(void); -void CmndPulsetime(void); -void CmndBlinktime(void); -void CmndBlinkcount(void); -void CmndSavedata(void); -void CmndSetoption(void); -void CmndTemperatureResolution(void); -void CmndHumidityResolution(void); -void CmndPressureResolution(void); -void CmndPowerResolution(void); -void CmndVoltageResolution(void); -void CmndFrequencyResolution(void); -void CmndCurrentResolution(void); -void CmndEnergyResolution(void); -void CmndWeightResolution(void); -void CmndModule(void); -void CmndModules(void); -void CmndGpio(void); -void CmndGpios(void); -void CmndTemplate(void); -void CmndPwm(void); -void CmndPwmfrequency(void); -void CmndPwmrange(void); -void CmndButtonDebounce(void); -void CmndSwitchDebounce(void); -void CmndBaudrate(void); -void CmndSerialConfig(void); -void CmndSerialSend(void); -void CmndSerialDelimiter(void); -void CmndSyslog(void); -void CmndLoghost(void); -void CmndLogport(void); -void CmndIpAddress(void); -void CmndNtpServer(void); -void CmndAp(void); -void CmndSsid(void); -void CmndPassword(void); -void CmndHostname(void); -void CmndWifiConfig(void); -void CmndFriendlyname(void); -void CmndSwitchMode(void); -void CmndInterlock(void); -void CmndTeleperiod(void); -void CmndReset(void); -void CmndTime(void); -void CmndTimezone(void); -void CmndTimeStdDst(uint32_t ts); -void CmndTimeStd(void); -void CmndTimeDst(void); -void CmndAltitude(void); -void CmndLedPower(void); -void CmndLedState(void); -void CmndLedMask(void); -void CmndWifiPower(void); -void CmndI2cScan(void); -void CmndI2cDriver(void); -void CmndSensor(void); -void CmndDriver(void); -void CmndCrash(void); -void CmndWDT(void); -void CmndBlockedLoop(void); -void CrashDumpClear(void); -bool CrashFlag(void); -void CrashDump(void); -static bool spiflash_is_ready(void); -static void spi_write_enable(void); -bool EsptoolEraseSector(uint32_t sector); -void EsptoolErase(uint32_t start_sector, uint32_t end_sector); -void GetFeatures(void); -float fmodf(float x, float y); -double FastPrecisePow(double a, double b); -float FastPrecisePowf(const float x, const float y); -double TaylorLog(double x); -inline float sinf(float x); -inline float cosf(float x); -inline float tanf(float x); -inline float atanf(float x); -inline float asinf(float x); -inline float acosf(float x); -inline float sqrtf(float x); -inline float powf(float x, float y); -float cos_52s(float x); -float cos_52(float x); -float sin_52(float x); -float tan_56s(float x); -float tan_56(float x); -float atan_66s(float x); -float atan_66(float x); -float asinf1(float x); -float acosf1(float x); -float sqrt1(const float x); -uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max, - uint16_t ito_min, uint16_t ito_max); -void* memchr(const void* ptr, int value, size_t num); -size_t strcspn(const char *str1, const char *str2); -char* strpbrk(const char *s1, const char *s2); -void* memmove_P(void *dest, const void *src, size_t n); -void update_rotary(void); -bool RotaryButtonPressed(void); -void RotaryInit(void); -void RotaryHandler(void); -void RotaryLoop(void); -uint32_t UtcTime(void); -uint32_t LocalTime(void); -int32_t DriftTime(void); -uint32_t Midnight(void); -bool MidnightNow(void); -bool IsDst(void); -String GetBuildDateAndTime(void); -String GetMinuteTime(uint32_t minutes); -String GetTimeZone(void); -String GetDuration(uint32_t time); -String GetDT(uint32_t time); -String GetDateAndTime(uint8_t time_type); -String GetTime(int type); -uint32_t UpTime(void); -uint32_t MinutesUptime(void); -String GetUptime(void); -uint32_t MinutesPastMidnight(void); -void BreakTime(uint32_t time_input, TIME_T &tm); -uint32_t MakeTime(TIME_T &tm); -uint32_t RuleToTime(TimeRule r, int yr); -void RtcSecond(void); -void RtcSetTime(uint32_t epoch); -void RtcInit(void); -String GetStatistics(void); -String GetStatistics(void); -void SwitchPullupFlag(uint16 switch_bit); -void SwitchSetVirtual(uint32_t index, uint8_t state); -uint8_t SwitchGetVirtual(uint32_t index); -uint8_t SwitchLastState(uint32_t index); -bool SwitchState(uint32_t index); -void SwitchProbe(void); -void SwitchInit(void); -void SwitchHandler(uint8_t mode); -void SwitchLoop(void); -char* Format(char* output, const char* input, int size); -char* GetOtaUrl(char *otaurl, size_t otaurl_size); -char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopic); -char* GetGroupTopic_P(char *stopic, const char* subtopic); -char* GetFallbackTopic_P(char *stopic, const char* subtopic); -char* GetStateText(uint32_t state); -void SetLatchingRelay(power_t lpower, uint32_t state); -void SetDevicePower(power_t rpower, uint32_t source); -void RestorePower(bool publish_power, uint32_t source); -void SetAllPower(uint32_t state, uint32_t source); -void SetPowerOnState(void); -void SetLedPowerIdx(uint32_t led, uint32_t state); -void SetLedPower(uint32_t state); -void SetLedPowerAll(uint32_t state); -void SetLedLink(uint32_t state); -void SetPulseTimer(uint32_t index, uint32_t time); -uint32_t GetPulseTimer(uint32_t index); -bool SendKey(uint32_t key, uint32_t device, uint32_t state); -void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source); -void StopAllPowerBlink(void); -void MqttShowPWMState(void); -void MqttShowState(void); -void MqttPublishTeleState(void); -bool MqttShowSensor(void); -void MqttPublishSensor(void); -void PerformEverySecond(void); -void Every100mSeconds(void); -void Every250mSeconds(void); -void ArduinoOTAInit(void); -void ArduinoOtaLoop(void); -void SerialInput(void); -void GpioInit(void); -bool UdpDisconnect(void); -bool UdpConnect(void); -void PollUdp(void); -int WifiGetRssiAsQuality(int rssi); -bool WifiConfigCounter(void); -void WifiConfig(uint8_t type); -void WifiSetMode(WiFiMode_t wifi_mode); -void WiFiSetSleepMode(void); -void WifiBegin(uint8_t flag, uint8_t channel); -void WifiBeginAfterScan(void); -uint16_t WifiLinkCount(void); -String WifiDowntime(void); -void WifiSetState(uint8_t state); -bool WifiCheckIPv6(void); -String WifiGetIPv6(void); -bool WifiCheckIPAddrStatus(void); -void WifiCheckIp(void); -void WifiCheck(uint8_t param); -int WifiState(void); -String WifiGetOutputPower(void); -void WifiSetOutputPower(void); -void WifiConnect(void); -void WifiDisconnect(void); -void WifiShutdown(void); -void EspRestart(void); -static void WebGetArg(const char* arg, char* out, size_t max); -static bool WifiIsInManagerMode(); -void ShowWebSource(uint32_t source); -void ExecuteWebCommand(char* svalue, uint32_t source); -void StartWebserver(int type, IPAddress ipweb); -void StopWebserver(void); -void WifiManagerBegin(bool reset_only); -void PollDnsWebserver(void); -bool WebAuthenticate(void); -void HttpHeaderCors(void); -void WSHeaderSend(void); -void WSSend(int code, int ctype, const String& content); -void WSContentBegin(int code, int ctype); -void _WSContentSend(const String& content); -void WSContentFlush(void); -void _WSContentSendBuffer(void); -void WSContentSend_P(const char* formatP, ...); -void WSContentSend_PD(const char* formatP, ...); -void WSContentStart_P(const char* title, bool auth); -void WSContentStart_P(const char* title); -void WSContentSendStyle_P(const char* formatP, ...); -void WSContentSendStyle(void); -void WSContentButton(uint32_t title_index); -void WSContentSpaceButton(uint32_t title_index); -void WSContentEnd(void); -void WSContentStop(void); -void WebRestart(uint32_t type); -void HandleWifiLogin(void); -void WebSliderColdWarm(void); -void HandleRoot(void); -bool HandleRootStatusRefresh(void); -int32_t IsShutterWebButton(uint32_t idx); -void HandleConfiguration(void); -void HandleTemplateConfiguration(void); -void TemplateSaveSettings(void); -void HandleModuleConfiguration(void); -void ModuleSaveSettings(void); -String HtmlEscape(const String unescaped); -void HandleWifiConfiguration(void); -void WifiSaveSettings(void); -void HandleLoggingConfiguration(void); -void LoggingSaveSettings(void); -void HandleOtherConfiguration(void); -void OtherSaveSettings(void); -void HandleBackupConfiguration(void); -void HandleResetConfiguration(void); -void HandleRestoreConfiguration(void); -void HandleInformation(void); -void HandleUpgradeFirmware(void); -void HandleUpgradeFirmwareStart(void); -void HandleUploadDone(void); -void HandleUploadLoop(void); -void HandlePreflightRequest(void); -void HandleHttpCommand(void); -void HandleConsole(void); -void HandleConsoleRefresh(void); -void HandleNotFound(void); -bool CaptivePortal(void); -String UrlEncode(const String& text); -int WebSend(char *buffer); -bool JsonWebColor(const char* dataBuf); -void CmndEmulation(void); -void CmndSendmail(void); -void CmndWebServer(void); -void CmndWebPassword(void); -void CmndWeblog(void); -void CmndWebRefresh(void); -void CmndWebSend(void); -void CmndWebColor(void); -void CmndWebSensor(void); -void CmndWebButton(void); -void CmndCors(void); -bool Xdrv01(uint8_t function); -bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value); -void MakeValidMqtt(uint32_t option, char* str); -void MqttDiscoverServer(void); -void MqttInit(void); -bool MqttIsConnected(void); -void MqttDisconnect(void); -void MqttSubscribeLib(const char *topic); -void MqttUnsubscribeLib(const char *topic); -bool MqttPublishLib(const char* topic, bool retained); -void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len); -void MqttRetryCounter(uint8_t value); -void MqttSubscribe(const char *topic); -void MqttUnsubscribe(const char *topic); -void MqttPublishLogging(const char *mxtime); -void MqttPublish(const char* topic, bool retained); -void MqttPublish(const char* topic); -void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retained); -void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic); -void MqttPublishTeleSensor(void); -void MqttPublishPowerState(uint32_t device); -void MqttPublishAllPowerState(void); -void MqttPublishPowerBlinkState(uint32_t device); -uint16_t MqttConnectCount(void); -void MqttDisconnected(int state); -void MqttConnected(void); -void MqttReconnect(void); -void MqttCheck(void); -void CmndMqttFingerprint(void); -void CmndMqttUser(void); -void CmndMqttPassword(void); -void CmndMqttlog(void); -void CmndMqttHost(void); -void CmndMqttPort(void); -void CmndMqttRetry(void); -void CmndStateText(void); -void CmndMqttClient(void); -void CmndFullTopic(void); -void CmndPrefix(void); -void CmndPublish(void); -void CmndGroupTopic(void); -void CmndTopic(void); -void CmndButtonTopic(void); -void CmndSwitchTopic(void); -void CmndButtonRetain(void); -void CmndSwitchRetain(void); -void CmndPowerRetain(void); -void CmndSensorRetain(void); -inline void TlsEraseBuffer(uint8_t *buffer); -void loadTlsDir(void); -void CmndTlsKey(void); -uint32_t bswap32(uint32_t x); -void CmndTlsDump(void); -void HandleMqttConfiguration(void); -void MqttSaveSettings(void); -bool Xdrv02(uint8_t function); -bool EnergyTariff1Active(); -void EnergyUpdateToday(void); -void EnergyUpdateTotal(float value, bool kwh); -void Energy200ms(void); -void EnergySaveState(void); -bool EnergyMargin(bool type, uint16_t margin, uint16_t value, bool &flag, bool &save_flag); -void EnergyMarginCheck(void); -void EnergyMqttShow(void); -void EnergyEverySecond(void); -void EnergyCommandCalResponse(uint32_t nvalue); -void CmndEnergyReset(void); -void CmndTariff(void); -void CmndPowerCal(void); -void CmndVoltageCal(void); -void CmndCurrentCal(void); -void CmndPowerSet(void); -void CmndVoltageSet(void); -void CmndCurrentSet(void); -void CmndFrequencySet(void); -void CmndModuleAddress(void); -void CmndPowerDelta(void); -void CmndPowerLow(void); -void CmndPowerHigh(void); -void CmndVoltageLow(void); -void CmndVoltageHigh(void); -void CmndCurrentLow(void); -void CmndCurrentHigh(void); -void CmndMaxPower(void); -void CmndMaxPowerHold(void); -void CmndMaxPowerWindow(void); -void CmndSafePower(void); -void CmndSafePowerHold(void); -void CmndSafePowerWindow(void); -void CmndMaxEnergy(void); -void CmndMaxEnergyStart(void); -void EnergySnsInit(void); -void EnergyShow(bool json); -bool Xdrv03(uint8_t function); -bool Xsns03(uint8_t function); -power_t LightPower(void); -power_t LightPowerIRAM(void); -uint8_t LightDevice(void); -static uint32_t min3(uint32_t a, uint32_t b, uint32_t c); -uint16_t change8to10(uint8_t v); -uint8_t change10to8(uint16_t v); -uint16_t ledGamma_internal(uint16_t v, const struct gamma_table_t *gt_ptr); -uint16_t ledGammaReverse_internal(uint16_t vg, const struct gamma_table_t *gt_ptr); -uint16_t ledGamma10_10(uint16_t v); -uint16_t ledGamma10(uint8_t v); -uint8_t ledGamma(uint8_t v); -void LightPwmOffset(uint32_t offset); -bool LightModuleInit(void); -void LightInit(void); -void LightUpdateColorMapping(void); -uint8_t LightGetDimmer(uint8_t dimmer); -void LightSetDimmer(uint8_t dimmer); -void LightGetHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri); -void LightHsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b); -uint8_t LightGetBri(uint8_t device); -void LightSetBri(uint8_t device, uint8_t bri); -void LightSetColorTemp(uint16_t ct); -uint16_t LightGetColorTemp(void); -void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value); -void LightPowerOn(void); -void LightState(uint8_t append); -void LightCycleColor(int8_t direction); -void LightSetPower(void); -void LightAnimate(void); -bool isChannelGammaCorrected(uint32_t channel); -uint16_t fadeGamma(uint32_t channel, uint16_t v); -uint16_t fadeGammaReverse(uint32_t channel, uint16_t vg); -bool LightApplyFade(void); -void LightApplyPower(uint8_t new_color[LST_MAX], power_t power); -void LightSetOutputs(const uint16_t *cur_col_10); -void calcGammaMultiChannels(uint16_t cur_col_10[5]); -void calcGammaBulbs(uint16_t cur_col_10[5]); -bool LightColorEntry(char *buffer, uint32_t buffer_length); -void CmndSupportColor(void); -void CmndColor(void); -void CmndWhite(void); -void CmndChannel(void); -void CmndHsbColor(void); -void CmndScheme(void); -void CmndWakeup(void); -void CmndColorTemperature(void); -void CmndDimmer(void); -void CmndDimmerRange(void); -void CmndLedTable(void); -void CmndRgbwwTable(void); -void CmndFade(void); -void CmndSpeed(void); -void CmndWakeupDuration(void); -void CmndUndocA(void); -bool Xdrv04(uint8_t function); -void IrSendInit(void); -void IrReceiveUpdateThreshold(void); -void IrReceiveInit(void); -void IrReceiveCheck(void); -uint32_t IrRemoteCmndIrSendJson(void); -void CmndIrSend(void); -void IrRemoteCmndResponse(uint32_t error); -bool Xdrv05(uint8_t function); -void IrSendInit(void); -uint8_t reverseBitsInByte(uint8_t b); -uint64_t reverseBitsInBytes64(uint64_t b); -void IrReceiveUpdateThreshold(void); -void IrReceiveInit(void); -String sendIRJsonState(const struct decode_results &results); -void IrReceiveCheck(void); -String listSupportedProtocols(bool hvac); -uint32_t IrRemoteCmndIrHvacJson(void); -void CmndIrHvac(void); -uint32_t IrRemoteCmndIrSendJson(void); -uint32_t IrRemoteCmndIrSendRaw(void); -void CmndIrSend(void); -void IrRemoteCmndResponse(uint32_t error); -bool Xdrv05(uint8_t function); -ssize_t rf_find_hex_record_start(uint8_t *buf, size_t size); -ssize_t rf_find_hex_record_end(uint8_t *buf, size_t size); -ssize_t rf_glue_remnant_with_new_data_and_write(const uint8_t *remnant_data, uint8_t *new_data, size_t new_data_len); -ssize_t rf_decode_and_write(uint8_t *record, size_t size); -ssize_t rf_search_and_write(uint8_t *buf, size_t size); -uint8_t rf_erase_flash(void); -uint8_t SnfBrUpdateInit(void); -void SonoffBridgeReceivedRaw(void); -void SonoffBridgeLearnFailed(void); -void SonoffBridgeReceived(void); -bool SonoffBridgeSerialInput(void); -void SonoffBridgeSendCommand(uint8_t code); -void SonoffBridgeSendAck(void); -void SonoffBridgeSendCode(uint32_t code); -void SonoffBridgeSend(uint8_t idx, uint8_t key); -void SonoffBridgeLearn(uint8_t key); -void CmndRfBridge(void); -void CmndRfKey(void); -void CmndRfRaw(void); -bool Xdrv06(uint8_t function); -int DomoticzBatteryQuality(void); -int DomoticzRssiQuality(void); -void MqttPublishDomoticzFanState(void); -void DomoticzUpdateFanState(void); -void MqttPublishDomoticzPowerState(uint8_t device); -void DomoticzUpdatePowerState(uint8_t device); -void DomoticzMqttUpdate(void); -void DomoticzMqttSubscribe(void); -bool DomoticzMqttData(void); -bool DomoticzSendKey(uint8_t key, uint8_t device, uint8_t state, uint8_t svalflg); -uint8_t DomoticzHumidityState(char *hum); -void DomoticzSensor(uint8_t idx, char *data); -void DomoticzSensor(uint8_t idx, uint32_t value); -void DomoticzTempHumSensor(char *temp, char *hum); -void DomoticzTempHumPressureSensor(char *temp, char *hum, char *baro); -void DomoticzSensorPowerEnergy(int power, char *energy); -void DomoticzSensorP1SmartMeter(char *usage1, char *usage2, char *return1, char *return2, int power); -void CmndDomoticzIdx(void); -void CmndDomoticzKeyIdx(void); -void CmndDomoticzSwitchIdx(void); -void CmndDomoticzSensorIdx(void); -void CmndDomoticzUpdateTimer(void); -void HandleDomoticzConfiguration(void); -void DomoticzSaveSettings(void); -bool Xdrv07(uint8_t function); -void SerialBridgeInput(void); -void SerialBridgeInit(void); -void CmndSSerialSend(void); -void CmndSBaudrate(void); -bool Xdrv08(uint8_t function); -float JulianischesDatum(void); -float InPi(float x); -float eps(float T); -float BerechneZeitgleichung(float *DK,float T); -void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8_t *minute_down); -void ApplyTimerOffsets(Timer *duskdawn); -String GetSun(uint32_t dawn); -uint16_t SunMinutes(uint32_t dawn); -void TimerSetRandomWindow(uint32_t index); -void TimerSetRandomWindows(void); -void TimerEverySecond(void); -void PrepShowTimer(uint32_t index); -void CmndTimer(void); -void CmndTimers(void); -void CmndLongitude(void); -void CmndLatitude(void); -void HandleTimerConfiguration(void); -void TimerSaveSettings(void); -bool Xdrv09(uint8_t function); -bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule); -int8_t parseCompareExpression(String &expr, String &leftExpr, String &rightExpr); -void RulesVarReplace(String &commands, const String &sfind, const String &replace); -bool RuleSetProcess(uint8_t rule_set, String &event_saved); -bool RulesProcessEvent(char *json_event); -bool RulesProcess(void); -void RulesInit(void); -void RulesEvery50ms(void); -void RulesEvery100ms(void); -void RulesEverySecond(void); -void RulesSaveBeforeRestart(void); -void RulesSetPower(void); -void RulesTeleperiod(void); -bool RulesMqttData(void); -void CmndSubscribe(void); -void CmndUnsubscribe(void); -bool findNextNumber(char * &pNumber, float &value); -bool findNextVariableValue(char * &pVarname, float &value); -bool findNextObjectValue(char * &pointer, float &value); -bool findNextOperator(char * &pointer, int8_t &op); -float calculateTwoValues(float v1, float v2, uint8_t op); -float evaluateExpression(const char * expression, unsigned int len); -void CmndIf(void); -bool evaluateComparisonExpression(const char *expression, int len); -bool findNextLogicOperator(char * &pointer, int8_t &op); -bool findNextLogicObjectValue(char * &pointer, bool &value); -bool evaluateLogicalExpression(const char * expression, int len); -int8_t findIfBlock(char * &pointer, int &lenWord, int8_t block_type); -void ExecuteCommandBlock(const char * commands, int len); -void ProcessIfStatement(const char* statements); -void RulesPreprocessCommand(char *pCommands); -void CmndRule(void); -void CmndRuleTimer(void); -void CmndEvent(void); -void CmndVariable(void); -void CmndMemory(void); -void CmndCalcResolution(void); -void CmndAddition(void); -void CmndSubtract(void); -void CmndMultiply(void); -void CmndScale(void); -float map_double(float x, float in_min, float in_max, float out_min, float out_max); -bool Xdrv10(uint8_t function); -void ScriptEverySecond(void); -void RulesTeleperiod(void); -int16_t Init_Scripter(void); -void ws2812_set_array(float *array ,uint8_t len); -float median_array(float *array,uint8_t len); -float Get_MFVal(uint8_t index,uint8_t bind); -void Set_MFVal(uint8_t index,uint8_t bind,float val); -float Get_MFilter(uint8_t index); -void Set_MFilter(uint8_t index, float invar); -float DoMedian5(uint8_t index, float in); -uint32_t HSVToRGB(uint16_t hue, uint8_t saturation, uint8_t value); -uint16_t GetStack(void); -uint16_t GetStack(void); -void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize); -void toLog(const char *str); -void toLogN(const char *cp,uint8_t len); -void toLogEOL(const char *s1,const char *str); -void toSLog(const char *str); -int16_t Run_Scripter(const char *type, int8_t tlen, char *js); -void ScripterEvery100ms(void); -void Scripter_save_pvars(void); -void ListDir(char *path, uint8_t depth); -void Script_FileUploadConfiguration(void); -void ScriptFileUploadSuccess(void); -void script_upload(void); -uint8_t DownloadFile(char *file); -void HandleScriptTextareaConfiguration(void); -void HandleScriptConfiguration(void); -void ScriptSaveSettings(void); -void Script_HueStatus(String *response, uint16_t hue_devs); -void Script_Check_Hue(String *response); -void Script_Handle_Hue(String *path); -bool Script_SubCmd(void); -void execute_script(char *script); -bool ScriptCommand(void); -uint16_t xFAT_DATE(uint16_t year, uint8_t month, uint8_t day); -uint16_t xFAT_TIME(uint8_t hour, uint8_t minute, uint8_t second); -void dateTime(uint16_t* date, uint16_t* time); -bool ScriptMqttData(void); -String ScriptSubscribe(const char *data, int data_len); -String ScriptUnsubscribe(const char * data, int data_len); -void Script_Check_HTML_Setvars(void); -void ScriptGetVarname(char *nbuf,char *sp, uint32_t blen); -void ScriptWebShow(void); -void ScriptJsonAppend(void); -bool Xdrv10(uint8_t function); -void KNX_ADD_GA( uint8_t GAop, uint8_t GA_FNUM, uint8_t GA_AREA, uint8_t GA_FDEF ); -void KNX_DEL_GA( uint8_t GAnum ); -void KNX_ADD_CB( uint8_t CBop, uint8_t CB_FNUM, uint8_t CB_AREA, uint8_t CB_FDEF ); -void KNX_DEL_CB( uint8_t CBnum ); -bool KNX_CONFIG_NOT_MATCH(void); -void KNXStart(void); -void KNX_INIT(void); -void KNX_CB_Action(message_t const &msg, void *arg); -void KnxUpdatePowerState(uint8_t device, power_t state); -void KnxSendButtonPower(void); -void KnxSensor(uint8_t sensor_type, float value); -void HandleKNXConfiguration(void); -void KNX_Save_Settings(void); -void CmndKnxTxCmnd(void); -void CmndKnxTxVal(void); -void CmndKnxEnabled(void); -void CmndKnxEnhanced(void); -void CmndKnxPa(void); -void CmndKnxGa(void); -void CmndKnxCb(void); -bool Xdrv11(uint8_t function); -void TryResponseAppend_P(const char *format, ...); -void HAssAnnounceRelayLight(void); -void HAssAnnounceButtonSwitch(uint8_t device, char *topic, uint8_t present, uint8_t key, uint8_t toggle); -void HAssAnnounceSwitches(void); -void HAssAnnounceButtons(void); -void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char *MultiSubName, uint8_t subqty, uint8_t subidx); -void HAssAnnounceSensors(void); -void HAssAnnounceStatusSensor(void); -void HAssPublishStatus(void); -void HAssDiscovery(void); -void HAssDiscover(void); -void HAssAnyKey(void); -bool Xdrv12(uint8_t function); -void DisplayInit(uint8_t mode); -void DisplayClear(void); -void DisplayDrawHLine(uint16_t x, uint16_t y, int16_t len, uint16_t color); -void DisplayDrawVLine(uint16_t x, uint16_t y, int16_t len, uint16_t color); -void DisplayDrawLine(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color); -void DisplayDrawCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color); -void DisplayDrawFilledCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color); -void DisplayDrawRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color); -void DisplayDrawFilledRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color); -void DisplayDrawFrame(void); -void DisplaySetSize(uint8_t size); -void DisplaySetFont(uint8_t font); -void DisplaySetRotation(uint8_t rotation); -void DisplayDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); -void DisplayOnOff(uint8_t on); -uint8_t fatoiv(char *cp,float *res); -uint8_t atoiv(char *cp, int16_t *res); -uint8_t atoiV(char *cp, uint16_t *res); -void alignright(char *string); -uint32_t decode_te(char *line); -void DisplayText(void); -void DisplayClearScreenBuffer(void); -void DisplayFreeScreenBuffer(void); -void DisplayAllocScreenBuffer(void); -void DisplayReAllocScreenBuffer(void); -void DisplayFillScreen(uint32_t line); -void DisplayClearLogBuffer(void); -void DisplayFreeLogBuffer(void); -void DisplayAllocLogBuffer(void); -void DisplayReAllocLogBuffer(void); -void DisplayLogBufferAdd(char* txt); -char* DisplayLogBuffer(char temp_code); -void DisplayLogBufferInit(void); -void DisplayJsonValue(const char* topic, const char* device, const char* mkey, const char* value); -void DisplayAnalyzeJson(char *topic, char *json); -void DisplayMqttSubscribe(void); -bool DisplayMqttData(void); -void DisplayLocalSensor(void); -void DisplayInitDriver(void); -void DisplaySetPower(void); -void CmndDisplay(void); -void CmndDisplayModel(void); -void CmndDisplayWidth(void); -void CmndDisplayHeight(void); -void CmndDisplayMode(void); -void CmndDisplayDimmer(void); -void CmndDisplaySize(void); -void CmndDisplayFont(void); -void CmndDisplayRotate(void); -void CmndDisplayText(void); -void CmndDisplayAddress(void); -void CmndDisplayRefresh(void); -void CmndDisplayColumns(void); -void CmndDisplayRows(void); -void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp); -void DrawAClock(uint16_t rad); -void ClrGraph(uint16_t num); -void DefineGraph(uint16_t num,uint16_t xp,uint16_t yp,int16_t xs,uint16_t ys,int16_t dec,float ymin, float ymax,uint8_t icol); -void DisplayCheckGraph(); -void Save_graph(uint8_t num, char *path); -void Restore_graph(uint8_t num, char *path); -void RedrawGraph(uint8_t num, uint8_t flags); -void AddGraph(uint8_t num,uint8_t val); -void AddValue(uint8_t num,float fval); -bool Xdrv13(uint8_t function); -uint16_t MP3_Checksum(uint8_t *array); -void MP3PlayerInit(void); -void MP3_CMD(uint8_t mp3cmd,uint16_t val); -bool MP3PlayerCmd(void); -bool Xdrv14(uint8_t function); -void PCA9685_Detect(void); -void PCA9685_Reset(void); -void PCA9685_SetPWMfreq(double freq); -void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off); -void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted); -bool PCA9685_Command(void); -void PCA9685_OutputTelemetry(bool telemetry); -bool Xdrv15(uint8_t function); -void CmndTuyaSend(void); -void CmndTuyaMcu(void); -void TuyaAddMcuFunc(uint8_t fnId, uint8_t dpId); -void UpdateDevices(); -inline bool TuyaFuncIdValid(uint8_t fnId); -uint8_t TuyaGetFuncId(uint8_t dpid); -uint8_t TuyaGetDpId(uint8_t fnId); -void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value); -void TuyaSendBool(uint8_t id, bool value); -void TuyaSendValue(uint8_t id, uint32_t value); -void TuyaSendEnum(uint8_t id, uint32_t value); -void TuyaSendString(uint8_t id, char data[]); -bool TuyaSetPower(void); -bool TuyaSetChannels(void); -void LightSerialDuty(uint16_t duty); -void TuyaRequestState(void); -void TuyaResetWifi(void); -void TuyaProcessStatePacket(void); -void TuyaLowPowerModePacketProcess(void); -void TuyaHandleProductInfoPacket(void); -void TuyaSendLowPowerSuccessIfNeeded(void); -void TuyaNormalPowerModePacketProcess(void); -bool TuyaModuleSelected(void); -void TuyaInit(void); -void TuyaSerialInput(void); -bool TuyaButtonPressed(void); -uint8_t TuyaGetTuyaWifiState(void); -void TuyaSetWifiLed(void); -bool Xnrg16(uint8_t function); -bool Xdrv16(uint8_t function); -void RfReceiveCheck(void); -void RfInit(void); -void CmndRfSend(void); -bool Xdrv17(uint8_t function); -bool ArmtronixSetChannels(void); -void LightSerial2Duty(uint8_t duty1, uint8_t duty2); -void ArmtronixRequestState(void); -bool ArmtronixModuleSelected(void); -void ArmtronixInit(void); -void ArmtronixSerialInput(void); -void ArmtronixSetWifiLed(void); -bool Xdrv18(uint8_t function); -void PS16DZSerialSend(const char *tx_buffer); -void PS16DZSerialSendOk(void); -void PS16DZSerialSendUpdateCommand(void); -void PS16DZSerialInput(void); -bool PS16DZSerialSendUpdateCommandIfRequired(void); -void PS16DZInit(void); -bool PS16DZModuleSelected(void); -bool Xdrv19(uint8_t function); -String HueBridgeId(void); -String HueSerialnumber(void); -String HueUuid(void); -void HueRespondToMSearch(void); -String GetHueDeviceId(uint8_t id); -String GetHueUserId(void); -void HandleUpnpSetupHue(void); -void HueNotImplemented(String *path); -void HueConfigResponse(String *response); -void HueConfig(String *path); -uint8_t getLocalLightSubtype(uint8_t device); -void HueLightStatus1(uint8_t device, String *response); -bool HueActive(uint8_t device); -void HueLightStatus2(uint8_t device, String *response); -uint32_t EncodeLightId(uint8_t relay_id); -uint32_t DecodeLightId(uint32_t hue_id); -uint32_t findEchoGeneration(void); -void HueGlobalConfig(String *path); -void HueAuthentication(String *path); -void HueLights(String *path); -void HueGroups(String *path); -void HandleHueApi(String *path); -bool Xdrv20(uint8_t function); -String WemoSerialnumber(void); -String WemoUuid(void); -void WemoRespondToMSearch(int echo_type); -void HandleUpnpEvent(void); -void HandleUpnpService(void); -void HandleUpnpMetaService(void); -void HandleUpnpSetupWemo(void); -bool Xdrv21(uint8_t function); -bool IsModuleIfan(void); -uint8_t MaxFanspeed(void); -uint8_t GetFanspeed(void); -void SonoffIFanSetFanspeed(uint8_t fanspeed, bool sequence); -void SonoffIfanReceived(void); -bool SonoffIfanSerialInput(void); -void CmndFanspeed(void); -bool SonoffIfanInit(void); -void SonoffIfanUpdate(void); -bool Xdrv22(uint8_t function); -void CopyJsonVariant(JsonObject &to, const String &key, const JsonVariant &val); -void CopyJsonArray(JsonArray &to, const JsonArray &arr); -void CopyJsonObject(JsonObject &to, const JsonObject &from); -uint16_t fromClusterCode(uint8_t c); -uint8_t toClusterCode(uint16_t c); -class SBuffer hibernateDevice(const struct Z_Device &device); -class SBuffer hibernateDevices(void); -void hidrateDevices(const SBuffer &buf); -void loadZigbeeDevices(void); -void saveZigbeeDevices(void); -void eraseZigbeeDevices(void); -uint8_t toPercentageCR2032(uint32_t voltage); -uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer &buf, - uint32_t offset, uint32_t len); -int32_t Z_ManufKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_ModelKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_Remove(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_Copy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_AddPressureUnit(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_FloatDiv100(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value); -int32_t Z_AqaraCube(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value); -void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint); -const __FlashStringHelper* zigbeeFindCommand(const char *command); -inline bool isXYZ(char c); -inline char hexDigit(uint32_t h); -String zigbeeCmdAddParams(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t z); -uint32_t ZigbeeAliasOrNumber(const char *state_text); -uint8_t ZigbeeGetInstructionSize(uint8_t instr); -void ZigbeeGotoLabel(uint8_t label); -void ZigbeeStateMachine_Run(void); -int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf); -int32_t Z_CheckNVWrite(int32_t res, class SBuffer &buf); -int32_t Z_Reboot(int32_t res, class SBuffer &buf); -int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf); -bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match); -int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf); -void Z_SendActiveEpReq(uint16_t shortaddr); -void Z_SendSimpleDescReq(uint16_t shortaddr, uint8_t endpoint); -int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf); -int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf); -void Z_SendAFInfoRequest(uint16_t shortaddr, uint8_t endpoint, uint16_t clusterid, uint8_t transacid); -int32_t Z_ReceiveSimpleDesc(int32_t res, const class SBuffer &buf); -int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf); -int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf); -void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, const JsonObject *json); -int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value); -int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf); -int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf); -int32_t Z_Load_Devices(uint8_t value); -int32_t Z_State_Ready(uint8_t value); -int32_t ZigbeeProcessInput(class SBuffer &buf); -void ZigbeeInput(void); -void ZigbeeInit(void); -uint32_t strToUInt(const JsonVariant &val); -void CmndZbReset(void); -void CmndZbZNPSendOrReceive(bool send); -void CmndZbZNPReceive(void); -void CmndZbZNPSend(void); -void ZigbeeZNPSend(const uint8_t *msg, size_t len); -void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp, uint8_t transacId); -inline int8_t hexValue(char c); -void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data); -void CmndZbSend(void); -void CmndZbBind(void); -void CmndZbProbe(void); -void CmndZbName(void); -void CmndZbForget(void); -void CmndZbSave(void); -void CmndZbRead(void); -void CmndZbPermitJoin(void); -void CmndZbStatus(void); -bool Xdrv23(uint8_t function); -void BuzzerOff(void); -void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32_t mode); -void BuzzerSetStateToLed(uint32_t state); -void BuzzerBeep(uint32_t count); -void BuzzerEnabledBeep(uint32_t count, uint32_t duration); -bool BuzzerPinState(void); -void BuzzerInit(void); -void BuzzerEvery100mSec(void); -void CmndBuzzer(void); -bool Xdrv24(uint8_t function); -void A4988Init(void); -void CmndDoMove(void); -void CmndDoRotate(void); -void CmndDoTurn(void); -void CmndSetMIS(void); -void CmndSetSPR(void); -void CmndSetRPM(void); -bool Xdrv25(uint8_t function); -void AriluxRfInterrupt(void); -void AriluxRfHandler(void); -void AriluxRfInit(void); -void AriluxRfDisable(void); -bool Xdrv26(uint8_t function); -void ShutterLogPos(uint32_t i); -void ShutterRtc50mS(void); -int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index); -uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index); -void ShutterInit(void); -void ShutterReportPosition(bool always); -void ShutterLimitRealAndTargetPositions(uint32_t i); -void ShutterUpdatePosition(void); -bool ShutterState(uint32_t device); -void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos); -void ShutterWaitForMotorStop(uint32_t i); -int32_t ShutterCounterBasedPosition(uint32_t i); -void ShutterRelayChanged(void); -bool ShutterButtonIsSimultaneousHold(uint32_t button_index, uint32_t shutter_index); -void ShutterButtonHandler(void); -void ShutterSetPosition(uint32_t device, uint32_t position); -void CmndShutterOpen(void); -void CmndShutterClose(void); -void CmndShutterStop(void); -void CmndShutterPosition(void); -void CmndShutterOpenTime(void); -void CmndShutterCloseTime(void); -void CmndShutterMotorDelay(void); -void CmndShutterRelay(void); -void CmndShutterButton(void); -void CmndShutterSetHalfway(void); -void CmndShutterFrequency(void); -void CmndShutterSetClose(void); -void CmndShutterInvert(void); -void CmndShutterCalibration(void); -void CmndShutterLock(void); -void CmndShutterEnableEndStopTime(void); -bool Xdrv27(uint8_t function); -void Pcf8574SwitchRelay(void); -void Pcf8574Init(void); -void HandlePcf8574(void); -void Pcf8574SaveSettings(void); -bool Xdrv28(uint8_t function); -bool DeepSleepEnabled(void); -void DeepSleepReInit(void); -void DeepSleepPrepare(void); -void DeepSleepStart(void); -void DeepSleepEverySecond(void); -void CmndDeepsleepTime(void); -bool Xdrv29(uint8_t function); -uint8_t crc8(const uint8_t *p, uint8_t len); -void ExsSendCmd(uint8_t cmd, uint8_t value); -uint8_t ExsSetPower(uint8_t device, uint8_t power); -uint8_t ExsSetBri(uint8_t device, uint8_t bri); -uint8_t ExsSyncState(uint8_t device); -bool ExsSyncState(); -void ExsDebugState(); -void ExsPacketProcess(void); -bool ExsModuleSelected(void); -bool ExsSetChannels(void); -bool ExsSetPower(void); -void EsxMcuStart(void); -void ExsInit(void); -void ExsSerialInput(void); -void CmndExsDimm(void); -void CmndExsDimmTbl(void); -void CmndExsDimmVal(void); -void CmndExsDimms(void); -void CmndExsChLock(void); -void CmndExsState(void); -bool Xdrv30(uint8_t function); -uint32_t TasmotaSlave_FlashStart(void); -uint8_t TasmotaSlave_UpdateInit(void); -void TasmotaSlave_Reset(void); -uint8_t TasmotaSlave_waitForSerialData(int dataCount, int timeout); -uint8_t TasmotaSlave_sendBytes(uint8_t* bytes, int count); -uint8_t TasmotaSlave_execCmd(uint8_t cmd); -uint8_t TasmotaSlave_execParam(uint8_t cmd, uint8_t* params, int count); -uint8_t TasmotaSlave_exitProgMode(void); -uint8_t TasmotaSlave_SetupFlash(void); -uint8_t TasmotaSlave_loadAddress(uint8_t adrHi, uint8_t adrLo); -void TasmotaSlave_FlashPage(uint8_t addr_h, uint8_t addr_l, uint8_t* data); -void TasmotaSlave_Flash(void); -void TasmotaSlave_SetFlagFlashing(bool value); -bool TasmotaSlave_GetFlagFlashing(void); -void TasmotaSlave_WriteBuffer(uint8_t *buf, size_t size); -void TasmotaSlave_Init(void); -void TasmotaSlave_Show(void); -void TasmotaSlave_sendCmnd(uint8_t cmnd, uint8_t param); -void CmndTasmotaSlaveReset(void); -void CmndTasmotaSlaveSend(void); -void TasmotaSlave_ProcessIn(void); -bool Xdrv31(uint8_t function); -void HotPlugInit(void); -void HotPlugEverySecond(void); -void CmndHotPlugTime(void); -bool Xdrv32(uint8_t function); -bool NRF24initRadio(); -bool NRF24Detect(void); -bool Xdrv33(uint8_t function); -void ExceptionTest(uint8_t type); -void CpuLoadLoop(void); -void DebugFreeMem(void); -void DebugFreeMem(void); -void DebugRtcDump(char* parms); -void DebugCfgDump(char* parms); -void DebugCfgPeek(char* parms); -void DebugCfgPoke(char* parms); -void SetFlashMode(uint8_t mode); -void CmndHelp(void); -void CmndRtcDump(void); -void CmndCfgDump(void); -void CmndCfgPeek(void); -void CmndCfgPoke(void); -void CmndCfgXor(void); -void CmndException(void); -void CmndCpuCheck(void); -void CmndFreemem(void); -void CmndSetSensor(void); -void CmndFlashMode(void); -uint32_t DebugSwap32(uint32_t x); -void CmndFlashDump(void); -void CmndI2cWrite(void); -void CmndI2cRead(void); -void CmndI2cStretch(void); -void CmndI2cClock(void); -bool Xdrv99(uint8_t function); -void XsnsDriverState(void); -bool XdrvRulesProcess(void); -void ShowFreeMem(const char *where); -bool XdrvCallDriver(uint32_t driver, uint8_t Function); -bool XdrvCall(uint8_t Function); -void LcdInitMode(void); -void LcdInit(uint8_t mode); -void LcdInitDriver(void); -void LcdDrawStringAt(void); -void LcdDisplayOnOff(uint8_t on); -void LcdCenter(uint8_t row, char* txt); -bool LcdPrintLog(void); -void LcdTime(void); -void LcdRefresh(void); -bool Xdsp01(uint8_t function); -void SSD1306InitDriver(void); -void Ssd1306PrintLog(void); -void Ssd1306Time(void); -void Ssd1306Refresh(void); -bool Xdsp02(byte function); -void MatrixWrite(void); -void MatrixClear(void); -void MatrixFixed(char* txt); -void MatrixCenter(char* txt); -void MatrixScrollLeft(char* txt, int loop); -void MatrixScrollUp(char* txt, int loop); -void MatrixInitMode(void); -void MatrixInit(uint8_t mode); -void MatrixInitDriver(void); -void MatrixOnOff(void); -void MatrixDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); -void MatrixPrintLog(uint8_t direction); -void MatrixRefresh(void); -bool Xdsp03(uint8_t function); -void Ili9341InitMode(void); -void Ili9341Init(uint8_t mode); -void Ili9341InitDriver(void); -void Ili9341Clear(void); -void Ili9341DrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag); -void Ili9341DisplayOnOff(uint8_t on); -void Ili9341OnOff(void); -void Ili9341PrintLog(void); -void Ili9341Refresh(void); -bool Xdsp04(uint8_t function); -void EpdInitDriver29(); -void EpdPrintLog29(void); -void EpdRefresh29(void); -bool Xdsp05(uint8_t function); -void EpdInitDriver42(); -void EpdRefresh42(); -bool Xdsp06(uint8_t function); -void SH1106InitDriver(); -void SH1106PrintLog(void); -void SH1106Time(void); -void SH1106Refresh(void); -bool Xdsp07(uint8_t function); -void ILI9488_InitDriver(); -void ILI9488_MQTT(uint8_t count,const char *cp); -void ILI9488_RDW_BUTT(uint32_t count,uint32_t pwr); -void FT6236Check(); -bool Xdsp08(uint8_t function); -void SSD1351_InitDriver(); -void SSD1351PrintLog(void); -void SSD1351Time(void); -void SSD1351Refresh(void); -bool Xdsp09(uint8_t function); -void RA8876_InitDriver(); -void RA8876_MQTT(uint8_t count,const char *cp); -void RA8876_RDW_BUTT(uint32_t count,uint32_t pwr); -void FT5316Check(); -bool Xdsp10(uint8_t function); -uint8_t XdspPresent(void); -bool XdspCall(uint8_t Function); -void Ws2812StripShow(void); -int mod(int a, int b); -void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offset); -void Ws2812UpdateHand(int position, uint32_t index); -void Ws2812Clock(void); -void Ws2812GradientColor(uint32_t schemenr, struct WsColor* mColor, uint32_t range, uint32_t gradRange, uint32_t i); -void Ws2812Gradient(uint32_t schemenr); -void Ws2812Bars(uint32_t schemenr); -void Ws2812Clear(void); -void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white); -char* Ws2812GetColor(uint32_t led, char* scolor); -void Ws2812ForceSuspend (void); -void Ws2812ForceUpdate (void); -bool Ws2812SetChannels(void); -void Ws2812ShowScheme(void); -void Ws2812ModuleSelected(void); -void CmndLed(void); -void CmndPixels(void); -void CmndRotation(void); -void CmndWidth(void); -bool Xlgt01(uint8_t function); -void LightDiPulse(uint8_t times); -void LightDckiPulse(uint8_t times); -void LightMy92x1Write(uint8_t data); -void LightMy92x1Init(void); -void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty_w, uint8_t duty_c); -bool My92x1SetChannels(void); -void My92x1ModuleSelected(void); -bool Xlgt02(uint8_t function); -void SM16716_SendBit(uint8_t v); -void SM16716_SendByte(uint8_t v); -void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b); -void SM16716_Init(void); -bool Sm16716SetChannels(void); -void Sm16716ModuleSelected(void); -bool Xlgt03(uint8_t function); -uint8_t Sm2135Write(uint8_t data); -void Sm2135Send(uint8_t *buffer, uint8_t size); -bool Sm2135SetChannels(void); -void Sm2135ModuleSelected(void); -bool Xlgt04(uint8_t function); -void SnfL1Send(const char *buffer); -void SnfL1SerialSendOk(void); -bool SnfL1SerialInput(void); -bool SnfL1SetChannels(void); -void SnfL1ModuleSelected(void); -bool Xlgt05(uint8_t function); -bool XlgtCall(uint8_t function); -void HlwCfInterrupt(void); -void HlwCf1Interrupt(void); -void HlwEvery200ms(void); -void HlwEverySecond(void); -void HlwSnsInit(void); -void HlwDrvInit(void); -bool HlwCommand(void); -bool Xnrg01(uint8_t function); -void CseReceived(void); -bool CseSerialInput(void); -void CseEverySecond(void); -void CseSnsInit(void); -void CseDrvInit(void); -bool CseCommand(void); -bool Xnrg02(uint8_t function); -uint8_t PzemCrc(uint8_t *data); -void PzemSend(uint8_t cmd); -bool PzemReceiveReady(void); -bool PzemRecieve(uint8_t resp, float *data); -void PzemEvery250ms(void); -void PzemSnsInit(void); -void PzemDrvInit(void); -bool PzemCommand(void); -bool Xnrg03(uint8_t function); -uint8_t McpChecksum(uint8_t *data); -unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size); -void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size); -void McpSend(uint8_t *data); -void McpGetAddress(void); -void McpAddressReceive(void); -void McpGetCalibration(void); -void McpParseCalibration(void); -bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift); -void McpSetCalibration(struct mcp_cal_registers_type *cal_registers); -void McpSetSystemConfiguration(uint16 interval); -void McpGetFrequency(void); -void McpParseFrequency(void); -void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency); -void McpGetData(void); -void McpParseData(void); -void McpSerialInput(void); -void McpEverySecond(void); -void McpSnsInit(void); -void McpDrvInit(void); -bool McpCommand(void); -bool Xnrg04(uint8_t function); -void PzemAcEverySecond(void); -void PzemAcSnsInit(void); -void PzemAcDrvInit(void); -bool PzemAcCommand(void); -bool Xnrg05(uint8_t function); -void PzemDcEverySecond(void); -void PzemDcSnsInit(void); -void PzemDcDrvInit(void); -bool PzemDcCommand(void); -bool Xnrg06(uint8_t function); -int Ade7953RegSize(uint16_t reg); -void Ade7953Write(uint16_t reg, uint32_t val); -int32_t Ade7953Read(uint16_t reg); -void Ade7953Init(void); -void Ade7953GetData(void); -void Ade7953EnergyEverySecond(void); -void Ade7953DrvInit(void); -bool Ade7953Command(void); -bool Xnrg07(uint8_t function); -void SDM120Every250ms(void); -void Sdm120SnsInit(void); -void Sdm120DrvInit(void); -void Sdm220Reset(void); -void Sdm220Show(bool json); -bool Xnrg08(uint8_t function); -void Dds2382EverySecond(void); -void Dds2382SnsInit(void); -void Dds2382DrvInit(void); -bool Xnrg09(uint8_t function); -void SDM630Every250ms(void); -void Sdm630SnsInit(void); -void Sdm630DrvInit(void); -bool Xnrg10(uint8_t function); -void DDSU666Every250ms(void); -void Ddsu666SnsInit(void); -void Ddsu666DrvInit(void); -bool Xnrg11(uint8_t function); -bool solaxX1_RS485ReceiveReady(void); -void solaxX1_RS485Send(uint16_t msgLen); -uint8_t solaxX1_RS485Receive(uint8_t *value); -uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen); -void solaxX1_SendInverterAddress(void); -void solaxX1_QueryLiveData(void); -uint8_t solaxX1_ParseErrorCode(uint32_t code); -void solaxX1250MSecond(void); -void solaxX1SnsInit(void); -void solaxX1DrvInit(void); -void solaxX1Show(bool json); -bool Xnrg12(uint8_t function); -void FifLEEvery250ms(void); -void FifLESnsInit(void); -void FifLEDrvInit(void); -void FifLEReset(void); -void FifLEShow(bool json); -bool Xnrg13(uint8_t function); -bool XnrgCall(uint8_t function); -void CounterUpdate(uint8_t index); -void CounterUpdate1(void); -void CounterUpdate2(void); -void CounterUpdate3(void); -void CounterUpdate4(void); -bool CounterPinState(void); -void CounterInit(void); -void CounterEverySecond(void); -void CounterSaveState(void); -void CounterShow(bool json); -void CmndCounter(void); -void CmndCounterType(void); -void CmndCounterDebounce(void); -bool Xsns01(uint8_t function); -void AdcInit(void); -uint16_t AdcRead(uint8_t factor); -void AdcEvery250ms(void); -uint16_t AdcGetLux(void); -uint16_t AdcGetRange(void); -void AdcGetCurrentPower(uint8_t factor); -void AdcEverySecond(void); -void AdcShow(bool json); -void CmndAdc(void); -void CmndAdcs(void); -void CmndAdcParam(void); -bool Xsns02(uint8_t function); -void SonoffScSend(const char *data); -void SonoffScInit(void); -void SonoffScSerialInput(char *rcvstat); -void SonoffScShow(bool json); -bool Xsns04(uint8_t function); -uint8_t OneWireReset(void); -void OneWireWriteBit(uint8_t v); -uint8_t OneWireReadBit(void); -void OneWireWrite(uint8_t v); -uint8_t OneWireRead(void); -void OneWireSelect(const uint8_t rom[8]); -void OneWireResetSearch(void); -uint8_t OneWireSearch(uint8_t *newAddr); -bool OneWireCrc8(uint8_t *addr); -void Ds18x20Init(void); -void Ds18x20Convert(void); -bool Ds18x20Read(uint8_t sensor); -void Ds18x20Name(uint8_t sensor); -void Ds18x20EverySecond(void); -void Ds18x20Show(bool json); -bool Xsns05(uint8_t function); -void DhtReadPrep(void); -int32_t DhtExpectPulse(uint8_t sensor, bool level); -bool DhtRead(uint8_t sensor); -void DhtReadTempHum(uint8_t sensor); -bool DhtPinState(); -void DhtInit(void); -void DhtEverySecond(void); -void DhtShow(bool json); -bool Xsns06(uint8_t function); -void DhtReadPrep(void); -int32_t DhtExpectPulse(uint8_t sensor, bool level); -bool DhtRead(uint8_t sensor); -void DhtReadTempHum(uint8_t sensor); -bool DhtPinState(); -void DhtInit(void); -void DhtEverySecond(void); -void DhtShow(bool json); -bool Xsns06(uint8_t function); -bool DhtExpectPulse(uint8_t sensor, int level); -int DhtReadDat(uint8_t sensor); -bool DhtRead(uint8_t sensor); -void DhtReadTempHum(uint8_t sensor); -bool DhtPinState(); -void DhtInit(void); -void DhtEverySecond(void); -void DhtShow(bool json); -bool Xsns06(uint8_t function); -bool DhtExpectPulse(uint32_t sensor, uint32_t level); -bool DhtRead(uint32_t sensor); -void DhtReadTempHum(uint32_t sensor); -bool DhtPinState(); -void DhtInit(void); -void DhtEverySecond(void); -void DhtShow(bool json); -bool Xsns06(uint8_t function); -bool DhtWaitState(uint32_t sensor, uint32_t level); -bool DhtRead(uint32_t sensor); -bool DhtPinState(); -void DhtInit(void); -void DhtEverySecond(void); -void DhtShow(bool json); -bool Xsns06(uint8_t function); -bool ShtReset(void); -bool ShtSendCommand(const uint8_t cmd); -bool ShtAwaitResult(void); -int ShtReadData(void); -bool ShtRead(void); -void ShtDetect(void); -void ShtEverySecond(void); -void ShtShow(bool json); -bool Xsns07(uint8_t function); -uint8_t HtuCheckCrc8(uint16_t data); -uint8_t HtuReadDeviceId(void); -void HtuSetResolution(uint8_t resolution); -void HtuReset(void); -void HtuHeater(uint8_t heater); -void HtuInit(void); -bool HtuRead(void); -void HtuDetect(void); -void HtuEverySecond(void); -void HtuShow(bool json); -bool Xsns08(uint8_t function); -bool Bmp180Calibration(uint8_t bmp_idx); -void Bmp180Read(uint8_t bmp_idx); -bool Bmx280Calibrate(uint8_t bmp_idx); -void Bme280Read(uint8_t bmp_idx); -static void BmeDelayMs(uint32_t ms); -bool Bme680Init(uint8_t bmp_idx); -void Bme680Read(uint8_t bmp_idx); -void BmpDetect(void); -void BmpRead(void); -void BmpShow(bool json); -void BMP_EnterSleep(void); -bool Xsns09(uint8_t function); -bool Bh1750Read(void); -void Bh1750Detect(void); -void Bh1750EverySecond(void); -void Bh1750Show(bool json); -bool Xsns10(uint8_t function); -void Veml6070Detect(void); -void Veml6070UvTableInit(void); -void Veml6070EverySecond(void); -void Veml6070ModeCmd(bool mode_cmd); -uint16_t Veml6070ReadUv(void); -double Veml6070UvRiskLevel(uint16_t uv_level); -double Veml6070UvPower(double uvrisk); -void Veml6070Show(bool json); -bool Xsns11(uint8_t function); -void Ads1115StartComparator(uint8_t channel, uint16_t mode); -int16_t Ads1115GetConversion(uint8_t channel); -void Ads1115Detect(void); -void Ads1115Show(bool json); -bool Xsns12(uint8_t function); -bool Ina219SetCalibration(uint8_t mode, uint16_t addr); -float Ina219GetShuntVoltage_mV(uint16_t addr); -float Ina219GetBusVoltage_V(uint16_t addr); -float Ina219GetCurrent_mA(uint16_t addr); -bool Ina219Read(void); -bool Ina219CommandSensor(void); -void Ina219Detect(void); -void Ina219EverySecond(void); -void Ina219Show(bool json); -bool Xsns13(uint8_t function); -bool Sht3xRead(float &t, float &h, uint8_t sht3x_address); -void Sht3xDetect(void); -void Sht3xShow(bool json); -bool Xsns14(uint8_t function); -uint8_t MhzCalculateChecksum(uint8_t *array); -size_t MhzSendCmd(uint8_t command_id); -bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s); -void MhzEverySecond(void); -bool MhzCommandSensor(void); -void MhzInit(void); -void MhzShow(bool json); -bool Xsns15(uint8_t function); -bool Tsl2561Read(void); -void Tsl2561Detect(void); -void Tsl2561EverySecond(void); -void Tsl2561Show(bool json); -bool Xsns16(uint8_t function); -void Senseair250ms(void); -void SenseairInit(void); -void SenseairShow(bool json); -bool Xsns17(uint8_t function); -bool PmsReadData(void); -void PmsSecond(void); -void PmsInit(void); -void PmsShow(bool json); -bool Xsns18(uint8_t function); -void MGSInit(void); -void MGSPrepare(void); -char* measure_gas(int gas_type, char* buffer); -void MGSShow(bool json); -bool Xsns19(uint8_t function); -bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensorid, uint8_t *buffer); -void NovaSdsSetWorkPeriod(void); -bool NovaSdsReadData(void); -void NovaSdsSecond(void); -bool NovaSdsCommandSensor(void); -void NovaSdsInit(void); -void NovaSdsShow(bool json); -bool Xsns20(uint8_t function); -void sgp30_Init(void); -float sgp30_AbsoluteHumidity(float temperature, float humidity,char tempUnit); -void Sgp30Update(void); -void Sgp30Show(bool json); -bool Xsns21(uint8_t function); -uint8_t Sr04TModeDetect(void); -uint16_t Sr04TMiddleValue(uint16_t first, uint16_t second, uint16_t third); -uint16_t Sr04TMode3Distance(); -uint16_t Sr04TMode2Distance(void); -void Sr04TReading(void); -void Sr04Show(bool json); -bool Xsns22(uint8_t function); -uint8_t Si1145ReadByte(uint8_t reg); -uint16_t Si1145ReadHalfWord(uint8_t reg); -bool Si1145WriteByte(uint8_t reg, uint16_t val); -uint8_t Si1145WriteParamData(uint8_t p, uint8_t v); -bool Si1145Present(void); -void Si1145Reset(void); -void Si1145DeInit(void); -bool Si1145Begin(void); -uint16_t Si1145ReadUV(void); -uint16_t Si1145ReadVisible(void); -uint16_t Si1145ReadIR(void); -bool Si1145Read(void); -void Si1145Detect(void); -void Si1145Update(void); -void Si1145Show(bool json); -bool Xsns24(uint8_t function); -void LM75ADDetect(void); -float LM75ADGetTemp(void); -void LM75ADShow(bool json); -bool Xsns26(uint8_t function); -int8_t wireReadDataBlock( uint8_t reg, - uint8_t *val, - uint16_t len); -void calculateColorTemperature(void); -bool APDS9960_init(void); -uint8_t getMode(void); -void setMode(uint8_t mode, uint8_t enable); -void enableLightSensor(void); -void disableLightSensor(void); -void enableProximitySensor(void); -void disableProximitySensor(void); -void enableGestureSensor(void); -void disableGestureSensor(void); -bool isGestureAvailable(void); -int16_t readGesture(void); -void enablePower(void); -void disablePower(void); -void readAllColorAndProximityData(void); -void resetGestureParameters(void); -bool processGestureData(void); -bool decodeGesture(void); -void handleGesture(void); -void APDS9960_adjustATime(void); -void APDS9960_loop(void); -void APDS9960_detect(void); -void APDS9960_show(bool json); -bool APDS9960CommandSensor(void); -bool Xsns27(uint8_t function); -void Tm16XXSend(uint8_t data); -void Tm16XXSendCommand(uint8_t cmd); -void TM16XXSendData(uint8_t address, uint8_t data); -uint8_t Tm16XXReceive(void); -void Tm16XXClearDisplay(void); -void Tm1638SetLED(uint8_t color, uint8_t pos); -void Tm1638SetLEDs(word leds); -uint8_t Tm1638GetButtons(void); -void TmInit(void); -void TmLoop(void); -bool Xsns28(uint8_t function); -void MCP230xx_CheckForIntCounter(void); -void MCP230xx_CheckForIntRetainer(void); -const char* IntModeTxt(uint8_t intmo); -uint8_t MCP230xx_readGPIO(uint8_t port); -void MCP230xx_ApplySettings(void); -void MCP230xx_Detect(void); -void MCP230xx_CheckForInterrupt(void); -void MCP230xx_Show(bool json); -void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate); -void MCP230xx_Reset(uint8_t pinmode); -bool MCP230xx_Command(void); -void MCP230xx_UpdateWebData(void); -void MCP230xx_OutputTelemetry(void); -void MCP230xx_Interrupt_Counter_Report(void); -void MCP230xx_Interrupt_Retain_Report(void); -bool Xsns29(uint8_t function); -void Mpr121Init(struct mpr121 *pS, bool initial); -void Mpr121Show(struct mpr121 *pS, uint8_t function); -bool Xsns30(uint8_t function); -void CCS811Detect(void); -void CCS811Update(void); -void CCS811Show(bool json); -bool Xsns31(uint8_t function); -void MPU_6050PerformReading(void); -void MPU_6050Detect(void); -void MPU_6050Show(bool json); -bool Xsns32(uint8_t function); -void DS3231Detect(void); -uint8_t bcd2dec(uint8_t n); -uint8_t dec2bcd(uint8_t n); -uint32_t ReadFromDS3231(void); -void SetDS3231Time (uint32_t epoch_time); -void DS3231EverySecond(void); -bool Xsns33(uint8_t function); -bool HxIsReady(uint16_t timeout); -long HxRead(void); -void HxResetPart(void); -void HxReset(void); -void HxCalibrationStateTextJson(uint8_t msg_id); -void SetWeightDelta(); -bool HxCommand(void); -long HxWeight(void); -void HxInit(void); -void HxEvery100mSecond(void); -void HxSaveBeforeRestart(void); -void HxShow(bool json); -void HandleHxAction(void); -void HxSaveSettings(void); -void HxLogUpdates(void); -bool Xsns34(uint8_t function); -void Tx20StartRead(void); -void Tx20Read(void); -void Tx20Init(void); -void Tx20Show(bool json); -bool Xsns35(uint8_t function); -void MGC3130_handleSensorData(); -void MGC3130_sendMessage(uint8_t data[], uint8_t length); -void MGC3130_handleGesture(); -bool MGC3130_handleTouch(); -void MGC3130_handleAirWheel(); -void MGC3130_handleSystemStatus(); -bool MGC3130_receiveMessage(); -bool MGC3130_readData(); -void MGC3130_nextMode(); -void MGC3130_loop(); -void MGC3130_detect(void); -void MGC3130_show(bool json); -bool MGC3130CommandSensor(); -bool Xsns36(uint8_t function); -bool RfSnsFetchSignal(uint8_t DataPin, bool StateSignal); -void RfSnsInitTheoV2(void); -void RfSnsAnalyzeTheov2(void); -void RfSnsTheoV2Show(bool json); -void RfSnsInitAlectoV2(void); -void RfSnsAnalyzeAlectov2(); -void RfSnsAlectoResetRain(void); -uint8_t RfSnsAlectoCRC8(uint8_t *addr, uint8_t len); -void RfSnsAlectoV2Show(bool json); -void RfSnsInit(void); -void RfSnsAnalyzeRawSignal(void); -void RfSnsEverySecond(void); -void RfSnsShow(bool json); -bool Xsns37(uint8_t function); -void AzEverySecond(void); -void AzInit(void); -void AzShow(bool json); -bool Xsns38(uint8_t function); -void MAX31855_Init(void); -void MAX31855_GetResult(void); -float MAX31855_GetProbeTemperature(int32_t RawData); -float MAX31855_GetReferenceTemperature(int32_t RawData); -int32_t MAX31855_ShiftIn(uint8_t Length); -void MAX31855_Show(bool Json); -bool Xsns39(uint8_t function); -void PN532_Init(void); -int8_t PN532_receive(uint8_t *buf, int len, uint16_t timeout); -int8_t PN532_readAckFrame(void); -uint32_t PN532_getFirmwareVersion(void); -void PN532_wakeup(void); -bool PN532_setPassiveActivationRetries(uint8_t maxRetries); -bool PN532_SAMConfig(void); -uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData); -uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data); -uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data); -void PN532_ScanForTag(void); -bool PN532_Command(void); -bool Xsns40(uint8_t function); -bool Max4409Read_lum(void); -void Max4409Detect(void); -void Max4409EverySecond(void); -void Max4409Show(bool json); -bool Xsns41(uint8_t function); -void Scd30Detect(void); -void Scd30Update(void); -int Scd30GetCommand(int command_code, uint16_t *pvalue); -int Scd30SetCommand(int command_code, uint16_t value); -bool Scd30CommandSensor(); -void Scd30Show(bool json); -bool Xsns42(byte function); -int hreReadBit(); -char hreReadChar(int &parity_errors); -void hreInit(void); -void hreEvery50ms(void); -void hreShow(boolean json); -bool Xsns43(byte function); -uint8_t sps30_calc_CRC(uint8_t *data); -void sps30_get_data(uint16_t cmd, uint8_t *data, uint8_t dlen); -void sps30_cmd(uint16_t cmd); -void SPS30_Detect(void); -void SPS30_Every_Second(); -void SPS30_Show(bool json); -bool SPS30_cmd(void); -bool Xsns44(byte function); -void Vl53l0Detect(void); -void Vl53l0Every_250MSecond(void); -void Vl53l0Show(boolean json); -bool Xsns45(byte function); -void MLX90614_Init(void); -void MLX90614_Every_Second(void); -void MLX90614_Show(uint8_t json); -bool Xsns46(byte function); -void MAX31865_Init(void); -void MAX31865_GetResult(void); -void MAX31865_Show(bool Json); -bool Xsns47(uint8_t function); -void ChirpWriteI2CRegister(uint8_t addr, uint8_t reg); -uint16_t ChirpFinishReadI2CRegister16bit(uint8_t addr); -void ChirpReset(uint8_t addr); -void ChirpResetAll(void); -void ChirpClockSet(); -void ChirpSleep(uint8_t addr); -void ChirpSelect(uint8_t sensor); -uint8_t ChirpReadVersion(uint8_t addr); -bool ChirpSet(uint8_t addr); -bool ChirpScan(); -void ChirpDetect(void); -void ChirpServiceAllSensors(uint8_t job); -void ChirpEvery100MSecond(void); -void ChirpShow(bool json); -bool ChirpCmd(void); -bool Xsns48(uint8_t function); -void PAJ7620SelectBank(uint8_t bank); -void PAJ7620DecodeGesture(void); -void PAJ7620ReadGesture(void); -void PAJ7620Detect(void); -void PAJ7620Init(void); -void PAJ7620Loop(void); -void PAJ7620Show(bool json); -bool PAJ7620CommandSensor(void); -bool Xsns50(uint8_t function); -void RDM6300_Init(); -void RDM6300_ScanForTag(); -uint8_t rm6300_hexnibble(char chr); -void rm6300_hstring_to_array(uint8_t array[], uint8_t len, char buffer[]); -void RDM6300_Show(void); -bool Xsns51(byte function); -void IBEACON_Init(); -void hm17_every_second(void); -void hm17_sbclr(void); -void hm17_sendcmd(uint8_t cmd); -uint32_t ibeacon_add(struct IBEACON *ib); -void hm17_decode(void); -void IBEACON_loop(); -void IBEACON_Show(void); -bool xsns52_cmd(void); -bool ibeacon_cmd(void); -void ib_sendbeep(void); -void ibeacon_mqtt(const char *mac,const char *rssi); -bool Xsns52(byte function); -double sml_median_array(double *array,uint8_t len); -double sml_median(struct SML_MEDIAN_FILTER* mf, double in); -void ADS1115_init(void); -bool Serial_available(); -uint8_t Serial_read(); -uint8_t Serial_peek(); -void Dump2log(void); -double sml_getvalue(unsigned char *cp,uint8_t index); -uint8_t hexnibble(char chr); -double CharToDouble(const char *str); -void ebus_esc(uint8_t *ebus_buffer, unsigned char len); -uint8_t ebus_crc8(uint8_t data, uint8_t crc_init); -uint8_t ebus_CalculateCRC( uint8_t *Data, uint16_t DataLen ); -void sml_empty_receiver(uint32_t meters); -void sml_shift_in(uint32_t meters,uint32_t shard); -void SML_Poll(void); -void SML_Decode(uint8_t index); -void SML_Immediate_MQTT(const char *mp,uint8_t index,uint8_t mindex); -void SML_Show(boolean json); -void SML_CounterUpd(uint8_t index); -void SML_CounterUpd1(void); -void SML_CounterUpd2(void); -void SML_CounterUpd3(void); -void SML_CounterUpd4(void); -bool Gpio_used(uint8_t gpiopin); -void SML_Init(void); -uint32_t SML_SetBaud(uint32_t meter, uint32_t br); -uint32_t SML_Write(uint32_t meter,char *hstr); -void SetDBGLed(uint8_t srcpin, uint8_t ledpin); -void SML_Counter_Poll(void); -void SML_Check_Send(void); -uint8_t sml_hexnibble(char chr); -void SML_Send_Seq(uint32_t meter,char *seq); -uint16_t MBUS_calculateCRC(uint8_t *frame, uint8_t num); -uint8_t SML_PzemCrc(uint8_t *data, uint8_t len); -uint8_t CalcEvenParity(uint8_t data); -bool XSNS_53_cmd(void); -void InjektCounterValue(uint8_t meter,uint32_t counter); -void SML_CounterSaveState(void); -bool Xsns53(byte function); -static uint32_t _expand_r_shunt(uint16_t compact_r_shunt); -void Ina226SetCalibration(uint8_t slaveIndex); -bool Ina226TestPresence(uint8_t device); -void Ina226ResetActive(void); -void Ina226Init(); -float Ina226ReadBus_v(uint8_t device); -float Ina226ReadShunt_i(uint8_t device); -float Ina226ReadPower_w(uint8_t device); -void Ina226Read(uint8_t device); -void Ina226EverySecond(); -bool Ina226CommandSensor(); -void Ina226Show(bool json); -bool Xsns54(byte callback_id); -bool Hih6Read(void); -void Hih6Detect(void); -void Hih6EverySecond(void); -void Hih6Show(bool json); -bool Xsns55(uint8_t function); -void HpmaSecond(void); -void HpmaInit(void); -void HpmaShow(bool json); -bool Xsns56(uint8_t function); -void Tsl2591Init(void); -bool Tsl2591Read(void); -void Tsl2591EverySecond(void); -void Tsl2591Show(bool json); -bool Xsns57(uint8_t function); -bool Dht12Read(void); -void Dht12Detect(void); -void Dht12EverySecond(void); -void Dht12Show(bool json); -bool Xsns58(uint8_t function); -uint32_t DS1624_Idx2Addr(uint32_t idx); -int DS1624_Restart(uint8_t config, uint32_t idx); -void DS1624_HotPlugUp(uint32_t idx); -void DS1624_HotPlugDown(int idx); -bool DS1624GetTemp(float *value, int idx); -void DS1624HotPlugScan(void); -void DS1624EverySecond(void); -void DS1624Show(bool json); -bool Xsns59(uint8_t function); -void UBXcalcChecksum(char* CK, size_t msgSize); -bool UBXcompareMsgHeader(const char* msgHeader); -void UBXinitCFG(void); -void UBXTriggerTele(void); -void UBXDetect(void); -uint32_t UBXprocessGPS(); -void UBXsendHeader(void); -void UBXsendRecord(uint8_t *buf); -void UBXsendFooter(void); -void UBXsendFile(void); -void UBXSetRate(uint16_t interval); -void UBXSelectMode(uint16_t mode); -bool UBXHandlePOSLLH(); -void UBXHandleSTATUS(); -void UBXHandleTIME(); -void UBXHandleOther(void); -void UBXLoop50msec(void); -void UBXLoop(void); -void UBXShow(bool json); -bool UBXCmd(void); -bool Xsns60(uint8_t function); -bool MINRFinitBLE(uint8_t _mode); -void MINRFhopChannel(); -bool MINRFreceivePacket(void); -void MINRFswapbuf(uint8_t len); -void MINRFwhiten(uint8_t *buf, uint8_t len, uint8_t lfsr); -void MINRFreverseMAC(uint8_t _mac[]); -void MINRFchangePacketModeTo(uint8_t _mode); -void MINRFpurgeFakeSensors(void); -void MINRFhandleFloraPacket(void); -void MINRFhandleMJ_HT_V1Packet(void); -void MINRFhandleLYWSD02Packet(void); -void MINRFhandleLYWSD03Packet(void); -void MINRF_EVERY_50_MSECOND(); -void MINRFShow(bool json); -bool Xsns61(uint8_t function); -void HM10_Launchtask(uint8_t task, uint8_t slot, uint8_t delay); -void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot); -void HM10_Reset(void); -void HM10_Discovery_Scan(void); -void HM10_Read_LYWSD03(void); -void HM10_Read_LYWSD02(void); -void HM10_Time_LYWSD02(void); -void HM10SerialInit(void); -void HM10MACStringToBytes(const char* string, uint8_t _mac[]); -void HM10ParseResponse(char *buf); -void HM10readTempHum(char *_buf); -bool HM10readBat(char *_buf); -bool HM10SerialHandleFeedback(); -void HM10_TaskEvery100ms(); -void HM10EverySecond(); -bool HM10Cmd(void); -void HM10Show(bool json); -bool Xsns62(uint8_t function); -bool begin(); -bool AHT10Read(void); -unsigned char readStatus(void); -void AHT10Detect(void); -void AHT10EverySecond(void); -void AHT10Show(bool json); -bool Xsns64(uint8_t function); -void HandleMetrics(void); -bool Xsns91(uint8_t function); -bool XsnsEnabled(uint32_t sns_index); -void XsnsSensorState(void); -bool XsnsNextCall(uint8_t Function, uint8_t &xsns_index); -bool XsnsCall(uint8_t Function); -bool I2cEnabled(uint32_t i2c_index); -void I2cDriverState(void); -#line 184 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota.ino" -void setup(void) -{ - global_state.data = 3; - - RtcRebootLoad(); - if (!RtcRebootValid()) { - RtcReboot.fast_reboot_count = 0; - } - RtcReboot.fast_reboot_count++; - RtcRebootSave(); - - Serial.begin(APP_BAUDRATE); - seriallog_level = LOG_LEVEL_INFO; - - snprintf_P(my_version, sizeof(my_version), PSTR("%d.%d.%d"), VERSION >> 24 & 0xff, VERSION >> 16 & 0xff, VERSION >> 8 & 0xff); - if (VERSION & 0xff) { - snprintf_P(my_version, sizeof(my_version), PSTR("%s.%d"), my_version, VERSION & 0xff); - } - - snprintf_P(my_image, sizeof(my_image), PSTR("(%s)"), CODE_IMAGE_STR); - - SettingsLoad(); - SettingsDelta(); - - OsWatchInit(); - - GetFeatures(); - - if (1 == RtcReboot.fast_reboot_count) { - UpdateQuickPowerCycle(true); - XdrvCall(FUNC_SETTINGS_OVERRIDE); - } - - - seriallog_level = Settings.seriallog_level; - seriallog_timer = SERIALLOG_TIMER; - syslog_level = Settings.syslog_level; - stop_flash_rotate = Settings.flag.stop_flash_rotate; - save_data_counter = Settings.save_data; - sleep = Settings.sleep; -#ifndef USE_EMULATION - Settings.flag2.emulation = 0; -#else -#ifndef USE_EMULATION_WEMO - if (EMUL_WEMO == Settings.flag2.emulation) { Settings.flag2.emulation = 0; } -#endif -#ifndef USE_EMULATION_HUE - if (EMUL_HUE == Settings.flag2.emulation) { Settings.flag2.emulation = 0; } -#endif -#endif - - if (Settings.param[P_BOOT_LOOP_OFFSET]) { - - if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET]) { - Settings.flag3.user_esp8285_enable = 0; - if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +1) { - for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { - if (bitRead(Settings.rule_stop, i)) { - bitWrite(Settings.rule_enabled, i, 0); - } - } - } - if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +2) { - Settings.rule_enabled = 0; - } - if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +3) { - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - Settings.my_gp.io[i] = GPIO_NONE; - } - Settings.my_adc0 = ADC0_NONE; - } - if (RtcReboot.fast_reboot_count > Settings.param[P_BOOT_LOOP_OFFSET] +4) { - Settings.module = SONOFF_BASIC; - - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_LOG_SOME_SETTINGS_RESET " (%d)"), RtcReboot.fast_reboot_count); - } - } - - Format(mqtt_client, SettingsText(SET_MQTT_CLIENT), sizeof(mqtt_client)); - Format(mqtt_topic, SettingsText(SET_MQTT_TOPIC), sizeof(mqtt_topic)); - if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { - SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); - snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME), mqtt_topic, ESP.getChipId() & 0x1FFF); - } else { - snprintf_P(my_hostname, sizeof(my_hostname)-1, SettingsText(SET_HOSTNAME)); - } - - GetEspHardwareType(); - GpioInit(); - - - - WifiConnect(); - - SetPowerOnState(); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_ESP8266_RELEASE), PROJECT, SettingsText(SET_FRIENDLYNAME1), my_version, my_image); -#ifdef FIRMWARE_MINIMAL - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION)); -#endif - - memcpy_P(log_data, VERSION_MARKER, 1); - - RtcInit(); - -#ifdef USE_ARDUINO_OTA - ArduinoOTAInit(); -#endif - - XdrvCall(FUNC_INIT); - XsnsCall(FUNC_INIT); -} - -void BacklogLoop(void) -{ - if (TimeReached(backlog_delay)) { - if (!BACKLOG_EMPTY && !backlog_mutex) { -#ifdef SUPPORT_IF_STATEMENT - backlog_mutex = true; - String cmd = backlog.shift(); - backlog_mutex = false; - ExecuteCommand((char*)cmd.c_str(), SRC_BACKLOG); -#else - backlog_mutex = true; - ExecuteCommand((char*)backlog[backlog_pointer].c_str(), SRC_BACKLOG); - backlog_pointer++; - if (backlog_pointer >= MAX_BACKLOG) { backlog_pointer = 0; } - backlog_mutex = false; -#endif - } - } -} - -void loop(void) -{ - uint32_t my_sleep = millis(); - - XdrvCall(FUNC_LOOP); - XsnsCall(FUNC_LOOP); - - OsWatchLoop(); - - ButtonLoop(); - SwitchLoop(); -#ifdef ROTARY_V1 - RotaryLoop(); -#endif - BacklogLoop(); - - if (TimeReached(state_50msecond)) { - SetNextTimeInterval(state_50msecond, 50); - XdrvCall(FUNC_EVERY_50_MSECOND); - XsnsCall(FUNC_EVERY_50_MSECOND); - } - if (TimeReached(state_100msecond)) { - SetNextTimeInterval(state_100msecond, 100); - Every100mSeconds(); - XdrvCall(FUNC_EVERY_100_MSECOND); - XsnsCall(FUNC_EVERY_100_MSECOND); - } - if (TimeReached(state_250msecond)) { - SetNextTimeInterval(state_250msecond, 250); - Every250mSeconds(); - XdrvCall(FUNC_EVERY_250_MSECOND); - XsnsCall(FUNC_EVERY_250_MSECOND); - } - if (TimeReached(state_second)) { - SetNextTimeInterval(state_second, 1000); - PerformEverySecond(); - XdrvCall(FUNC_EVERY_SECOND); - XsnsCall(FUNC_EVERY_SECOND); - } - - if (!serial_local) { SerialInput(); } - -#ifdef USE_ARDUINO_OTA - ArduinoOtaLoop(); -#endif - - uint32_t my_activity = millis() - my_sleep; - - if (Settings.flag3.sleep_normal) { - - delay(sleep); - } else { - if (my_activity < (uint32_t)sleep) { - delay((uint32_t)sleep - my_activity); - } else { - if (global_state.wifi_down) { - delay(my_activity /2); - } - } - } - - if (!my_activity) { my_activity++; } - uint32_t loop_delay = sleep; - if (!loop_delay) { loop_delay++; } - uint32_t loops_per_second = 1000 / loop_delay; - uint32_t this_cycle_ratio = 100 * my_activity / loop_delay; - loop_load_avg = loop_load_avg - (loop_load_avg / loops_per_second) + (this_cycle_ratio / loops_per_second); -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/sendemail.ino" -#ifdef USE_SENDMAIL - -#include "sendemail.h" -# 25 "C:/shared/sonoff/Git/Tasmota/tasmota/sendemail.ino" -#ifndef SEND_MAIL_MINRAM -#define SEND_MAIL_MINRAM 12*1024 -#endif - -#define xPSTR(a) a - -uint16_t SendMail(char *buffer) { - char *params,*oparams; - const char *mserv; - uint16_t port; - const char *user; - const char *pstr; - const char *passwd; - const char *from; - const char *to; - const char *subject; - const char *cmd; - char auth=0; - uint16_t status=1; - SendEmail *mail=0; - uint16_t blen; - char *endcmd; - - - - uint16_t mem=ESP.getFreeHeap(); - if (memsend(from,to,subject,cmd); - delete mail; - if (result==true) status=0; - } - -exit: - if (oparams) free(oparams); - return status; -} - -void script_send_email_body(BearSSL::WiFiClientSecure_light *client); - - -SendEmail::SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used) : - host(host), port(port), user(user), passwd(passwd), timeout(timeout), ssl(ssl), auth_used(auth_used), client(new BearSSL::WiFiClientSecure_light(1024,1024)) { -} - -String SendEmail::readClient() { - delay(0); - String r = client->readStringUntil('\n'); - - r.trim(); - while (client->available()) { - delay(0); - r += client->readString(); - } - return r; -} - -bool SendEmail::send(const String& from, const String& to, const String& subject, const char *msg) { -bool status=false; -String buffer; - - if (!host.length()) { - return status; - } - - client->setTimeout(timeout); - -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("Connecting: %s on port %d"),host.c_str(),port); -#endif - - if (!client->connect(host.c_str(), port)) { -#ifdef DEBUG_EMAIL_PORT - AddLog_P(LOG_LEVEL_INFO, PSTR("Connection failed")); -#endif - goto exit; - } - - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("220"))) { - goto exit; - } - - buffer = F("EHLO "); - buffer += client->localIP().toString(); - - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("250"))) { - goto exit; - } - if (user.length()>0 && passwd.length()>0 ) { - - buffer = F("AUTH LOGIN"); - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("334"))) - { - goto exit; - } - base64 b; - buffer = b.encode(user); - - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("334"))) { - goto exit; - } - buffer = b.encode(passwd); - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("235"))) { - goto exit; - } - } - - - buffer = F("MAIL FROM:"); - buffer += from; - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("250"))) { - goto exit; - } - buffer = F("RCPT TO:"); - buffer += to; - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("250"))) { - goto exit; - } - - buffer = F("DATA"); - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = readClient(); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - if (!buffer.startsWith(F("354"))) { - goto exit; - } - buffer = F("From: "); - buffer += from; - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = F("To: "); - buffer += to; - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - buffer = F("Subject: "); - buffer += subject; - buffer += F("\r\n"); - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - -#ifdef USE_SCRIPT - if (*msg=='*' && *(msg+1)==0) { - script_send_email_body(client); - } else { - client->println(msg); - } -#else - client->println(msg); -#endif - client->println('.'); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - - buffer = F("QUIT"); - client->println(buffer); -#ifdef DEBUG_EMAIL_PORT - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); -#endif - - status=true; -exit: - - return status; -} - - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" -#ifndef DOMOTICZ_UPDATE_TIMER -#define DOMOTICZ_UPDATE_TIMER 0 -#endif - -#ifndef EMULATION -#define EMULATION EMUL_NONE -#endif - -#ifndef MTX_ADDRESS1 -#define MTX_ADDRESS1 0 -#endif -#ifndef MTX_ADDRESS2 -#define MTX_ADDRESS2 0 -#endif -#ifndef MTX_ADDRESS3 -#define MTX_ADDRESS3 0 -#endif -#ifndef MTX_ADDRESS4 -#define MTX_ADDRESS4 0 -#endif -#ifndef MTX_ADDRESS5 -#define MTX_ADDRESS5 0 -#endif -#ifndef MTX_ADDRESS6 -#define MTX_ADDRESS6 0 -#endif -#ifndef MTX_ADDRESS7 -#define MTX_ADDRESS7 0 -#endif -#ifndef MTX_ADDRESS8 -#define MTX_ADDRESS8 0 -#endif - -#ifndef HOME_ASSISTANT_DISCOVERY_ENABLE -#define HOME_ASSISTANT_DISCOVERY_ENABLE 0 -#endif - -#ifndef LATITUDE -#define LATITUDE 48.858360 -#endif -#ifndef LONGITUDE -#define LONGITUDE 2.294442 -#endif - -#ifndef WORKING_PERIOD -#define WORKING_PERIOD 5 -#endif - -#ifndef COLOR_TEXT -#define COLOR_TEXT "#000" -#endif -#ifndef COLOR_BACKGROUND -#define COLOR_BACKGROUND "#fff" -#endif -#ifndef COLOR_FORM -#define COLOR_FORM "#f2f2f2" -#endif -#ifndef COLOR_INPUT_TEXT -#define COLOR_INPUT_TEXT "#000" -#endif -#ifndef COLOR_INPUT -#define COLOR_INPUT "#fff" -#endif -#ifndef COLOR_CONSOLE_TEXT -#define COLOR_CONSOLE_TEXT "#000" -#endif -#ifndef COLOR_CONSOLE -#define COLOR_CONSOLE "#fff" -#endif -#ifndef COLOR_TEXT_WARNING -#define COLOR_TEXT_WARNING "#f00" -#endif -#ifndef COLOR_TEXT_SUCCESS -#define COLOR_TEXT_SUCCESS "#008000" -#endif -#ifndef COLOR_BUTTON_TEXT -#define COLOR_BUTTON_TEXT "#fff" -#endif -#ifndef COLOR_BUTTON -#define COLOR_BUTTON "#1fa3ec" -#endif -#ifndef COLOR_BUTTON_HOVER -#define COLOR_BUTTON_HOVER "#0e70a4" -#endif -#ifndef COLOR_BUTTON_RESET -#define COLOR_BUTTON_RESET "#d43535" -#endif -#ifndef COLOR_BUTTON_RESET_HOVER -#define COLOR_BUTTON_RESET_HOVER "#931f1f" -#endif -#ifndef COLOR_BUTTON_SAVE -#define COLOR_BUTTON_SAVE "#47c266" -#endif -#ifndef COLOR_BUTTON_SAVE_HOVER -#define COLOR_BUTTON_SAVE_HOVER "#5aaf6f" -#endif -#ifndef COLOR_TIMER_TAB_TEXT -#define COLOR_TIMER_TAB_TEXT "#fff" -#endif -#ifndef COLOR_TIMER_TAB_BACKGROUND -#define COLOR_TIMER_TAB_BACKGROUND "#999" -#endif -#ifndef COLOR_TITLE_TEXT -#define COLOR_TITLE_TEXT COLOR_TEXT -#endif -#ifndef IR_RCV_MIN_UNKNOWN_SIZE -#define IR_RCV_MIN_UNKNOWN_SIZE 6 -#endif -#ifndef ENERGY_OVERTEMP -#define ENERGY_OVERTEMP 90 -#endif -#ifndef DEFAULT_DIMMER_MAX -#define DEFAULT_DIMMER_MAX 100 -#endif -#ifndef DEFAULT_DIMMER_MIN -#define DEFAULT_DIMMER_MIN 0 -#endif -#ifndef DEFAULT_LIGHT_DIMMER -#define DEFAULT_LIGHT_DIMMER 10 -#endif -#ifndef DEFAULT_LIGHT_COMPONENT -#define DEFAULT_LIGHT_COMPONENT 255 -#endif -#ifndef CORS_ENABLED_ALL -#define CORS_ENABLED_ALL "*" -#endif - - -enum WebColors { - COL_TEXT, COL_BACKGROUND, COL_FORM, - COL_INPUT_TEXT, COL_INPUT, COL_CONSOLE_TEXT, COL_CONSOLE, - COL_TEXT_WARNING, COL_TEXT_SUCCESS, - COL_BUTTON_TEXT, COL_BUTTON, COL_BUTTON_HOVER, COL_BUTTON_RESET, COL_BUTTON_RESET_HOVER, COL_BUTTON_SAVE, COL_BUTTON_SAVE_HOVER, - COL_TIMER_TAB_TEXT, COL_TIMER_TAB_BACKGROUND, COL_TITLE, - COL_LAST }; - -const char kWebColors[] PROGMEM = - COLOR_TEXT "|" COLOR_BACKGROUND "|" COLOR_FORM "|" - COLOR_INPUT_TEXT "|" COLOR_INPUT "|" COLOR_CONSOLE_TEXT "|" COLOR_CONSOLE "|" - COLOR_TEXT_WARNING "|" COLOR_TEXT_SUCCESS "|" - COLOR_BUTTON_TEXT "|" COLOR_BUTTON "|" COLOR_BUTTON_HOVER "|" COLOR_BUTTON_RESET "|" COLOR_BUTTON_RESET_HOVER "|" COLOR_BUTTON_SAVE "|" COLOR_BUTTON_SAVE_HOVER "|" - COLOR_TIMER_TAB_TEXT "|" COLOR_TIMER_TAB_BACKGROUND "|" COLOR_TITLE_TEXT; - -enum TasmotaSerialConfig { - TS_SERIAL_5N1, TS_SERIAL_6N1, TS_SERIAL_7N1, TS_SERIAL_8N1, - TS_SERIAL_5N2, TS_SERIAL_6N2, TS_SERIAL_7N2, TS_SERIAL_8N2, - TS_SERIAL_5E1, TS_SERIAL_6E1, TS_SERIAL_7E1, TS_SERIAL_8E1, - TS_SERIAL_5E2, TS_SERIAL_6E2, TS_SERIAL_7E2, TS_SERIAL_8E2, - TS_SERIAL_5O1, TS_SERIAL_6O1, TS_SERIAL_7O1, TS_SERIAL_8O1, - TS_SERIAL_5O2, TS_SERIAL_6O2, TS_SERIAL_7O2, TS_SERIAL_8O2 }; - -const uint8_t kTasmotaSerialConfig[] PROGMEM = { - SERIAL_5N1, SERIAL_6N1, SERIAL_7N1, SERIAL_8N1, - SERIAL_5N2, SERIAL_6N2, SERIAL_7N2, SERIAL_8N2, - SERIAL_5E1, SERIAL_6E1, SERIAL_7E1, SERIAL_8E1, - SERIAL_5E2, SERIAL_6E2, SERIAL_7E2, SERIAL_8E2, - SERIAL_5O1, SERIAL_6O1, SERIAL_7O1, SERIAL_8O1, - SERIAL_5O2, SERIAL_6O2, SERIAL_7O2, SERIAL_8O2 -}; - - - - - -const uint16_t RTC_MEM_VALID = 0xA55A; - -uint32_t rtc_settings_crc = 0; - -uint32_t GetRtcSettingsCrc(void) -{ - uint32_t crc = 0; - uint8_t *bytes = (uint8_t*)&RtcSettings; - - for (uint32_t i = 0; i < sizeof(RTCMEM); i++) { - crc += bytes[i]*(i+1); - } - return crc; -} - -void RtcSettingsSave(void) -{ - if (GetRtcSettingsCrc() != rtc_settings_crc) { - RtcSettings.valid = RTC_MEM_VALID; - ESP.rtcUserMemoryWrite(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); - rtc_settings_crc = GetRtcSettingsCrc(); - } -} - -void RtcSettingsLoad(void) -{ - ESP.rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); - if (RtcSettings.valid != RTC_MEM_VALID) { - memset(&RtcSettings, 0, sizeof(RTCMEM)); - RtcSettings.valid = RTC_MEM_VALID; - RtcSettings.energy_kWhtoday = Settings.energy_kWhtoday; - RtcSettings.energy_kWhtotal = Settings.energy_kWhtotal; - RtcSettings.energy_usage = Settings.energy_usage; - for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - RtcSettings.pulse_counter[i] = Settings.pulse_counter[i]; - } - RtcSettings.power = Settings.power; - RtcSettingsSave(); - } - rtc_settings_crc = GetRtcSettingsCrc(); -} - -bool RtcSettingsValid(void) -{ - return (RTC_MEM_VALID == RtcSettings.valid); -} - - - -uint32_t rtc_reboot_crc = 0; - -uint32_t GetRtcRebootCrc(void) -{ - uint32_t crc = 0; - uint8_t *bytes = (uint8_t*)&RtcReboot; - - for (uint32_t i = 0; i < sizeof(RTCRBT); i++) { - crc += bytes[i]*(i+1); - } - return crc; -} - -void RtcRebootSave(void) -{ - if (GetRtcRebootCrc() != rtc_reboot_crc) { - RtcReboot.valid = RTC_MEM_VALID; - ESP.rtcUserMemoryWrite(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT)); - rtc_reboot_crc = GetRtcRebootCrc(); - } -} - -void RtcRebootReset(void) -{ - RtcReboot.fast_reboot_count = 0; - RtcRebootSave(); -} - -void RtcRebootLoad(void) -{ - ESP.rtcUserMemoryRead(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT)); - if (RtcReboot.valid != RTC_MEM_VALID) { - memset(&RtcReboot, 0, sizeof(RTCRBT)); - RtcReboot.valid = RTC_MEM_VALID; - - RtcRebootSave(); - } - rtc_reboot_crc = GetRtcRebootCrc(); -} - -bool RtcRebootValid(void) -{ - return (RTC_MEM_VALID == RtcReboot.valid); -} - - - - - -extern "C" { -#include "spi_flash.h" -} -#include "eboot_command.h" - -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) - -extern "C" uint32_t _SPIFFS_end; - -const uint32_t SPIFFS_END = ((uint32_t)&_SPIFFS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; - -#else - -#if AUTOFLASHSIZE - -#include "flash_hal.h" - - -const uint32_t SPIFFS_END = (FS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; - -#else - -extern "C" uint32_t _FS_end; - -const uint32_t SPIFFS_END = ((uint32_t)&_FS_end - 0x40200000) / SPI_FLASH_SEC_SIZE; - -#endif - -#endif - - -const uint32_t SETTINGS_LOCATION = SPIFFS_END; - -const uint8_t CFG_ROTATES = 8; - -uint32_t settings_location = SETTINGS_LOCATION; -uint32_t settings_crc32 = 0; -uint8_t *settings_buffer = nullptr; - - - - - -void SetFlashModeDout(void) -{ - uint8_t *_buffer; - uint32_t address; - - eboot_command ebcmd; - eboot_command_read(&ebcmd); - address = ebcmd.args[0]; - _buffer = new uint8_t[FLASH_SECTOR_SIZE]; - - if (ESP.flashRead(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) { - if (_buffer[2] != 3) { - _buffer[2] = 3; - if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) { - ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); - } - } - } - delete[] _buffer; -} - -bool VersionCompatible(void) -{ - if (Settings.flag3.compatibility_check) { - return true; - } - - eboot_command ebcmd; - eboot_command_read(&ebcmd); - uint32_t start_address = ebcmd.args[0]; - uint32_t end_address = start_address + (ebcmd.args[2] & 0xFFFFF000) + FLASH_SECTOR_SIZE; - uint32_t* buffer = new uint32_t[FLASH_SECTOR_SIZE / 4]; - - uint32_t version[3] = { 0 }; - bool found = false; - for (uint32_t address = start_address; address < end_address; address = address + FLASH_SECTOR_SIZE) { - ESP.flashRead(address, (uint32_t*)buffer, FLASH_SECTOR_SIZE); - if ((address == start_address) && (0x1F == (buffer[0] & 0xFF))) { - version[1] = 0xFFFFFFFF; - found = true; - } else { - for (uint32_t i = 0; i < (FLASH_SECTOR_SIZE / 4); i++) { - version[0] = version[1]; - version[1] = version[2]; - version[2] = buffer[i]; - if ((MARKER_START == version[0]) && (MARKER_END == version[2])) { - found = true; - break; - } - } - } - if (found) { break; } - } - delete[] buffer; - - if (!found) { version[1] = 0; } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("OTA: Version 0x%08X, Compatible 0x%08X"), version[1], VERSION_COMPATIBLE); - - if (version[1] < VERSION_COMPATIBLE) { - uint32_t eboot_magic = 0; - ESP.rtcUserMemoryWrite(0, (uint32_t*)&eboot_magic, sizeof(eboot_magic)); - return false; - } - - return true; -} - -void SettingsBufferFree(void) -{ - if (settings_buffer != nullptr) { - free(settings_buffer); - settings_buffer = nullptr; - } -} - -bool SettingsBufferAlloc(void) -{ - SettingsBufferFree(); - if (!(settings_buffer = (uint8_t *)malloc(sizeof(Settings)))) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_UPLOAD_ERR_2)); - return false; - } - return true; -} - -uint16_t GetCfgCrc16(uint8_t *bytes, uint32_t size) -{ - uint16_t crc = 0; - - for (uint32_t i = 0; i < size; i++) { - if ((i < 14) || (i > 15)) { crc += bytes[i]*(i+1); } - } - return crc; -} - -uint16_t GetSettingsCrc(void) -{ - - uint32_t size = ((Settings.version < 0x06060007) || (Settings.version > 0x0606000A)) ? 3584 : sizeof(SYSCFG); - return GetCfgCrc16((uint8_t*)&Settings, size); -} - -uint32_t GetCfgCrc32(uint8_t *bytes, uint32_t size) -{ - - uint32_t crc = 0; - - while (size--) { - crc ^= *bytes++; - for (uint32_t j = 0; j < 8; j++) { - crc = (crc >> 1) ^ (-int(crc & 1) & 0xEDB88320); - } - } - return ~crc; -} - -uint32_t GetSettingsCrc32(void) -{ - return GetCfgCrc32((uint8_t*)&Settings, sizeof(SYSCFG) -4); -} - -void SettingsSaveAll(void) -{ - if (Settings.flag.save_state) { - Settings.power = power; - } else { - Settings.power = 0; - } - XsnsCall(FUNC_SAVE_BEFORE_RESTART); - XdrvCall(FUNC_SAVE_BEFORE_RESTART); - SettingsSave(0); -} - - - - - -void UpdateQuickPowerCycle(bool update) -{ - if (Settings.flag3.fast_power_cycle_disable) { return; } - - uint32_t pc_register; - uint32_t pc_location = SETTINGS_LOCATION - CFG_ROTATES; - - ESP.flashRead(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); - if (update && ((pc_register & 0xFFFFFFF0) == 0xFFA55AB0)) { - uint32_t counter = ((pc_register & 0xF) << 1) & 0xF; - if (0 == counter) { - SettingsErase(3); - EspRestart(); - } else { - pc_register = 0xFFA55AB0 | counter; - ESP.flashWrite(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("QPC: Flag %02X"), counter); - } - } - else if (pc_register != 0xFFA55ABF) { - pc_register = 0xFFA55ABF; - - if (ESP.flashEraseSector(pc_location)) { - ESP.flashWrite(pc_location * SPI_FLASH_SEC_SIZE, (uint32*)&pc_register, sizeof(pc_register)); - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("QPC: Reset")); - } -} - - - - - -uint32_t GetSettingsTextLen(void) -{ - char* position = Settings.text_pool; - for (uint32_t size = 0; size < SET_MAX; size++) { - while (*position++ != '\0') { } - } - return position - Settings.text_pool; -} - -bool SettingsUpdateText(uint32_t index, const char* replace_me) -{ - if (index >= SET_MAX) { - return false; - } - - - uint32_t replace_len = strlen(replace_me); - char replace[replace_len +1]; - memcpy(replace, replace_me, sizeof(replace)); - - uint32_t start_pos = 0; - uint32_t end_pos = 0; - char* position = Settings.text_pool; - for (uint32_t size = 0; size < SET_MAX; size++) { - while (*position++ != '\0') { } - if (1 == index) { - start_pos = position - Settings.text_pool; - } - else if (0 == index) { - end_pos = position - Settings.text_pool -1; - } - index--; - } - uint32_t char_len = position - Settings.text_pool; - - uint32_t current_len = end_pos - start_pos; - int diff = replace_len - current_len; - - - - - int too_long = (char_len + diff) - settings_text_size; - if (too_long > 0) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_CONFIG "Text overflow by %d char(s)"), too_long); - return false; - } - - if (diff != 0) { - - memmove_P(Settings.text_pool + start_pos + replace_len, Settings.text_pool + end_pos, char_len - end_pos); - } - - memmove_P(Settings.text_pool + start_pos, replace, replace_len); - - memset(Settings.text_pool + char_len + diff, 0x00, settings_text_size - char_len - diff); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG "CR %d/%d"), GetSettingsTextLen(), settings_text_size); - - return true; -} - -char* SettingsText(uint32_t index) -{ - char* position = Settings.text_pool; - - if (index >= SET_MAX) { - position += settings_text_size -1; - } else { - for (;index > 0; index--) { - while (*position++ != '\0') { } - } - } - return position; -} - - - - - -uint32_t GetSettingsAddress(void) -{ - return settings_location * SPI_FLASH_SEC_SIZE; -} - -void SettingsSave(uint8_t rotate) -{ -# 590 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" -#ifndef FIRMWARE_MINIMAL - if ((GetSettingsCrc32() != settings_crc32) || rotate) { - if (1 == rotate) { - stop_flash_rotate = 1; - } - if (2 == rotate) { - settings_location = SETTINGS_LOCATION +1; - } - if (stop_flash_rotate) { - settings_location = SETTINGS_LOCATION; - } else { - settings_location--; - if (settings_location <= (SETTINGS_LOCATION - CFG_ROTATES)) { - settings_location = SETTINGS_LOCATION; - } - } - - Settings.save_flag++; - if (UtcTime() > START_VALID_TIME) { - Settings.cfg_timestamp = UtcTime(); - } else { - Settings.cfg_timestamp++; - } - Settings.cfg_size = sizeof(SYSCFG); - Settings.cfg_crc = GetSettingsCrc(); - Settings.cfg_crc32 = GetSettingsCrc32(); - - if (ESP.flashEraseSector(settings_location)) { - ESP.flashWrite(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); - } - - if (!stop_flash_rotate && rotate) { - for (uint32_t i = 1; i < CFG_ROTATES; i++) { - ESP.flashEraseSector(settings_location -i); - delay(1); - } - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_CONFIG D_SAVED_TO_FLASH_AT " %X, " D_COUNT " %d, " D_BYTES " %d"), settings_location, Settings.save_flag, sizeof(SYSCFG)); - - settings_crc32 = Settings.cfg_crc32; - } -#endif - RtcSettingsSave(); -} - -void SettingsLoad(void) -{ - - struct SYSCFGH { - uint16_t cfg_holder; - uint16_t cfg_size; - unsigned long save_flag; - } _SettingsH; - unsigned long save_flag = 0; - - settings_location = 0; - uint32_t flash_location = SETTINGS_LOCATION +1; - uint16_t cfg_holder = 0; - for (uint32_t i = 0; i < CFG_ROTATES; i++) { - flash_location--; - ESP.flashRead(flash_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); - - bool valid = false; - if (Settings.version > 0x06000000) { - bool almost_valid = (Settings.cfg_crc32 == GetSettingsCrc32()); - if (Settings.version < 0x0606000B) { - almost_valid = (Settings.cfg_crc == GetSettingsCrc()); - } - - if (almost_valid && (0 == cfg_holder)) { cfg_holder = Settings.cfg_holder; } - valid = (cfg_holder == Settings.cfg_holder); - } else { - ESP.flashRead((flash_location -1) * SPI_FLASH_SEC_SIZE, (uint32*)&_SettingsH, sizeof(SYSCFGH)); - valid = (Settings.cfg_holder == _SettingsH.cfg_holder); - } - if (valid) { - if (Settings.save_flag > save_flag) { - save_flag = Settings.save_flag; - settings_location = flash_location; - if (Settings.flag.stop_flash_rotate && (0 == i)) { - break; - } - } - } - - delay(1); - } - if (settings_location > 0) { - ESP.flashRead(settings_location * SPI_FLASH_SEC_SIZE, (uint32*)&Settings, sizeof(SYSCFG)); - AddLog_P2(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_LOADED_FROM_FLASH_AT " %X, " D_COUNT " %lu"), settings_location, Settings.save_flag); - } - -#ifndef FIRMWARE_MINIMAL - if (!settings_location || (Settings.cfg_holder != (uint16_t)CFG_HOLDER)) { - SettingsDefault(); - } - settings_crc32 = GetSettingsCrc32(); -#endif - - RtcSettingsLoad(); -} - -void EspErase(uint32_t start_sector, uint32_t end_sector) -{ - bool serial_output = (LOG_LEVEL_DEBUG_MORE <= seriallog_level); - for (uint32_t sector = start_sector; sector < end_sector; sector++) { - - bool result = ESP.flashEraseSector(sector); - - - - if (serial_output) { -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - Serial.printf(D_LOG_APPLICATION D_ERASED_SECTOR " %d %s\n", sector, (result) ? D_OK : D_ERROR); -#else - Serial.printf_P(PSTR(D_LOG_APPLICATION D_ERASED_SECTOR " %d %s\n"), sector, (result) ? D_OK : D_ERROR); -#endif - delay(10); - } else { - yield(); - } - OsWatchLoop(); - } -} - -void SettingsErase(uint8_t type) -{ -# 733 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" -#ifndef FIRMWARE_MINIMAL - uint32_t _sectorStart = (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 1; - uint32_t _sectorEnd = ESP.getFlashChipRealSize() / SPI_FLASH_SEC_SIZE; - if (1 == type) { - - _sectorStart = (ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE) - 4; - } - else if (2 == type) { - _sectorStart = SETTINGS_LOCATION - CFG_ROTATES; - _sectorEnd = SETTINGS_LOCATION +1; - } - else if (3 == type) { - _sectorStart = SETTINGS_LOCATION - CFG_ROTATES; - _sectorEnd = ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " %d " D_UNIT_SECTORS), _sectorEnd - _sectorStart); - - - EsptoolErase(_sectorStart, _sectorEnd); -#endif -} - -void SettingsSdkErase(void) -{ - WiFi.disconnect(true); - SettingsErase(1); - delay(1000); -} - - - -void SettingsDefault(void) -{ - AddLog_P(LOG_LEVEL_NONE, PSTR(D_LOG_CONFIG D_USE_DEFAULTS)); - SettingsDefaultSet1(); - SettingsDefaultSet2(); - SettingsSave(2); -} - -void SettingsDefaultSet1(void) -{ - memset(&Settings, 0x00, sizeof(SYSCFG)); - - Settings.cfg_holder = (uint16_t)CFG_HOLDER; - Settings.cfg_size = sizeof(SYSCFG); - - Settings.version = VERSION; - - -} - -void SettingsDefaultSet2(void) -{ - memset((char*)&Settings +16, 0x00, sizeof(SYSCFG) -16); - - Settings.flag.stop_flash_rotate = APP_FLASH_CYCLE; - Settings.flag.global_state = APP_ENABLE_LEDLINK; - Settings.flag3.sleep_normal = APP_NORMAL_SLEEP; - Settings.flag3.no_power_feedback = APP_NO_RELAY_SCAN; - Settings.flag3.fast_power_cycle_disable = APP_DISABLE_POWERCYCLE; - Settings.flag3.bootcount_update = DEEPSLEEP_BOOTCOUNT; - Settings.flag3.compatibility_check = OTA_COMPATIBILITY; - Settings.save_data = SAVE_DATA; - Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY; - Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; - Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; - Settings.sleep = APP_SLEEP; - if (Settings.sleep < 50) { - Settings.sleep = 50; - } - - - - Settings.interlock[0] = 0xFF; - Settings.module = MODULE; - ModuleDefault(WEMOS); - - SettingsUpdateText(SET_FRIENDLYNAME1, FRIENDLY_NAME); - SettingsUpdateText(SET_FRIENDLYNAME2, FRIENDLY_NAME"2"); - SettingsUpdateText(SET_FRIENDLYNAME3, FRIENDLY_NAME"3"); - SettingsUpdateText(SET_FRIENDLYNAME4, FRIENDLY_NAME"4"); - SettingsUpdateText(SET_OTAURL, OTA_URL); - - - Settings.flag.save_state = SAVE_STATE; - Settings.power = APP_POWER; - Settings.poweronstate = APP_POWERON_STATE; - Settings.blinktime = APP_BLINKTIME; - Settings.blinkcount = APP_BLINKCOUNT; - Settings.ledstate = APP_LEDSTATE; - Settings.ledmask = APP_LEDMASK; - Settings.pulse_timer[0] = APP_PULSETIME; - - - - Settings.serial_config = TS_SERIAL_8N1; - Settings.baudrate = APP_BAUDRATE / 300; - Settings.sbaudrate = SOFT_BAUDRATE / 300; - Settings.serial_delimiter = 0xff; - Settings.seriallog_level = SERIAL_LOG_LEVEL; - - - Settings.flag3.use_wifi_scan = WIFI_SCAN_AT_RESTART; - Settings.flag3.use_wifi_rescan = WIFI_SCAN_REGULARLY; - Settings.wifi_output_power = 170; - ParseIp(&Settings.ip_address[0], WIFI_IP_ADDRESS); - ParseIp(&Settings.ip_address[1], WIFI_GATEWAY); - ParseIp(&Settings.ip_address[2], WIFI_SUBNETMASK); - ParseIp(&Settings.ip_address[3], WIFI_DNS); - Settings.sta_config = WIFI_CONFIG_TOOL; - - SettingsUpdateText(SET_STASSID1, STA_SSID1); - SettingsUpdateText(SET_STASSID2, STA_SSID2); - SettingsUpdateText(SET_STAPWD1, STA_PASS1); - SettingsUpdateText(SET_STAPWD2, STA_PASS2); - SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); - - - SettingsUpdateText(SET_SYSLOG_HOST, SYS_LOG_HOST); - Settings.syslog_port = SYS_LOG_PORT; - Settings.syslog_level = SYS_LOG_LEVEL; - - - Settings.flag2.emulation = EMULATION; - Settings.flag3.gui_hostname_ip = GUI_SHOW_HOSTNAME; - Settings.flag3.mdns_enabled = MDNS_ENABLED; - Settings.webserver = WEB_SERVER; - Settings.weblog_level = WEB_LOG_LEVEL; - SettingsUpdateText(SET_WEBPWD, WEB_PASSWORD); - SettingsUpdateText(SET_CORS, CORS_DOMAIN); - - - Settings.flag.button_restrict = KEY_DISABLE_MULTIPRESS; - Settings.flag.button_swap = KEY_SWAP_DOUBLE_PRESS; - Settings.flag.button_single = KEY_ONLY_SINGLE_PRESS; - Settings.param[P_HOLD_TIME] = KEY_HOLD_TIME; - - - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { Settings.switchmode[i] = SWITCH_MODE; } - - - Settings.flag.mqtt_enabled = MQTT_USE; - Settings.flag.mqtt_response = MQTT_RESULT_COMMAND; - Settings.flag.mqtt_offline = MQTT_LWT_MESSAGE; - 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.mqtt_sensor_retain = MQTT_SENSOR_RETAIN; - - Settings.flag.device_index_enable = MQTT_POWER_FORMAT; - Settings.flag3.time_append_timezone = MQTT_APPEND_TIMEZONE; - Settings.flag3.button_switch_force_local = MQTT_BUTTON_SWITCH_FORCE_LOCAL; - Settings.flag3.no_hold_retain = MQTT_NO_HOLD_RETAIN; - Settings.flag3.use_underscore = MQTT_INDEX_SEPARATOR; - Settings.flag3.grouptopic_mode = MQTT_GROUPTOPIC_FORMAT; - SettingsUpdateText(SET_MQTT_HOST, MQTT_HOST); - Settings.mqtt_port = MQTT_PORT; - SettingsUpdateText(SET_MQTT_CLIENT, MQTT_CLIENT_ID); - SettingsUpdateText(SET_MQTT_USER, MQTT_USER); - SettingsUpdateText(SET_MQTT_PWD, MQTT_PASS); - SettingsUpdateText(SET_MQTT_TOPIC, MQTT_TOPIC); - SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC); - SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); - SettingsUpdateText(SET_MQTT_GRP_TOPIC, MQTT_GRPTOPIC); - SettingsUpdateText(SET_MQTT_FULLTOPIC, MQTT_FULLTOPIC); - Settings.mqtt_retry = MQTT_RETRY_SECS; - SettingsUpdateText(SET_MQTTPREFIX1, SUB_PREFIX); - SettingsUpdateText(SET_MQTTPREFIX2, PUB_PREFIX); - SettingsUpdateText(SET_MQTTPREFIX3, PUB_PREFIX2); - SettingsUpdateText(SET_STATE_TXT1, MQTT_STATUS_OFF); - SettingsUpdateText(SET_STATE_TXT2, MQTT_STATUS_ON); - SettingsUpdateText(SET_STATE_TXT3, MQTT_CMND_TOGGLE); - SettingsUpdateText(SET_STATE_TXT4, MQTT_CMND_HOLD); - char fingerprint[60]; - strlcpy(fingerprint, MQTT_FINGERPRINT1, sizeof(fingerprint)); - char *p = fingerprint; - for (uint32_t i = 0; i < 20; i++) { - Settings.mqtt_fingerprint[0][i] = strtol(p, &p, 16); - } - strlcpy(fingerprint, MQTT_FINGERPRINT2, sizeof(fingerprint)); - p = fingerprint; - for (uint32_t i = 0; i < 20; i++) { - Settings.mqtt_fingerprint[1][i] = strtol(p, &p, 16); - } - Settings.tele_period = TELE_PERIOD; - Settings.mqttlog_level = MQTT_LOG_LEVEL; - - - Settings.flag.no_power_on_check = ENERGY_VOLTAGE_ALWAYS; - Settings.flag2.current_resolution = 3; - - - Settings.flag2.energy_resolution = ENERGY_RESOLUTION; - Settings.flag3.dds2382_model = ENERGY_DDS2382_MODE; - Settings.flag3.hardware_energy_total = ENERGY_HARDWARE_TOTALS; - Settings.param[P_MAX_POWER_RETRY] = MAX_POWER_RETRY; - - Settings.energy_power_calibration = HLW_PREF_PULSE; - Settings.energy_voltage_calibration = HLW_UREF_PULSE; - Settings.energy_current_calibration = HLW_IREF_PULSE; -# 944 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" - Settings.energy_max_power_limit_hold = MAX_POWER_HOLD; - Settings.energy_max_power_limit_window = MAX_POWER_WINDOW; - - Settings.energy_max_power_safe_limit_hold = SAFE_POWER_HOLD; - Settings.energy_max_power_safe_limit_window = SAFE_POWER_WINDOW; - - - - RtcSettings.energy_kWhtotal = 0; - - memset((char*)&RtcSettings.energy_usage, 0x00, sizeof(RtcSettings.energy_usage)); - Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP; - - - Settings.flag.ir_receive_decimal = IR_DATA_RADIX; - Settings.flag3.receive_raw = IR_ADD_RAW_DATA; - Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; - - - Settings.flag.rf_receive_decimal = RF_DATA_RADIX; - - memcpy_P(Settings.rf_code[0], kDefaultRfCode, 9); - - - Settings.domoticz_update_timer = DOMOTICZ_UPDATE_TIMER; -# 979 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" - Settings.flag.temperature_conversion = TEMP_CONVERSION; - Settings.flag.pressure_conversion = PRESSURE_CONVERSION; - Settings.flag2.pressure_resolution = PRESSURE_RESOLUTION; - Settings.flag2.humidity_resolution = HUMIDITY_RESOLUTION; - Settings.flag2.temperature_resolution = TEMP_RESOLUTION; - Settings.flag3.ds18x20_internal_pullup = DS18X20_PULL_UP; - Settings.flag3.counter_reset_on_tele = COUNTER_RESET; - - - - - - - Settings.flag2.calc_resolution = CALC_RESOLUTION; - - - Settings.flag3.timers_enable = TIMERS_ENABLED; - - - Settings.flag.hass_light = HASS_AS_LIGHT; - Settings.flag.hass_discovery = HOME_ASSISTANT_DISCOVERY_ENABLE; - Settings.flag3.hass_tele_on_power = TELE_ON_POWER; - - - Settings.flag.knx_enabled = KNX_ENABLED; - Settings.flag.knx_enable_enhancement = KNX_ENHANCED; - - - Settings.flag.pwm_control = LIGHT_MODE; - Settings.flag.ws_clock_reverse = LIGHT_CLOCK_DIRECTION; - Settings.flag.light_signal = LIGHT_PAIRS_CO2; - Settings.flag.not_power_linked = LIGHT_POWER_CONTROL; - Settings.flag.decimal_text = LIGHT_COLOR_RADIX; - Settings.flag3.pwm_multi_channels = LIGHT_CHANNEL_MODE; - Settings.flag3.slider_dimmer_stay_on = LIGHT_SLIDER_POWER; - Settings.flag4.alexa_ct_range = LIGHT_ALEXA_CT_RANGE; - - Settings.pwm_frequency = PWM_FREQ; - Settings.pwm_range = PWM_RANGE; - for (uint32_t i = 0; i < MAX_PWMS; i++) { - Settings.light_color[i] = DEFAULT_LIGHT_COMPONENT; - - } - Settings.light_correction = 1; - Settings.light_dimmer = DEFAULT_LIGHT_DIMMER; - - Settings.light_speed = 1; - - Settings.light_width = 1; - - Settings.light_pixels = WS2812_LEDS; - - Settings.ws_width[WS_SECOND] = 1; - Settings.ws_color[WS_SECOND][WS_RED] = 255; - - Settings.ws_color[WS_SECOND][WS_BLUE] = 255; - Settings.ws_width[WS_MINUTE] = 3; - - Settings.ws_color[WS_MINUTE][WS_GREEN] = 255; - - Settings.ws_width[WS_HOUR] = 5; - Settings.ws_color[WS_HOUR][WS_RED] = 255; - - - - Settings.dimmer_hw_max = DEFAULT_DIMMER_MAX; - Settings.dimmer_hw_min = DEFAULT_DIMMER_MIN; - - - - Settings.display_mode = 1; - Settings.display_refresh = 2; - Settings.display_rows = 2; - Settings.display_cols[0] = 16; - Settings.display_cols[1] = 8; - Settings.display_dimmer = 1; - Settings.display_size = 1; - Settings.display_font = 1; - - Settings.display_address[0] = MTX_ADDRESS1; - Settings.display_address[1] = MTX_ADDRESS2; - Settings.display_address[2] = MTX_ADDRESS3; - Settings.display_address[3] = MTX_ADDRESS4; - Settings.display_address[4] = MTX_ADDRESS5; - Settings.display_address[5] = MTX_ADDRESS6; - Settings.display_address[6] = MTX_ADDRESS7; - Settings.display_address[7] = MTX_ADDRESS8; - - - if (((APP_TIMEZONE > -14) && (APP_TIMEZONE < 15)) || (99 == APP_TIMEZONE)) { - Settings.timezone = APP_TIMEZONE; - Settings.timezone_minutes = 0; - } else { - Settings.timezone = APP_TIMEZONE / 60; - Settings.timezone_minutes = abs(APP_TIMEZONE % 60); - } - SettingsUpdateText(SET_NTPSERVER1, NTP_SERVER1); - SettingsUpdateText(SET_NTPSERVER2, NTP_SERVER2); - SettingsUpdateText(SET_NTPSERVER3, NTP_SERVER3); - for (uint32_t i = 0; i < MAX_NTP_SERVERS; i++) { - SettingsUpdateText(SET_NTPSERVER1 +i, ReplaceCommaWithDot(SettingsText(SET_NTPSERVER1 +i))); - } - Settings.latitude = (int)((double)LATITUDE * 1000000); - Settings.longitude = (int)((double)LONGITUDE * 1000000); - SettingsResetStd(); - SettingsResetDst(); - - Settings.button_debounce = KEY_DEBOUNCE_TIME; - Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; - - for (uint32_t j = 0; j < 5; j++) { - Settings.rgbwwTable[j] = 255; - } - - Settings.novasds_startingoffset = STARTING_OFFSET; - - SettingsDefaultWebColor(); - - memset(&Settings.monitors, 0xFF, 20); - SettingsEnableAllI2cDrivers(); - - - Settings.flag3.tuya_apply_o20 = TUYA_SETOPTION_20; - Settings.flag3.tuya_serial_mqtt_publish = MQTT_TUYA_RECEIVED; - - Settings.flag3.buzzer_enable = BUZZER_ENABLE; - Settings.flag3.shutter_mode = SHUTTER_SUPPORT; - Settings.flag3.pcf8574_ports_inverted = PCF8574_INVERT_PORTS; - Settings.flag4.zigbee_use_names = ZIGBEE_FRIENDLY_NAMES; -} - - - -void SettingsResetStd(void) -{ - Settings.tflag[0].hemis = TIME_STD_HEMISPHERE; - Settings.tflag[0].week = TIME_STD_WEEK; - Settings.tflag[0].dow = TIME_STD_DAY; - Settings.tflag[0].month = TIME_STD_MONTH; - Settings.tflag[0].hour = TIME_STD_HOUR; - Settings.toffset[0] = TIME_STD_OFFSET; -} - -void SettingsResetDst(void) -{ - Settings.tflag[1].hemis = TIME_DST_HEMISPHERE; - Settings.tflag[1].week = TIME_DST_WEEK; - Settings.tflag[1].dow = TIME_DST_DAY; - Settings.tflag[1].month = TIME_DST_MONTH; - Settings.tflag[1].hour = TIME_DST_HOUR; - Settings.toffset[1] = TIME_DST_OFFSET; -} - -void SettingsDefaultWebColor(void) -{ - char scolor[10]; - for (uint32_t i = 0; i < COL_LAST; i++) { - WebHexCode(i, GetTextIndexed(scolor, sizeof(scolor), i, kWebColors)); - } -} - -void SettingsEnableAllI2cDrivers(void) -{ - Settings.i2c_drivers[0] = 0xFFFFFFFF; - Settings.i2c_drivers[1] = 0xFFFFFFFF; - Settings.i2c_drivers[2] = 0xFFFFFFFF; -} - - - -void SettingsDelta(void) -{ - if (Settings.version != VERSION) { - if (Settings.version < 0x06000000) { - Settings.cfg_size = sizeof(SYSCFG); - Settings.cfg_crc = GetSettingsCrc(); - } - if (Settings.version < 0x06000002) { - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - if (i < 4) { - Settings.switchmode[i] = Settings.interlock[i]; - } else { - Settings.switchmode[i] = SWITCH_MODE; - } - } - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if (Settings.my_gp.io[i] >= GPIO_SWT5) { - Settings.my_gp.io[i] += 4; - } - } - } - if (Settings.version < 0x06000003) { - Settings.flag.mqtt_serial_raw = 0; - Settings.flag.pressure_conversion = 0; - Settings.flag3.data = 0; - } - if (Settings.version < 0x06010103) { - Settings.flag3.timers_enable = 1; - } - if (Settings.version < 0x0601010C) { - Settings.button_debounce = KEY_DEBOUNCE_TIME; - Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; - } - if (Settings.version < 0x0602010A) { - for (uint32_t j = 0; j < 5; j++) { - Settings.rgbwwTable[j] = 255; - } - } - if (Settings.version < 0x06030002) { - Settings.timezone_minutes = 0; - } - if (Settings.version < 0x06030004) { - memset(&Settings.monitors, 0xFF, 20); - } - if (Settings.version < 0x0603000E) { - Settings.flag2.calc_resolution = CALC_RESOLUTION; - } - if (Settings.version < 0x0603000F) { - if (Settings.sleep < 50) { - Settings.sleep = 50; - } - } - if (Settings.version < 0x06040105) { - Settings.flag3.mdns_enabled = MDNS_ENABLED; - Settings.param[P_MDNS_DELAYED_START] = 0; - } - if (Settings.version < 0x0604010B) { - Settings.interlock[0] = 0xFF; - for (uint32_t i = 1; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } - } - if (Settings.version < 0x0604010D) { - Settings.param[P_BOOT_LOOP_OFFSET] = BOOT_LOOP_OFFSET; - } - if (Settings.version < 0x06040110) { - ModuleDefault(WEMOS); - } - if (Settings.version < 0x06040113) { - Settings.param[P_RGB_REMAP] = RGB_REMAP_RGBW; - } - if (Settings.version < 0x06050003) { - Settings.novasds_startingoffset = STARTING_OFFSET; - } - if (Settings.version < 0x06050006) { - SettingsDefaultWebColor(); - } - if (Settings.version < 0x06050007) { - Settings.ledmask = APP_LEDMASK; - } - if (Settings.version < 0x0605000A) { - Settings.my_adc0 = ADC0_NONE; - } - if (Settings.version < 0x0605000D) { - Settings.param[P_IR_UNKNOW_THRESHOLD] = IR_RCV_MIN_UNKNOWN_SIZE; - } - if (Settings.version < 0x06060001) { - Settings.param[P_OVER_TEMP] = ENERGY_OVERTEMP; - } - if (Settings.version < 0x06060007) { - memset((char*)&Settings +0xE00, 0x00, sizeof(SYSCFG) -0xE00); - } - if (Settings.version < 0x06060008) { - - if (Settings.flag3.tuya_serial_mqtt_publish) { - Settings.param[P_ex_DIMMER_MAX] = 100; - } else { - Settings.param[P_ex_DIMMER_MAX] = 255; - } - } - if (Settings.version < 0x06060009) { - Settings.baudrate = APP_BAUDRATE / 300; - Settings.sbaudrate = SOFT_BAUDRATE / 300; - } - if (Settings.version < 0x0606000A) { - uint8_t tuyaindex = 0; - if (Settings.param[P_BACKLOG_DELAY] > 0) { - Settings.tuya_fnid_map[tuyaindex].fnid = 21; - Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_BACKLOG_DELAY]; - tuyaindex++; - } else if (Settings.flag3.fast_power_cycle_disable == 1) { - Settings.tuya_fnid_map[tuyaindex].fnid = 11; - Settings.tuya_fnid_map[tuyaindex].dpid = 1; - tuyaindex++; - } - if (Settings.param[P_ex_TUYA_RELAYS] > 0) { - for (uint8_t i = 0 ; i < Settings.param[P_ex_TUYA_RELAYS]; i++) { - Settings.tuya_fnid_map[tuyaindex].fnid = 12 + i; - Settings.tuya_fnid_map[tuyaindex].dpid = i + 2; - tuyaindex++; - } - } - if (Settings.param[P_ex_TUYA_POWER_ID] > 0) { - Settings.tuya_fnid_map[tuyaindex].fnid = 31; - Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_POWER_ID]; - tuyaindex++; - } - if (Settings.param[P_ex_TUYA_VOLTAGE_ID] > 0) { - Settings.tuya_fnid_map[tuyaindex].fnid = 33; - Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_VOLTAGE_ID]; - tuyaindex++; - } - if (Settings.param[P_ex_TUYA_CURRENT_ID] > 0) { - Settings.tuya_fnid_map[tuyaindex].fnid = 32; - Settings.tuya_fnid_map[tuyaindex].dpid = Settings.param[P_ex_TUYA_CURRENT_ID]; - } - } - if (Settings.version < 0x0606000C) { - memset((char*)&Settings +0x1D6, 0x00, 16); - } - if (Settings.version < 0x0606000F) { - Settings.ex_shutter_accuracy = 0; - Settings.ex_mqttlog_level = MQTT_LOG_LEVEL; - } - if (Settings.version < 0x06060011) { - Settings.param[P_BACKLOG_DELAY] = MIN_BACKLOG_DELAY; - } - if (Settings.version < 0x06060012) { - Settings.dimmer_hw_min = DEFAULT_DIMMER_MIN; - Settings.dimmer_hw_max = DEFAULT_DIMMER_MAX; - if (TUYA_DIMMER == Settings.module) { - if (Settings.flag3.ex_tuya_dimmer_min_limit) { - Settings.dimmer_hw_min = 25; - } else { - Settings.dimmer_hw_min = 1; - } - Settings.dimmer_hw_max = Settings.param[P_ex_DIMMER_MAX]; - } - else if (PS_16_DZ == Settings.module) { - Settings.dimmer_hw_min = 10; - Settings.dimmer_hw_max = Settings.param[P_ex_DIMMER_MAX]; - } - } - if (Settings.version < 0x06060014) { -# 1323 "C:/shared/sonoff/Git/Tasmota/tasmota/settings.ino" - Settings.flag3.fast_power_cycle_disable = 0; - Settings.energy_power_delta = Settings.ex_energy_power_delta; - Settings.ex_energy_power_delta = 0; - } - if (Settings.version < 0x06060015) { - if ((EX_WIFI_SMARTCONFIG == Settings.ex_sta_config) || (EX_WIFI_WPSCONFIG == Settings.ex_sta_config)) { - Settings.ex_sta_config = WIFI_MANAGER; - } - } - - if (Settings.version < 0x07000002) { - Settings.web_color2[0][0] = Settings.web_color[0][0]; - Settings.web_color2[0][1] = Settings.web_color[0][1]; - Settings.web_color2[0][2] = Settings.web_color[0][2]; - } - if (Settings.version < 0x07000003) { - SettingsEnableAllI2cDrivers(); - } - if (Settings.version < 0x07000004) { - Settings.ex_wifi_output_power = 170; - } - if (Settings.version < 0x07010202) { - Settings.ex_serial_config = TS_SERIAL_8N1; - } - if (Settings.version < 0x07010204) { - if (Settings.flag3.ex_cors_enabled == 1) { - strlcpy(Settings.ex_cors_domain, CORS_ENABLED_ALL, sizeof(Settings.ex_cors_domain)); - } else { - Settings.ex_cors_domain[0] = 0; - } - } - if (Settings.version < 0x07010205) { - Settings.seriallog_level = Settings.ex_seriallog_level; - Settings.sta_config = Settings.ex_sta_config; - Settings.sta_active = Settings.ex_sta_active; - memcpy((char*)&Settings.rule_stop, (char*)&Settings.ex_rule_stop, 47); - } - if (Settings.version < 0x07010206) { - Settings.flag4 = Settings.ex_flag4; - Settings.mqtt_port = Settings.ex_mqtt_port; - memcpy((char*)&Settings.serial_config, (char*)&Settings.ex_serial_config, 5); - } - - if (Settings.version < 0x08000000) { - char temp[strlen(Settings.text_pool) +1]; strncpy(temp, Settings.text_pool, sizeof(temp)); - char temp21[strlen(Settings.ex_mqtt_prefix[0]) +1]; strncpy(temp21, Settings.ex_mqtt_prefix[0], sizeof(temp21)); - char temp22[strlen(Settings.ex_mqtt_prefix[1]) +1]; strncpy(temp22, Settings.ex_mqtt_prefix[1], sizeof(temp22)); - char temp23[strlen(Settings.ex_mqtt_prefix[2]) +1]; strncpy(temp23, Settings.ex_mqtt_prefix[2], sizeof(temp23)); - char temp31[strlen(Settings.ex_sta_ssid[0]) +1]; strncpy(temp31, Settings.ex_sta_ssid[0], sizeof(temp31)); - char temp32[strlen(Settings.ex_sta_ssid[1]) +1]; strncpy(temp32, Settings.ex_sta_ssid[1], sizeof(temp32)); - char temp41[strlen(Settings.ex_sta_pwd[0]) +1]; strncpy(temp41, Settings.ex_sta_pwd[0], sizeof(temp41)); - char temp42[strlen(Settings.ex_sta_pwd[1]) +1]; strncpy(temp42, Settings.ex_sta_pwd[1], sizeof(temp42)); - char temp5[strlen(Settings.ex_hostname) +1]; strncpy(temp5, Settings.ex_hostname, sizeof(temp5)); - char temp6[strlen(Settings.ex_syslog_host) +1]; strncpy(temp6, Settings.ex_syslog_host, sizeof(temp6)); - char temp7[strlen(Settings.ex_mqtt_host) +1]; strncpy(temp7, Settings.ex_mqtt_host, sizeof(temp7)); - char temp8[strlen(Settings.ex_mqtt_client) +1]; strncpy(temp8, Settings.ex_mqtt_client, sizeof(temp8)); - char temp9[strlen(Settings.ex_mqtt_user) +1]; strncpy(temp9, Settings.ex_mqtt_user, sizeof(temp9)); - char temp10[strlen(Settings.ex_mqtt_pwd) +1]; strncpy(temp10, Settings.ex_mqtt_pwd, sizeof(temp10)); - char temp11[strlen(Settings.ex_mqtt_topic) +1]; strncpy(temp11, Settings.ex_mqtt_topic, sizeof(temp11)); - char temp12[strlen(Settings.ex_button_topic) +1]; strncpy(temp12, Settings.ex_button_topic, sizeof(temp12)); - char temp13[strlen(Settings.ex_mqtt_grptopic) +1]; strncpy(temp13, Settings.ex_mqtt_grptopic, sizeof(temp13)); - - memset(Settings.text_pool, 0x00, settings_text_size); - SettingsUpdateText(SET_OTAURL, temp); - SettingsUpdateText(SET_MQTTPREFIX1, temp21); - SettingsUpdateText(SET_MQTTPREFIX2, temp22); - SettingsUpdateText(SET_MQTTPREFIX3, temp23); - SettingsUpdateText(SET_STASSID1, temp31); - SettingsUpdateText(SET_STASSID2, temp32); - SettingsUpdateText(SET_STAPWD1, temp41); - SettingsUpdateText(SET_STAPWD2, temp42); - SettingsUpdateText(SET_HOSTNAME, temp5); - SettingsUpdateText(SET_SYSLOG_HOST, temp6); -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - if (!strlen(Settings.ex_mqtt_user)) { - SettingsUpdateText(SET_MQTT_HOST, temp7); - SettingsUpdateText(SET_MQTT_USER, temp9); - } else { - char aws_mqtt_host[66]; - snprintf_P(aws_mqtt_host, sizeof(aws_mqtt_host), PSTR("%s%s"), temp9, temp7); - SettingsUpdateText(SET_MQTT_HOST, aws_mqtt_host); - SettingsUpdateText(SET_MQTT_USER, ""); - } -#else - SettingsUpdateText(SET_MQTT_HOST, temp7); - SettingsUpdateText(SET_MQTT_USER, temp9); -#endif - SettingsUpdateText(SET_MQTT_CLIENT, temp8); - SettingsUpdateText(SET_MQTT_PWD, temp10); - SettingsUpdateText(SET_MQTT_TOPIC, temp11); - SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, temp12); - SettingsUpdateText(SET_MQTT_GRP_TOPIC, temp13); - - SettingsUpdateText(SET_WEBPWD, Settings.ex_web_password); - SettingsUpdateText(SET_CORS, Settings.ex_cors_domain); - SettingsUpdateText(SET_MQTT_FULLTOPIC, Settings.ex_mqtt_fulltopic); - SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, Settings.ex_switch_topic); - SettingsUpdateText(SET_STATE_TXT1, Settings.ex_state_text[0]); - SettingsUpdateText(SET_STATE_TXT2, Settings.ex_state_text[1]); - SettingsUpdateText(SET_STATE_TXT3, Settings.ex_state_text[2]); - SettingsUpdateText(SET_STATE_TXT4, Settings.ex_state_text[3]); - SettingsUpdateText(SET_NTPSERVER1, Settings.ex_ntp_server[0]); - SettingsUpdateText(SET_NTPSERVER2, Settings.ex_ntp_server[1]); - SettingsUpdateText(SET_NTPSERVER3, Settings.ex_ntp_server[2]); - SettingsUpdateText(SET_MEM1, Settings.script_pram[0]); - SettingsUpdateText(SET_MEM2, Settings.script_pram[1]); - SettingsUpdateText(SET_MEM3, Settings.script_pram[2]); - SettingsUpdateText(SET_MEM4, Settings.script_pram[3]); - SettingsUpdateText(SET_MEM5, Settings.script_pram[4]); - SettingsUpdateText(SET_FRIENDLYNAME1, Settings.ex_friendlyname[0]); - SettingsUpdateText(SET_FRIENDLYNAME2, Settings.ex_friendlyname[1]); - SettingsUpdateText(SET_FRIENDLYNAME3, Settings.ex_friendlyname[2]); - SettingsUpdateText(SET_FRIENDLYNAME4, Settings.ex_friendlyname[3]); - } - - Settings.version = VERSION; - SettingsSave(1); - } -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support.ino" -IPAddress syslog_host_addr; -uint32_t syslog_host_hash = 0; - -extern "C" { -extern struct rst_info resetInfo; -} - - - - - -#include - -Ticker tickerOSWatch; - -const uint32_t OSWATCH_RESET_TIME = 120; - -static unsigned long oswatch_last_loop_time; -uint8_t oswatch_blocked_loop = 0; - -#ifndef USE_WS2812_DMA - -#endif - -#ifdef USE_KNX -bool knx_started = false; -#endif - -void OsWatchTicker(void) -{ - uint32_t t = millis(); - uint32_t last_run = abs(t - oswatch_last_loop_time); - -#ifdef DEBUG_THEO - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d %% (%d dBm), last_run %d"), ESP.getFreeHeap(), WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI(), last_run); -#endif - if (last_run >= (OSWATCH_RESET_TIME * 1000)) { - - RtcSettings.oswatch_blocked_loop = 1; - RtcSettingsSave(); - - - - - - volatile uint32_t dummy; - dummy = *((uint32_t*) 0x00000000); - } -} - -void OsWatchInit(void) -{ - oswatch_blocked_loop = RtcSettings.oswatch_blocked_loop; - RtcSettings.oswatch_blocked_loop = 0; - oswatch_last_loop_time = millis(); - tickerOSWatch.attach_ms(((OSWATCH_RESET_TIME / 3) * 1000), OsWatchTicker); -} - -void OsWatchLoop(void) -{ - oswatch_last_loop_time = millis(); - -} - -bool OsWatchBlockedLoop(void) -{ - return oswatch_blocked_loop; -} - -uint32_t ResetReason(void) -{ -# 101 "C:/shared/sonoff/Git/Tasmota/tasmota/support.ino" - return resetInfo.reason; -} - -String GetResetReason(void) -{ - if (oswatch_blocked_loop) { - char buff[32]; - strncpy_P(buff, PSTR(D_JSON_BLOCKED_LOOP), sizeof(buff)); - return String(buff); - } else { - return ESP.getResetReason(); - } -} - - - - - - -size_t strchrspn(const char *str1, int character) -{ - size_t ret = 0; - char *start = (char*)str1; - char *end = strchr(str1, character); - if (end) ret = end - start; - return ret; -} - - -char* subStr(char* dest, char* str, const char *delim, int index) -{ - char *act; - char *sub = nullptr; - char *ptr; - int i; - - - strncpy(dest, str, strlen(str)+1); - for (i = 1, act = dest; i <= index; i++, act = nullptr) { - sub = strtok_r(act, delim, &ptr); - if (sub == nullptr) break; - } - sub = Trim(sub); - return sub; -} - -float CharToFloat(const char *str) -{ - - char strbuf[24]; - - strlcpy(strbuf, str, sizeof(strbuf)); - char *pt = strbuf; - while ((*pt != '\0') && isblank(*pt)) { pt++; } - - signed char sign = 1; - if (*pt == '-') { sign = -1; } - if (*pt == '-' || *pt=='+') { pt++; } - - float left = 0; - if (*pt != '.') { - left = atoi(pt); - while (isdigit(*pt)) { pt++; } - } - - float right = 0; - if (*pt == '.') { - pt++; - right = atoi(pt); - while (isdigit(*pt)) { - pt++; - right /= 10.0f; - } - } - - float result = left + right; - if (sign < 0) { - return -result; - } - return result; -} - -int TextToInt(char *str) -{ - char *p; - uint8_t radix = 10; - if ('#' == str[0]) { - radix = 16; - str++; - } - return strtol(str, &p, radix); -} - -char* ulltoa(unsigned long long value, char *str, int radix) -{ - char digits[64]; - char *dst = str; - int i = 0; - - - - do { - int n = value % radix; - digits[i++] = (n < 10) ? (char)n+'0' : (char)n-10+'A'; - value /= radix; - } while (value != 0); - - while (i > 0) { *dst++ = digits[--i]; } - - *dst = 0; - return str; -} - - - -char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, char inbetween) -{ - - - - static const char * hex = "0123456789ABCDEF"; - int between = (inbetween) ? 3 : 2; - const unsigned char * pin = in; - char * pout = out; - for (; pin < in+insz; pout += between, pin++) { - pout[0] = hex[(pgm_read_byte(pin)>>4) & 0xF]; - pout[1] = hex[ pgm_read_byte(pin) & 0xF]; - if (inbetween) { pout[2] = inbetween; } - if (pout + 3 - out > outsz) { break; } - } - pout[(inbetween && insz) ? -1 : 0] = 0; - return out; -} - -char* Uint64toHex(uint64_t value, char *str, uint16_t bits) -{ - ulltoa(value, str, 16); - - int fill = 8; - if ((bits > 3) && (bits < 65)) { - fill = bits / 4; - if (bits % 4) { fill++; } - } - int len = strlen(str); - fill -= len; - if (fill > 0) { - memmove(str + fill, str, len +1); - memset(str, '0', fill); - } - return str; -} - -char* dtostrfd(double number, unsigned char prec, char *s) -{ - if ((isnan(number)) || (isinf(number))) { - strcpy(s, "null"); - return s; - } else { - return dtostrf(number, 1, prec, s); - } -} - -char* Unescape(char* buffer, uint32_t* size) -{ - uint8_t* read = (uint8_t*)buffer; - uint8_t* write = (uint8_t*)buffer; - int32_t start_size = *size; - int32_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; - case 'a': che = '\a'; break; - case 'b': che = '\b'; break; - case 'e': che = '\e'; break; - case 'f': che = '\f'; break; - case 'n': che = '\n'; break; - case 'r': che = '\r'; break; - case 's': che = ' '; break; - case 't': che = '\t'; break; - case 'v': che = '\v'; break; - case 'x': { - uint8_t* start = read; - che = (uint8_t)strtol((const char*)read, (char**)&read, 16); - start_size -= (uint16_t)(read - start); - end_size -= (uint16_t)(read - start); - break; - } - case '"': che = '\"'; break; - - default : { - che = chi; - *write++ = ch; - end_size++; - } - } - *write++ = che; - } - } - } - *size = end_size; - *write++ = 0; - - - return buffer; -} - -char* RemoveSpace(char* p) -{ - char* write = p; - char* read = p; - char ch = '.'; - - while (ch != '\0') { - ch = *read++; - if (!isspace(ch)) { - *write++ = ch; - } - } - - return p; -} - -char* ReplaceCommaWithDot(char* p) -{ - char* write = (char*)p; - char* read = (char*)p; - char ch = '.'; - - while (ch != '\0') { - ch = *read++; - if (ch == ',') { - ch = '.'; - } - *write++ = ch; - } - return p; -} - -char* LowerCase(char* dest, const char* source) -{ - char* write = dest; - const char* read = source; - char ch = '.'; - - while (ch != '\0') { - ch = *read++; - *write++ = tolower(ch); - } - return dest; -} - -char* UpperCase(char* dest, const char* source) -{ - char* write = dest; - const char* read = source; - char ch = '.'; - - while (ch != '\0') { - ch = *read++; - *write++ = toupper(ch); - } - return dest; -} - -char* UpperCase_P(char* dest, const char* source) -{ - char* write = dest; - const char* read = source; - char ch = '.'; - - while (ch != '\0') { - ch = pgm_read_byte(read++); - *write++ = toupper(ch); - } - return dest; -} - -char* Trim(char* p) -{ - while ((*p != '\0') && isblank(*p)) { p++; } - char* q = p + strlen(p) -1; - while ((q >= p) && isblank(*q)) { q--; } - q++; - *q = '\0'; - return p; -} - -char* RemoveAllSpaces(char* p) -{ - - char *cursor = p; - uint32_t offset = 0; - while (1) { - *cursor = *(cursor + offset); - if ((' ' == *cursor) || ('\t' == *cursor) || ('\n' == *cursor)) { - offset++; - } else { - if (0 == *cursor) { break; } - cursor++; - } - } - return p; -} - -char* NoAlNumToUnderscore(char* dest, const char* source) -{ - char* write = dest; - const char* read = source; - char ch = '.'; - - while (ch != '\0') { - ch = *read++; - *write++ = (isalnum(ch) || ('\0' == ch)) ? ch : '_'; - } - return dest; -} - -char IndexSeparator(void) -{ - - - - - - - if (Settings.flag3.use_underscore) { - return '_'; - } else { - return '-'; - } -} - -void SetShortcutDefault(void) -{ - if ('\0' != XdrvMailbox.data[0]) { - XdrvMailbox.data[0] = '0' + SC_DEFAULT; - XdrvMailbox.data[1] = '\0'; - } -} - -uint8_t Shortcut(void) -{ - uint8_t result = 10; - - if ('\0' == XdrvMailbox.data[1]) { - if (('"' == XdrvMailbox.data[0]) || ('0' == XdrvMailbox.data[0])) { - result = SC_CLEAR; - } else { - result = atoi(XdrvMailbox.data); - if (0 == result) { - result = 10; - } - } - } - return result; -} - -bool ValidIpAddress(const char* str) -{ - const char* p = str; - - while (*p && ((*p == '.') || ((*p >= '0') && (*p <= '9')))) { p++; } - return (*p == '\0'); -} - -bool ParseIp(uint32_t* addr, const char* str) -{ - uint8_t *part = (uint8_t*)addr; - uint8_t i; - - *addr = 0; - for (i = 0; i < 4; i++) { - part[i] = strtoul(str, nullptr, 10); - str = strchr(str, '.'); - if (str == nullptr || *str == '\0') { - break; - } - str++; - } - return (3 == i); -} - -uint32_t ParseParameters(uint32_t count, uint32_t *params) -{ - char *p; - uint32_t i = 0; - for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < count; str = strtok_r(nullptr, ", ", &p), i++) { - params[i] = strtoul(str, nullptr, 0); - } - return i; -} - - -bool NewerVersion(char* version_str) -{ - uint32_t version = 0; - uint32_t i = 0; - char *str_ptr; - - char version_dup[strlen(version_str) +1]; - strncpy(version_dup, version_str, sizeof(version_dup)); - - for (char *str = strtok_r(version_dup, ".", &str_ptr); str && i < sizeof(VERSION); str = strtok_r(nullptr, ".", &str_ptr), i++) { - int field = atoi(str); - - if ((field < 0) || (field > 255)) { - return false; - } - - version = (version << 8) + field; - - if ((2 == i) && isalpha(str[strlen(str)-1])) { - field = str[strlen(str)-1] & 0x1f; - version = (version << 8) + field; - i++; - } - } - - - if ((i < 2) || (i > sizeof(VERSION))) { - return false; - } - - - while (i < sizeof(VERSION)) { - version <<= 8; - i++; - } - - return (version > VERSION); -} - -char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option) -{ - strncpy_P(dest, S_RSLT_POWER, size); - if ((devices_present + option) > 1) { - char sidx[8]; - snprintf_P(sidx, sizeof(sidx), PSTR("%d"), idx); - strncat(dest, sidx, size - strlen(dest) -1); - } - return dest; -} - -char* GetPowerDevice(char* dest, uint32_t idx, size_t size) -{ - return GetPowerDevice(dest, idx, size, 0); -} - -void GetEspHardwareType(void) -{ - - uint32_t efuse1 = *(uint32_t*)(0x3FF00050); - uint32_t efuse2 = *(uint32_t*)(0x3FF00054); - - - - is_8285 = ( (efuse1 & (1 << 4)) || (efuse2 & (1 << 16)) ); - if (is_8285 && (ESP.getFlashChipRealSize() > 1048576)) { - is_8285 = false; - } -} - -String GetDeviceHardware(void) -{ - char buff[10]; - if (is_8285) { - strcpy_P(buff, PSTR("ESP8285")); - } else { - strcpy_P(buff, PSTR("ESP8266EX")); - } - return String(buff); -} - -float ConvertTemp(float c) -{ - float result = c; - - global_update = uptime; - global_temperature = c; - - if (!isnan(c) && Settings.flag.temperature_conversion) { - result = c * 1.8 + 32; - } - result = result + (0.1 * Settings.temp_comp); - return result; -} - -float ConvertTempToCelsius(float c) -{ - float result = c; - - if (!isnan(c) && Settings.flag.temperature_conversion) { - result = (c - 32) / 1.8; - } - result = result + (0.1 * Settings.temp_comp); - return result; -} - -char TempUnit(void) -{ - return (Settings.flag.temperature_conversion) ? 'F' : 'C'; -} - -float ConvertHumidity(float h) -{ - global_update = uptime; - global_humidity = h; - - return h; -} - -float ConvertPressure(float p) -{ - float result = p; - - global_update = uptime; - global_pressure = p; - - if (!isnan(p) && Settings.flag.pressure_conversion) { - result = p * 0.75006375541921; - } - return result; -} - -String PressureUnit(void) -{ - return (Settings.flag.pressure_conversion) ? String(D_UNIT_MILLIMETER_MERCURY) : String(D_UNIT_PRESSURE); -} - -void ResetGlobalValues(void) -{ - if ((uptime - global_update) > GLOBAL_VALUES_VALID) { - global_update = 0; - global_temperature = 9999; - global_humidity = 0; - global_pressure = 0; - } -} - -uint32_t SqrtInt(uint32_t num) -{ - if (num <= 1) { - return num; - } - - uint32_t x = num / 2; - uint32_t y; - do { - y = (x + num / x) / 2; - if (y >= x) { - return x; - } - x = y; - } while (true); -} - -uint32_t RoundSqrtInt(uint32_t num) -{ - uint32_t s = SqrtInt(4 * num); - if (s & 1) { - s++; - } - return s / 2; -} - -char* GetTextIndexed(char* destination, size_t destination_size, uint32_t index, const char* haystack) -{ - - - char* write = destination; - const char* read = haystack; - - index++; - while (index--) { - size_t size = destination_size -1; - write = destination; - char ch = '.'; - while ((ch != '\0') && (ch != '|')) { - ch = pgm_read_byte(read++); - if (size && (ch != '|')) { - *write++ = ch; - size--; - } - } - if (0 == ch) { - if (index) { - write = destination; - } - break; - } - } - *write = '\0'; - return destination; -} - -int GetCommandCode(char* destination, size_t destination_size, const char* needle, const char* haystack) -{ - - - int result = -1; - const char* read = haystack; - char* write = destination; - - while (true) { - result++; - size_t size = destination_size -1; - write = destination; - char ch = '.'; - while ((ch != '\0') && (ch != '|')) { - ch = pgm_read_byte(read++); - if (size && (ch != '|')) { - *write++ = ch; - size--; - } - } - *write = '\0'; - if (!strcasecmp(needle, destination)) { - break; - } - if (0 == ch) { - result = -1; - break; - } - } - return result; -} - -bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void)) -{ - GetTextIndexed(XdrvMailbox.command, CMDSZ, 0, haystack); - int prefix_length = strlen(XdrvMailbox.command); - if (prefix_length) { - char prefix[prefix_length +1]; - snprintf_P(prefix, sizeof(prefix), XdrvMailbox.topic); - if (strcasecmp(prefix, XdrvMailbox.command)) { - return false; - } - } - int command_code = GetCommandCode(XdrvMailbox.command + prefix_length, CMDSZ, XdrvMailbox.topic + prefix_length, haystack); - if (command_code > 0) { - XdrvMailbox.command_code = command_code -1; - MyCommand[XdrvMailbox.command_code](); - return true; - } - return false; -} - -const char kOptions[] PROGMEM = "OFF|" D_OFF "|FALSE|" D_FALSE "|STOP|" D_STOP "|" D_CELSIUS "|" - "ON|" D_ON "|TRUE|" D_TRUE "|START|" D_START "|" D_FAHRENHEIT "|" D_USER "|" - "TOGGLE|" D_TOGGLE "|" D_ADMIN "|" - "BLINK|" D_BLINK "|" - "BLINKOFF|" D_BLINKOFF "|" - "ALL" ; - -const uint8_t sNumbers[] PROGMEM = { 0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1, - 2,2,2, - 3,3, - 4,4, - 255 }; - -int GetStateNumber(char *state_text) -{ - char command[CMDSZ]; - int state_number = GetCommandCode(command, sizeof(command), state_text, kOptions); - if (state_number >= 0) { - state_number = pgm_read_byte(sNumbers + state_number); - } - return state_number; -} - -String GetSerialConfig(void) -{ - - - - - - const char kParity[] = "NEOI"; - - char config[4]; - config[0] = '5' + (Settings.serial_config & 0x3); - config[1] = kParity[(Settings.serial_config >> 3) & 0x3]; - config[2] = '1' + ((Settings.serial_config >> 2) & 0x1); - config[3] = '\0'; - return String(config); -} - -void SetSerialBegin() -{ - uint32_t baudrate = Settings.baudrate * 300; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_SERIAL "Set to %s %d bit/s"), GetSerialConfig().c_str(), baudrate); - Serial.flush(); - Serial.begin(baudrate, (SerialConfig)pgm_read_byte(kTasmotaSerialConfig + Settings.serial_config)); -} - -void SetSerialConfig(uint32_t serial_config) -{ - if (serial_config > TS_SERIAL_8O2) { - serial_config = TS_SERIAL_8N1; - } - if (serial_config != Settings.serial_config) { - Settings.serial_config = serial_config; - SetSerialBegin(); - } -} - -void SetSerialBaudrate(uint32_t baudrate) -{ - Settings.baudrate = baudrate / 300; - if (Serial.baudRate() != baudrate) { - SetSerialBegin(); - } -} - -void SetSerial(uint32_t baudrate, uint32_t serial_config) -{ - Settings.flag.mqtt_serial = 0; - Settings.serial_config = serial_config; - Settings.baudrate = baudrate / 300; - SetSeriallog(LOG_LEVEL_NONE); - SetSerialBegin(); -} - -void ClaimSerial(void) -{ - serial_local = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("SNS: Hardware Serial")); - SetSeriallog(LOG_LEVEL_NONE); - Settings.baudrate = Serial.baudRate() / 300; -} - -void SerialSendRaw(char *codes) -{ - char *p; - char stemp[3]; - uint8_t code; - - int size = strlen(codes); - - while (size > 1) { - strlcpy(stemp, codes, sizeof(stemp)); - code = strtol(stemp, &p, 16); - Serial.write(code); - size -= 2; - codes += 2; - } -} - -uint32_t GetHash(const char *buffer, size_t size) -{ - uint32_t hash = 0; - for (uint32_t i = 0; i <= size; i++) { - hash += (uint8_t)*buffer++ * (i +1); - } - return hash; -} - -void ShowSource(uint32_t source) -{ - if ((source > 0) && (source < SRC_MAX)) { - char stemp1[20]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SRC: %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource)); - } -} - -void WebHexCode(uint32_t i, const char* code) -{ - char scolor[10]; - - strlcpy(scolor, code, sizeof(scolor)); - char* p = scolor; - if ('#' == p[0]) { p++; } - - if (3 == strlen(p)) { - p[6] = p[3]; - p[5] = p[2]; - p[4] = p[2]; - p[3] = p[1]; - p[2] = p[1]; - p[1] = p[0]; - } - - uint32_t color = strtol(p, nullptr, 16); - - - - - - - uint32_t j = sizeof(Settings.web_color) / 3; -# 916 "C:/shared/sonoff/Git/Tasmota/tasmota/support.ino" - if (i >= j) { - - i += ((((uint8_t*)&Settings.web_color2 - (uint8_t*)&Settings.web_color) / 3) - j); - } - Settings.web_color[i][0] = (color >> 16) & 0xFF; - Settings.web_color[i][1] = (color >> 8) & 0xFF; - Settings.web_color[i][2] = color & 0xFF; -} - -uint32_t WebColor(uint32_t i) -{ - uint32_t j = sizeof(Settings.web_color) / 3; - - - - - if (i >= j) { - - i += ((((uint8_t*)&Settings.web_color2 - (uint8_t*)&Settings.web_color) / 3) - j); - } - uint32_t tcolor = (Settings.web_color[i][0] << 16) | (Settings.web_color[i][1] << 8) | Settings.web_color[i][2]; - - return tcolor; -} - - - - - -const uint16_t TIMESZ = 100; - -char* ResponseGetTime(uint32_t format, char* time_str) -{ - switch (format) { - case 1: - snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%u"), GetDateAndTime(DT_LOCAL).c_str(), UtcTime()); - break; - case 2: - snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":%u"), UtcTime()); - break; - default: - snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str()); - } - return time_str; -} - -int Response_P(const char* format, ...) -{ - - va_list args; - va_start(args, format); - int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), format, args); - va_end(args); - return len; -} - -int ResponseTime_P(const char* format, ...) -{ - - va_list args; - va_start(args, format); - - ResponseGetTime(Settings.flag2.time_format, mqtt_data); - - int mlen = strlen(mqtt_data); - int len = vsnprintf_P(mqtt_data + mlen, sizeof(mqtt_data) - mlen, format, args); - va_end(args); - return len + mlen; -} - -int ResponseAppend_P(const char* format, ...) -{ - - va_list args; - va_start(args, format); - int mlen = strlen(mqtt_data); - int len = vsnprintf_P(mqtt_data + mlen, sizeof(mqtt_data) - mlen, format, args); - va_end(args); - return len + mlen; -} - -int ResponseAppendTimeFormat(uint32_t format) -{ - char time_str[TIMESZ]; - return ResponseAppend_P(ResponseGetTime(format, time_str)); -} - -int ResponseAppendTime(void) -{ - return ResponseAppendTimeFormat(Settings.flag2.time_format); -} - -int ResponseJsonEnd(void) -{ - return ResponseAppend_P(PSTR("}")); -} - -int ResponseJsonEndEnd(void) -{ - return ResponseAppend_P(PSTR("}}")); -} - - - - - -void DigitalWrite(uint32_t gpio_pin, uint32_t state) -{ - if (pin[gpio_pin] < 99) { - digitalWrite(pin[gpio_pin], state &1); - } -} - -uint8_t ModuleNr(void) -{ - - - return (USER_MODULE == Settings.module) ? 0 : Settings.module +1; -} - -bool ValidTemplateModule(uint32_t index) -{ - for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { - if (index == pgm_read_byte(kModuleNiceList + i)) { - return true; - } - } - return false; -} - -bool ValidModule(uint32_t index) -{ - if (index == USER_MODULE) { return true; } - return ValidTemplateModule(index); -} - -String AnyModuleName(uint32_t index) -{ - if (USER_MODULE == index) { - return String(Settings.user_template.name); - } else { - return FPSTR(kModules[index].name); - } -} - -String ModuleName(void) -{ - return AnyModuleName(Settings.module); -} - -void ModuleGpios(myio *gp) -{ - uint8_t *dest = (uint8_t *)gp; - memset(dest, GPIO_NONE, sizeof(myio)); - - uint8_t src[sizeof(mycfgio)]; - if (USER_MODULE == Settings.module) { - memcpy(&src, &Settings.user_template.gp, sizeof(mycfgio)); - } else { - memcpy_P(&src, &kModules[Settings.module].gp, sizeof(mycfgio)); - } - - - - - uint32_t j = 0; - for (uint32_t i = 0; i < sizeof(mycfgio); i++) { - if (6 == i) { j = 9; } - if (8 == i) { j = 12; } - dest[j] = src[i]; - j++; - } - - - -} - -gpio_flag ModuleFlag(void) -{ - gpio_flag flag; - - if (USER_MODULE == Settings.module) { - flag = Settings.user_template.flag; - } else { - memcpy_P(&flag, &kModules[Settings.module].flag, sizeof(gpio_flag)); - } - - return flag; -} - -void ModuleDefault(uint32_t module) -{ - if (USER_MODULE == module) { module = WEMOS; } - Settings.user_template_base = module; - memcpy_P(&Settings.user_template, &kModules[module], sizeof(mytmplt)); -} - -void SetModuleType(void) -{ - my_module_type = (USER_MODULE == Settings.module) ? Settings.user_template_base : Settings.module; -} - -bool FlashPin(uint32_t pin) -{ - return (((pin > 5) && (pin < 9)) || (11 == pin)); -} - -uint8_t ValidPin(uint32_t pin, uint32_t gpio) -{ - if (FlashPin(pin)) { - return GPIO_NONE; - } - - - if ((WEMOS == Settings.module) && !Settings.flag3.user_esp8285_enable) { - if ((pin == 9) || (pin == 10)) { - return GPIO_NONE; - } - } - - return gpio; -} - -bool ValidGPIO(uint32_t pin, uint32_t gpio) -{ - return (GPIO_USER == ValidPin(pin, gpio)); -} - -bool ValidAdc(void) -{ - gpio_flag flag = ModuleFlag(); - uint32_t template_adc0 = flag.data &15; - return (ADC0_USER == template_adc0); -} - -bool GetUsedInModule(uint32_t val, uint8_t *arr) -{ - int offset = 0; - - if (!val) { return false; } - - if ((val >= GPIO_KEY1) && (val < GPIO_KEY1 + MAX_KEYS)) { - offset = (GPIO_KEY1_NP - GPIO_KEY1); - } - if ((val >= GPIO_KEY1_NP) && (val < GPIO_KEY1_NP + MAX_KEYS)) { - offset = -(GPIO_KEY1_NP - GPIO_KEY1); - } - if ((val >= GPIO_KEY1_INV) && (val < GPIO_KEY1_INV + MAX_KEYS)) { - offset = -(GPIO_KEY1_INV - GPIO_KEY1); - } - if ((val >= GPIO_KEY1_INV_NP) && (val < GPIO_KEY1_INV_NP + MAX_KEYS)) { - offset = -(GPIO_KEY1_INV_NP - GPIO_KEY1); - } - - if ((val >= GPIO_SWT1) && (val < GPIO_SWT1 + MAX_SWITCHES)) { - offset = (GPIO_SWT1_NP - GPIO_SWT1); - } - if ((val >= GPIO_SWT1_NP) && (val < GPIO_SWT1_NP + MAX_SWITCHES)) { - offset = -(GPIO_SWT1_NP - GPIO_SWT1); - } - - if ((val >= GPIO_REL1) && (val < GPIO_REL1 + MAX_RELAYS)) { - offset = (GPIO_REL1_INV - GPIO_REL1); - } - if ((val >= GPIO_REL1_INV) && (val < GPIO_REL1_INV + MAX_RELAYS)) { - offset = -(GPIO_REL1_INV - GPIO_REL1); - } - - if ((val >= GPIO_LED1) && (val < GPIO_LED1 + MAX_LEDS)) { - offset = (GPIO_LED1_INV - GPIO_LED1); - } - if ((val >= GPIO_LED1_INV) && (val < GPIO_LED1_INV + MAX_LEDS)) { - offset = -(GPIO_LED1_INV - GPIO_LED1); - } - - if ((val >= GPIO_PWM1) && (val < GPIO_PWM1 + MAX_PWMS)) { - offset = (GPIO_PWM1_INV - GPIO_PWM1); - } - if ((val >= GPIO_PWM1_INV) && (val < GPIO_PWM1_INV + MAX_PWMS)) { - offset = -(GPIO_PWM1_INV - GPIO_PWM1); - } - - if ((val >= GPIO_CNTR1) && (val < GPIO_CNTR1 + MAX_COUNTERS)) { - offset = (GPIO_CNTR1_NP - GPIO_CNTR1); - } - if ((val >= GPIO_CNTR1_NP) && (val < GPIO_CNTR1_NP + MAX_COUNTERS)) { - offset = -(GPIO_CNTR1_NP - GPIO_CNTR1); - } - - for (uint32_t i = 0; i < MAX_GPIO_PIN; i++) { - if (arr[i] == val) { return true; } - if (arr[i] == val + offset) { return true; } - } - return false; -} - -bool JsonTemplate(const char* dataBuf) -{ - - - if (strlen(dataBuf) < 9) { return false; } - - StaticJsonBuffer<350> jb; - JsonObject& obj = jb.parseObject(dataBuf); - if (!obj.success()) { return false; } - - - const char* name = obj[D_JSON_NAME]; - if (name != nullptr) { - strlcpy(Settings.user_template.name, name, sizeof(Settings.user_template.name)); - } - if (obj[D_JSON_GPIO].success()) { - for (uint32_t i = 0; i < sizeof(mycfgio); i++) { - Settings.user_template.gp.io[i] = obj[D_JSON_GPIO][i] | 0; - } - } - if (obj[D_JSON_FLAG].success()) { - uint8_t flag = obj[D_JSON_FLAG] | 0; - memcpy(&Settings.user_template.flag, &flag, sizeof(gpio_flag)); - } - if (obj[D_JSON_BASE].success()) { - uint8_t base = obj[D_JSON_BASE]; - if ((0 == base) || !ValidTemplateModule(base -1)) { base = 18; } - Settings.user_template_base = base -1; - } - return true; -} - -void TemplateJson(void) -{ - Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), Settings.user_template.name); - for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { - ResponseAppend_P(PSTR("%s%d"), (i>0)?",":"", Settings.user_template.gp.io[i]); - } - ResponseAppend_P(PSTR("],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), Settings.user_template.flag, Settings.user_template_base +1); -} - - - - - -inline int32_t TimeDifference(uint32_t prev, uint32_t next) -{ - return ((int32_t) (next - prev)); -} - -int32_t TimePassedSince(uint32_t timestamp) -{ - - - return TimeDifference(timestamp, millis()); -} - -bool TimeReached(uint32_t timer) -{ - - const long passed = TimePassedSince(timer); - return (passed >= 0); -} - -void SetNextTimeInterval(unsigned long& timer, const unsigned long step) -{ - timer += step; - const long passed = TimePassedSince(timer); - if (passed < 0) { return; } - if (static_cast(passed) > step) { - - timer = millis() + step; - return; - } - - timer = millis() + (step - passed); -} - -int32_t TimePassedSinceUsec(uint32_t timestamp) -{ - return TimeDifference(timestamp, micros()); -} - -bool TimeReachedUsec(uint32_t timer) -{ - - const long passed = TimePassedSinceUsec(timer); - return (passed >= 0); -} - - - - - -#ifdef USE_I2C -const uint8_t I2C_RETRY_COUNTER = 3; - -uint32_t i2c_active[4] = { 0 }; -uint32_t i2c_buffer = 0; - -bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size) -{ - uint8_t retry = I2C_RETRY_COUNTER; - bool status = false; - - i2c_buffer = 0; - while (!status && retry) { - Wire.beginTransmission(addr); - Wire.write(reg); - if (0 == Wire.endTransmission(false)) { - Wire.requestFrom((int)addr, (int)size); - if (Wire.available() == size) { - for (uint32_t i = 0; i < size; i++) { - i2c_buffer = i2c_buffer << 8 | Wire.read(); - } - status = true; - } - } - retry--; - } - return status; -} - -bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg) -{ - bool status = I2cValidRead(addr, reg, 1); - *data = (uint8_t)i2c_buffer; - return status; -} - -bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg) -{ - bool status = I2cValidRead(addr, reg, 2); - *data = (uint16_t)i2c_buffer; - return status; -} - -bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg) -{ - bool status = I2cValidRead(addr, reg, 2); - *data = (int16_t)i2c_buffer; - return status; -} - -bool I2cValidRead16LE(uint16_t *data, uint8_t addr, uint8_t reg) -{ - uint16_t ldata; - bool status = I2cValidRead16(&ldata, addr, reg); - *data = (ldata >> 8) | (ldata << 8); - return status; -} - -bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg) -{ - uint16_t ldata; - bool status = I2cValidRead16LE(&ldata, addr, reg); - *data = (int16_t)ldata; - return status; -} - -bool I2cValidRead24(int32_t *data, uint8_t addr, uint8_t reg) -{ - bool status = I2cValidRead(addr, reg, 3); - *data = i2c_buffer; - return status; -} - -uint8_t I2cRead8(uint8_t addr, uint8_t reg) -{ - I2cValidRead(addr, reg, 1); - return (uint8_t)i2c_buffer; -} - -uint16_t I2cRead16(uint8_t addr, uint8_t reg) -{ - I2cValidRead(addr, reg, 2); - return (uint16_t)i2c_buffer; -} - -int16_t I2cReadS16(uint8_t addr, uint8_t reg) -{ - I2cValidRead(addr, reg, 2); - return (int16_t)i2c_buffer; -} - -uint16_t I2cRead16LE(uint8_t addr, uint8_t reg) -{ - I2cValidRead(addr, reg, 2); - uint16_t temp = (uint16_t)i2c_buffer; - return (temp >> 8) | (temp << 8); -} - -int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg) -{ - return (int16_t)I2cRead16LE(addr, reg); -} - -int32_t I2cRead24(uint8_t addr, uint8_t reg) -{ - I2cValidRead(addr, reg, 3); - return i2c_buffer; -} - -bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size) -{ - uint8_t x = I2C_RETRY_COUNTER; - - do { - Wire.beginTransmission((uint8_t)addr); - Wire.write(reg); - uint8_t bytes = size; - while (bytes--) { - Wire.write((val >> (8 * bytes)) & 0xFF); - } - x--; - } while (Wire.endTransmission(true) != 0 && x != 0); - return (x); -} - -bool I2cWrite8(uint8_t addr, uint8_t reg, uint16_t val) -{ - return I2cWrite(addr, reg, val, 1); -} - -bool I2cWrite16(uint8_t addr, uint8_t reg, uint16_t val) -{ - return I2cWrite(addr, reg, val, 2); -} - -int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len) -{ - Wire.beginTransmission((uint8_t)addr); - Wire.write((uint8_t)reg); - Wire.endTransmission(); - if (len != Wire.requestFrom((uint8_t)addr, (uint8_t)len)) { - return 1; - } - while (len--) { - *reg_data = (uint8_t)Wire.read(); - reg_data++; - } - return 0; -} - -int8_t I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len) -{ - Wire.beginTransmission((uint8_t)addr); - Wire.write((uint8_t)reg); - while (len--) { - Wire.write(*reg_data); - reg_data++; - } - Wire.endTransmission(); - return 0; -} - -void I2cScan(char *devs, unsigned int devs_len) -{ - - - - - - - - uint8_t error = 0; - uint8_t address = 0; - uint8_t any = 0; - - snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_DEVICES_FOUND_AT)); - for (address = 1; address <= 127; address++) { - Wire.beginTransmission(address); - error = Wire.endTransmission(); - if (0 == error) { - any = 1; - snprintf_P(devs, devs_len, PSTR("%s 0x%02x"), devs, address); - } - else if (error != 2) { - any = 2; - snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"Error %d at 0x%02x"), error, address); - break; - } - } - if (any) { - strncat(devs, "\"}", devs_len - strlen(devs) -1); - } - else { - snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_NO_DEVICES_FOUND "\"}")); - } -} - -void I2cResetActive(uint32_t addr, uint32_t count = 1) -{ - addr &= 0x7F; - count &= 0x7F; - while (count-- && (addr < 128)) { - i2c_active[addr / 32] &= ~(1 << (addr % 32)); - addr++; - } - -} - -void I2cSetActive(uint32_t addr, uint32_t count = 1) -{ - addr &= 0x7F; - count &= 0x7F; - while (count-- && (addr < 128)) { - i2c_active[addr / 32] |= (1 << (addr % 32)); - addr++; - } - -} - -void I2cSetActiveFound(uint32_t addr, const char *types) -{ - I2cSetActive(addr); - AddLog_P2(LOG_LEVEL_INFO, S_LOG_I2C_FOUND_AT, types, addr); -} - -bool I2cActive(uint32_t addr) -{ - addr &= 0x7F; - if (i2c_active[addr / 32] & (1 << (addr % 32))) { - return true; - } - return false; -} - -bool I2cSetDevice(uint32_t addr) -{ - addr &= 0x7F; - if (I2cActive(addr)) { - return false; - } - Wire.beginTransmission((uint8_t)addr); - return (0 == Wire.endTransmission()); -} -#endif -# 1559 "C:/shared/sonoff/Git/Tasmota/tasmota/support.ino" -void SetSeriallog(uint32_t loglevel) -{ - Settings.seriallog_level = loglevel; - seriallog_level = loglevel; - seriallog_timer = 0; -} - -void SetSyslog(uint32_t loglevel) -{ - Settings.syslog_level = loglevel; - syslog_level = loglevel; - syslog_timer = 0; -} - -#ifdef USE_WEBSERVER -void GetLog(uint32_t idx, char** entry_pp, size_t* len_p) -{ - char* entry_p = nullptr; - size_t len = 0; - - if (idx) { - char* it = web_log; - do { - uint32_t cur_idx = *it; - it++; - size_t tmp = strchrspn(it, '\1'); - tmp++; - if (cur_idx == idx) { - len = tmp; - entry_p = it; - break; - } - it += tmp; - } while (it < web_log + WEB_LOG_SIZE && *it != '\0'); - } - *entry_pp = entry_p; - *len_p = len; -} -#endif - -void Syslog(void) -{ - - - uint32_t current_hash = GetHash(SettingsText(SET_SYSLOG_HOST), strlen(SettingsText(SET_SYSLOG_HOST))); - if (syslog_host_hash != current_hash) { - syslog_host_hash = current_hash; - WiFi.hostByName(SettingsText(SET_SYSLOG_HOST), syslog_host_addr); - } - if (PortUdp.beginPacket(syslog_host_addr, Settings.syslog_port)) { - char syslog_preamble[64]; - snprintf_P(syslog_preamble, sizeof(syslog_preamble), PSTR("%s ESP-"), my_hostname); - memmove(log_data + strlen(syslog_preamble), log_data, sizeof(log_data) - strlen(syslog_preamble)); - log_data[sizeof(log_data) -1] = '\0'; - memcpy(log_data, syslog_preamble, strlen(syslog_preamble)); - PortUdp.write(log_data, strlen(log_data)); - PortUdp.endPacket(); - delay(1); - } else { - syslog_level = 0; - syslog_timer = SYSLOG_TIMER; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_HOST_NOT_FOUND ". " D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER); - } -} - -void AddLog(uint32_t loglevel) -{ - char mxtime[10]; - - snprintf_P(mxtime, sizeof(mxtime), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d "), RtcTime.hour, RtcTime.minute, RtcTime.second); - - if (loglevel <= seriallog_level) { - Serial.printf("%s%s\r\n", mxtime, log_data); - } -#ifdef USE_WEBSERVER - if (Settings.webserver && (loglevel <= Settings.weblog_level)) { - - - web_log_index &= 0xFF; - if (!web_log_index) web_log_index++; - while (web_log_index == web_log[0] || - strlen(web_log) + strlen(log_data) + 13 > WEB_LOG_SIZE) - { - char* it = web_log; - it++; - it += strchrspn(it, '\1'); - it++; - memmove(web_log, it, WEB_LOG_SIZE -(it-web_log)); - } - snprintf_P(web_log, sizeof(web_log), PSTR("%s%c%s%s\1"), web_log, web_log_index++, mxtime, log_data); - web_log_index &= 0xFF; - if (!web_log_index) web_log_index++; - } -#endif - if (Settings.flag.mqtt_enabled && - !global_state.mqtt_down && - (loglevel <= Settings.mqttlog_level)) { MqttPublishLogging(mxtime); } - - if (!global_state.wifi_down && - (loglevel <= syslog_level)) { Syslog(); } -} - -void AddLog_P(uint32_t loglevel, const char *formatP) -{ - snprintf_P(log_data, sizeof(log_data), formatP); - AddLog(loglevel); -} - -void AddLog_P(uint32_t loglevel, const char *formatP, const char *formatP2) -{ - char message[sizeof(log_data)]; - - snprintf_P(log_data, sizeof(log_data), formatP); - snprintf_P(message, sizeof(message), formatP2); - strncat(log_data, message, sizeof(log_data) - strlen(log_data) -1); - AddLog(loglevel); -} - -void PrepLog_P2(uint32_t loglevel, PGM_P formatP, ...) -{ - va_list arg; - va_start(arg, formatP); - vsnprintf_P(log_data, sizeof(log_data), formatP, arg); - va_end(arg); - - prepped_loglevel = loglevel; -} - -void AddLog_P2(uint32_t loglevel, PGM_P formatP, ...) -{ - va_list arg; - va_start(arg, formatP); - vsnprintf_P(log_data, sizeof(log_data), formatP, arg); - va_end(arg); - - AddLog(loglevel); -} - -void AddLog_Debug(PGM_P formatP, ...) -{ - va_list arg; - va_start(arg, formatP); - vsnprintf_P(log_data, sizeof(log_data), formatP, arg); - va_end(arg); - - AddLog(LOG_LEVEL_DEBUG); -} - -void AddLogBuffer(uint32_t loglevel, uint8_t *buffer, uint32_t count) -{ -# 1721 "C:/shared/sonoff/Git/Tasmota/tasmota/support.ino" - char hex_char[(count * 3) + 2]; - AddLog_P2(loglevel, PSTR("DMP: %s"), ToHex_P(buffer, count, hex_char, sizeof(hex_char), ' ')); -} - -void AddLogSerial(uint32_t loglevel) -{ - AddLogBuffer(loglevel, (uint8_t*)serial_in_buffer, serial_in_byte_counter); -} - -void AddLogMissed(char *sensor, uint32_t misses) -{ - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses); -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_button.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_button.ino" -#define BUTTON_V1 -#ifdef BUTTON_V1 - - - - -#define MAX_BUTTON_COMMANDS 5 -const char kCommands[] PROGMEM = - D_CMND_WIFICONFIG " 2|" D_CMND_WIFICONFIG " 2|" D_CMND_WIFICONFIG " 2|" D_CMND_RESTART " 1|" D_CMND_UPGRADE " 1"; - -struct BUTTON { - unsigned long debounce = 0; - uint16_t hold_timer[MAX_KEYS] = { 0 }; - uint16_t dual_code = 0; - - uint8_t last_state[MAX_KEYS] = { NOT_PRESSED, NOT_PRESSED, NOT_PRESSED, NOT_PRESSED }; - uint8_t window_timer[MAX_KEYS] = { 0 }; - uint8_t press_counter[MAX_KEYS] = { 0 }; - - uint8_t dual_receive_count = 0; - uint8_t no_pullup_mask = 0; - uint8_t inverted_mask = 0; - uint8_t present = 0; - uint8_t adc = 99; -} Button; - - - -void ButtonPullupFlag(uint8 button_bit) -{ - bitSet(Button.no_pullup_mask, button_bit); -} - -void ButtonInvertFlag(uint8 button_bit) -{ - bitSet(Button.inverted_mask, button_bit); -} - -void ButtonInit(void) -{ - Button.present = 0; - for (uint32_t i = 0; i < MAX_KEYS; i++) { - if (pin[GPIO_KEY1 +i] < 99) { - Button.present++; - pinMode(pin[GPIO_KEY1 +i], bitRead(Button.no_pullup_mask, i) ? INPUT : ((16 == pin[GPIO_KEY1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); - } -#ifndef USE_ADC_VCC - else if ((99 == Button.adc) && ((ADC0_BUTTON == my_adc0) || (ADC0_BUTTON_INV == my_adc0))) { - Button.present++; - Button.adc = i; - } -#endif - } -} - -uint8_t ButtonSerial(uint8_t serial_in_byte) -{ - if (Button.dual_receive_count) { - Button.dual_receive_count--; - if (Button.dual_receive_count) { - Button.dual_code = (Button.dual_code << 8) | serial_in_byte; - serial_in_byte = 0; - } else { - if (serial_in_byte != 0xA1) { - Button.dual_code = 0; - } - } - } - if (0xA0 == serial_in_byte) { - serial_in_byte = 0; - Button.dual_code = 0; - Button.dual_receive_count = 3; - } - - return serial_in_byte; -} -# 108 "C:/shared/sonoff/Git/Tasmota/tasmota/support_button.ino" -void ButtonHandler(void) -{ - if (uptime < 4) { return; } - - uint8_t hold_time_extent = IMMINENT_RESET_FACTOR; - uint16_t loops_per_second = 1000 / Settings.button_debounce; - char scmnd[20]; - - - - for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) { - uint8_t button = NOT_PRESSED; - uint8_t button_present = 0; - - if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) { - button_present = 1; - if (Button.dual_code) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON " " D_CODE " %04X"), Button.dual_code); - button = PRESSED; - if (0xF500 == Button.dual_code) { - Button.hold_timer[button_index] = (loops_per_second * Settings.param[P_HOLD_TIME] / 10) -1; - hold_time_extent = 1; - } - Button.dual_code = 0; - } - } - else if (pin[GPIO_KEY1 +button_index] < 99) { - button_present = 1; - button = (digitalRead(pin[GPIO_KEY1 +button_index]) != bitRead(Button.inverted_mask, button_index)); - } -#ifndef USE_ADC_VCC - if (Button.adc == button_index) { - button_present = 1; - if (ADC0_BUTTON_INV == my_adc0) { - button = (AdcRead(1) < 128); - } - else if (ADC0_BUTTON == my_adc0) { - button = (AdcRead(1) > 128); - } - } -#endif - - if (button_present) { - XdrvMailbox.index = button_index; - XdrvMailbox.payload = button; - if (XdrvCall(FUNC_BUTTON_PRESSED)) { - - } - else if (SONOFF_4CHPRO == my_module_type) { - if (Button.hold_timer[button_index]) { Button.hold_timer[button_index]--; } - - bool button_pressed = false; - if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_10), button_index +1); - Button.hold_timer[button_index] = loops_per_second; - button_pressed = true; - } - if ((NOT_PRESSED == button) && (PRESSED == Button.last_state[button_index])) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_LEVEL_01), button_index +1); - if (!Button.hold_timer[button_index]) { button_pressed = true; } - } - if (button_pressed) { - if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { - ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); - } - } - } - else { - if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { - if (Settings.flag.button_single) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_IMMEDIATE), button_index +1); - if (!SendKey(KEY_BUTTON, button_index +1, POWER_TOGGLE)) { - ExecuteCommandPower(button_index +1, POWER_TOGGLE, SRC_BUTTON); - } - } else { - Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BUTTON "%d " D_MULTI_PRESS " %d"), button_index +1, Button.press_counter[button_index]); - Button.window_timer[button_index] = loops_per_second / 2; - } - blinks = 201; - } - - if (NOT_PRESSED == button) { - Button.hold_timer[button_index] = 0; - } else { - Button.hold_timer[button_index]++; - if (Settings.flag.button_single) { - if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { - - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_SETOPTION "13 0")); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } else { - if (Settings.flag.button_restrict) { - if (Settings.param[P_HOLD_IGNORE] > 0) { - if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) { - Button.hold_timer[button_index] = 0; - Button.press_counter[button_index] = 0; - DEBUG_CORE_LOG(PSTR("BTN: " D_BUTTON "%d cancel by " D_CMND_SETOPTION "40 %d"), button_index +1, Settings.param[P_HOLD_IGNORE]); - } - } - if (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10) { - Button.press_counter[button_index] = 0; - SendKey(KEY_BUTTON, button_index +1, POWER_HOLD); - } - } else { - if (Button.hold_timer[button_index] == loops_per_second * hold_time_extent * Settings.param[P_HOLD_TIME] / 10) { - Button.press_counter[button_index] = 0; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } - } - } - - if (!Settings.flag.button_single) { - if (Button.window_timer[button_index]) { - Button.window_timer[button_index]--; - } else { - if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0) && (Button.press_counter[button_index] < MAX_BUTTON_COMMANDS +3)) { - bool single_press = false; - if (Button.press_counter[button_index] < 3) { - if ((SONOFF_DUAL_R2 == my_module_type) || (SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { - single_press = true; - } else { - single_press = (Settings.flag.button_swap +1 == Button.press_counter[button_index]); - if ((1 == Button.present) && (2 == devices_present)) { - if (Settings.flag.button_swap) { - Button.press_counter[button_index] = (single_press) ? 1 : 2; - } - } else { - Button.press_counter[button_index] = 1; - } - } - } -#if defined(USE_LIGHT) && defined(ROTARY_V1) - if (!((0 == button_index) && RotaryButtonPressed())) { -#endif - if (single_press && SendKey(KEY_BUTTON, button_index + Button.press_counter[button_index], POWER_TOGGLE)) { - - } else { - if (Button.press_counter[button_index] < 3) { - if (WifiState() > WIFI_RESTART) { - restart_flag = 1; - } else { - ExecuteCommandPower(button_index + Button.press_counter[button_index], POWER_TOGGLE, SRC_BUTTON); - } - } else { - if (!Settings.flag.button_restrict) { - GetTextIndexed(scmnd, sizeof(scmnd), Button.press_counter[button_index] -3, kCommands); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } - } -#if defined(USE_LIGHT) && defined(ROTARY_V1) - } -#endif - Button.press_counter[button_index] = 0; - } - } - } - } - } - Button.last_state[button_index] = button; - } -} - -void ButtonLoop(void) -{ - if (Button.present) { - if (TimeReached(Button.debounce)) { - SetNextTimeInterval(Button.debounce, Settings.button_debounce); - ButtonHandler(); - } - } -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_command.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_command.ino" -const char kTasmotaCommands[] PROGMEM = "|" - D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_SLEEP "|" D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" - D_CMND_SERIALLOG "|" D_CMND_RESTART "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|" D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SAVEDATA "|" - D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|" D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" - D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|" - D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" - D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|" - D_CMND_SERIALDELIMITER "|" 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_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" - D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" -#ifdef USE_I2C - D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|" -#endif - D_CMND_SENSOR "|" D_CMND_DRIVER; - -void (* const TasmotaCommand[])(void) PROGMEM = { - &CmndBacklog, &CmndDelay, &CmndPower, &CmndStatus, &CmndState, &CmndSleep, &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, - &CmndSeriallog, &CmndRestart, &CmndPowerOnState, &CmndPulsetime, &CmndBlinktime, &CmndBlinkcount, &CmndSavedata, - &CmndSetoption, &CmndTemperatureResolution, &CmndHumidityResolution, &CmndPressureResolution, &CmndPowerResolution, - &CmndVoltageResolution, &CmndFrequencyResolution, &CmndCurrentResolution, &CmndEnergyResolution, &CmndWeightResolution, - &CmndModule, &CmndModules, &CmndGpio, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange, - &CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig, - &CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, - &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd, - &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset, -#ifdef USE_I2C - &CmndI2cScan, CmndI2cDriver, -#endif - &CmndSensor, &CmndDriver }; - -const char kWifiConfig[] PROGMEM = - D_WCFG_0_RESTART "||" D_WCFG_2_WIFIMANAGER "||" D_WCFG_4_RETRY "|" D_WCFG_5_WAIT "|" D_WCFG_6_SERIAL "|" D_WCFG_7_WIFIMANAGER_RESET_ONLY; - - - -void ResponseCmndNumber(int value) -{ - Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, value); -} - -void ResponseCmndFloat(float value, uint32_t decimals) -{ - char stemp1[TOPSZ]; - dtostrfd(value, decimals, stemp1); - Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, stemp1); -} - -void ResponseCmndIdxNumber(int value) -{ - Response_P(S_JSON_COMMAND_INDEX_NVALUE, XdrvMailbox.command, XdrvMailbox.index, value); -} - -void ResponseCmndChar(const char* value) -{ - Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, value); -} - -void ResponseCmndStateText(uint32_t value) -{ - ResponseCmndChar(GetStateText(value)); -} - -void ResponseCmndDone(void) -{ - ResponseCmndChar(D_JSON_DONE); -} - -void ResponseCmndIdxChar(const char* value) -{ - Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, XdrvMailbox.index, value); -} - -void ResponseCmndAll(uint32_t text_index, uint32_t count) -{ - mqtt_data[0] = '\0'; - for (uint32_t i = 0; i < count; i++) { - ResponseAppend_P(PSTR("%c\"%s%d\":\"%s\""), (i) ? ',' : '{', XdrvMailbox.command, i +1, SettingsText(text_index +i)); - } - ResponseJsonEnd(); -} - - - -void ExecuteCommand(const char *cmnd, uint32_t source) -{ - - - -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("ExecuteCommand")); -#endif - ShowSource(source); - - const char *pos = cmnd; - while (*pos && isspace(*pos)) { - pos++; - } - - const char *start = pos; - - while (*pos && (isalpha(*pos) || isdigit(*pos) || '_' == *pos || '/' == *pos)) { - if ('/' == *pos) { - start = pos + 1; - } - pos++; - } - if ('\0' == *start || pos <= start) { - return; - } - - uint32_t size = pos - start; - char stopic[size + 2]; - stopic[0] = '/'; - memcpy(stopic+1, start, size); - stopic[size+1] = '\0'; - - char svalue[strlen(pos) +1]; - strlcpy(svalue, pos, sizeof(svalue)); - CommandHandler(stopic, svalue, strlen(svalue)); -} -# 148 "C:/shared/sonoff/Git/Tasmota/tasmota/support_command.ino" -void CommandHandler(char* topicBuf, char* dataBuf, uint32_t data_len) -{ -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("CommandHandler")); -#endif - - while (*dataBuf && isspace(*dataBuf)) { - dataBuf++; - data_len--; - } - - bool grpflg = (strstr(topicBuf, SettingsText(SET_MQTT_GRP_TOPIC)) != nullptr); - - char stemp1[TOPSZ]; - GetFallbackTopic_P(stemp1, ""); - fallback_topic_flag = (!strncmp(topicBuf, stemp1, strlen(stemp1))); - - char *type = strrchr(topicBuf, '/'); - - uint32_t index = 1; - bool user_index = false; - if (type != nullptr) { - type++; - uint32_t i; - for (i = 0; i < strlen(type); i++) { - type[i] = toupper(type[i]); - } - while (isdigit(type[i-1])) { - i--; - } - if (i < strlen(type)) { - index = atoi(type +i); - user_index = true; - } - type[i] = '\0'; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("CMD: " D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " \"%s\", " D_DATA " \"%s\""), grpflg, index, type, dataBuf); - - if (type != nullptr) { - Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_ERROR "\"}")); - - if (Settings.ledstate &0x02) { blinks++; } - - if (!strcmp(dataBuf,"?")) { data_len = 0; } - - char *p; - int32_t payload = strtol(dataBuf, &p, 0); - if (p == dataBuf) { payload = -99; } - int temp_payload = GetStateNumber(dataBuf); - if (temp_payload > -1) { payload = temp_payload; } - - DEBUG_CORE_LOG(PSTR("CMD: Payload %d"), payload); - - - backlog_delay = millis() + Settings.param[P_BACKLOG_DELAY]; - - char command[CMDSZ] = { 0 }; - XdrvMailbox.command = command; - XdrvMailbox.index = index; - XdrvMailbox.data_len = data_len; - XdrvMailbox.payload = payload; - XdrvMailbox.grpflg = grpflg; - XdrvMailbox.usridx = user_index; - XdrvMailbox.topic = type; - XdrvMailbox.data = dataBuf; - -#ifdef USE_SCRIPT_SUB_COMMAND - - if (!Script_SubCmd()) { - if (!DecodeCommand(kTasmotaCommands, TasmotaCommand)) { - if (!XdrvCall(FUNC_COMMAND)) { - if (!XsnsCall(FUNC_COMMAND)) { - type = nullptr; - } - } - } - } -#else - if (!DecodeCommand(kTasmotaCommands, TasmotaCommand)) { - if (!XdrvCall(FUNC_COMMAND)) { - if (!XsnsCall(FUNC_COMMAND)) { - type = nullptr; - } - } - } -#endif - - } - - if (type == nullptr) { - blinks = 201; - snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_COMMAND)); - Response_P(PSTR("{\"" D_JSON_COMMAND "\":\"" D_JSON_UNKNOWN "\"}")); - type = (char*)stemp1; - } - - if (mqtt_data[0] != '\0') { - MqttPublishPrefixTopic_P(RESULT_OR_STAT, type); - XdrvRulesProcess(); - } - fallback_topic_flag = false; -} - - - -void CmndBacklog(void) -{ - if (XdrvMailbox.data_len) { - -#ifdef SUPPORT_IF_STATEMENT - char *blcommand = strtok(XdrvMailbox.data, ";"); - while ((blcommand != nullptr) && (backlog.size() < MAX_BACKLOG)) -#else - uint32_t bl_pointer = (!backlog_pointer) ? MAX_BACKLOG -1 : backlog_pointer; - bl_pointer--; - char *blcommand = strtok(XdrvMailbox.data, ";"); - while ((blcommand != nullptr) && (backlog_index != bl_pointer)) -#endif - { - while(true) { - blcommand = Trim(blcommand); - if (!strncasecmp_P(blcommand, PSTR(D_CMND_BACKLOG), strlen(D_CMND_BACKLOG))) { - blcommand += strlen(D_CMND_BACKLOG); - } else { - break; - } - } - if (*blcommand != '\0') { -#ifdef SUPPORT_IF_STATEMENT - if (backlog.size() < MAX_BACKLOG) { - backlog.add(blcommand); - } -#else - backlog[backlog_index] = String(blcommand); - backlog_index++; - if (backlog_index >= MAX_BACKLOG) backlog_index = 0; -#endif - } - blcommand = strtok(nullptr, ";"); - } - - mqtt_data[0] = '\0'; - } else { - bool blflag = BACKLOG_EMPTY; -#ifdef SUPPORT_IF_STATEMENT - backlog.clear(); -#else - backlog_pointer = backlog_index; -#endif - ResponseCmndChar(blflag ? D_JSON_EMPTY : D_JSON_ABORTED); - } -} - -void CmndDelay(void) -{ - if ((XdrvMailbox.payload >= (MIN_BACKLOG_DELAY / 100)) && (XdrvMailbox.payload <= 3600)) { - backlog_delay = millis() + (100 * XdrvMailbox.payload); - } - uint32_t bl_delay = 0; - long bl_delta = TimePassedSince(backlog_delay); - if (bl_delta < 0) { bl_delay = (bl_delta *-1) / 100; } - ResponseCmndNumber(bl_delay); -} - -void CmndPower(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= devices_present)) { - if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_BLINK_STOP)) { - XdrvMailbox.payload = POWER_SHOW_STATE; - } - - ExecuteCommandPower(XdrvMailbox.index, XdrvMailbox.payload, SRC_IGNORE); - mqtt_data[0] = '\0'; - } - else if (0 == XdrvMailbox.index) { - if ((XdrvMailbox.payload < POWER_OFF) || (XdrvMailbox.payload > POWER_TOGGLE)) { - XdrvMailbox.payload = POWER_SHOW_STATE; - } - SetAllPower(XdrvMailbox.payload, SRC_IGNORE); - mqtt_data[0] = '\0'; - } -} - -void CmndStatus(void) -{ - uint32_t payload = ((XdrvMailbox.payload < 0) || (XdrvMailbox.payload > MAX_STATUS)) ? 99 : XdrvMailbox.payload; - - uint32_t option = STAT; - char stemp[200]; - char stemp2[TOPSZ]; - - - - - - if ((!Settings.flag.mqtt_enabled) && (6 == payload)) { payload = 99; } - if (!energy_flg && (9 == payload)) { payload = 99; } - if (!CrashFlag() && (12 == payload)) { payload = 99; } - - if ((0 == payload) || (99 == payload)) { - uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { maxfn = 1; } -#endif - stemp[0] = '\0'; - for (uint32_t i = 0; i < maxfn; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), SettingsText(SET_FRIENDLYNAME1 +i)); - } - stemp2[0] = '\0'; - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]); - } - Response_P(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_LEDMASK "\":\"%04X\",\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\"" - D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"), - ModuleNr(), stemp, mqtt_topic, - SettingsText(SET_MQTT_BUTTON_TOPIC), power, Settings.poweronstate, Settings.ledstate, - Settings.ledmask, Settings.save_data, - Settings.flag.save_state, - SettingsText(SET_MQTT_SWITCH_TOPIC), - stemp2, - Settings.flag.mqtt_button_retain, - Settings.flag.mqtt_switch_retain, - Settings.flag.mqtt_sensor_retain, - Settings.flag.mqtt_power_retain); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS)); - } - - if ((0 == payload) || (1 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS1_PARAMETER "\":{\"" D_JSON_BAUDRATE "\":%d,\"" D_CMND_SERIALCONFIG "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\",\"" D_CMND_OTAURL "\":\"%s\",\"" - D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\",\"" D_JSON_STARTUPUTC "\":\"%s\",\"" D_CMND_SLEEP "\":%d,\"" - D_JSON_CONFIG_HOLDER "\":%d,\"" D_JSON_BOOTCOUNT "\":%d,\"BCResetTime\":\"%s\",\"" D_JSON_SAVECOUNT "\":%d,\"" D_JSON_SAVEADDRESS "\":\"%X\"}}"), - Settings.baudrate * 300, GetSerialConfig().c_str(), SettingsText(SET_MQTT_GRP_TOPIC), SettingsText(SET_OTAURL), - GetResetReason().c_str(), GetUptime().c_str(), GetDateAndTime(DT_RESTART).c_str(), Settings.sleep, - Settings.cfg_holder, Settings.bootcount, GetDateAndTime(DT_BOOTCOUNT).c_str(), Settings.save_flag, GetSettingsAddress()); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "1")); - } - - if ((0 == payload) || (2 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS2_FIRMWARE "\":{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\",\"" - D_JSON_BOOTVERSION "\":%d,\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"," - "\"Hardware\":\"%s\"" - "%s}}"), - my_version, my_image, GetBuildDateAndTime().c_str(), - ESP.getBootVersion(), ESP.getSdkVersion(), - GetDeviceHardware().c_str(), - GetStatistics().c_str()); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "2")); - } - - if ((0 == payload) || (3 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS3_LOGGING "\":{\"" D_CMND_SERIALLOG "\":%d,\"" D_CMND_WEBLOG "\":%d,\"" D_CMND_MQTTLOG "\":%d,\"" D_CMND_SYSLOG "\":%d,\"" - D_CMND_LOGHOST "\":\"%s\",\"" D_CMND_LOGPORT "\":%d,\"" D_CMND_SSID "\":[\"%s\",\"%s\"],\"" D_CMND_TELEPERIOD "\":%d,\"" - D_JSON_RESOLUTION "\":\"%08X\",\"" D_CMND_SETOPTION "\":[\"%08X\",\"%s\",\"%08X\",\"%08X\"]}}"), - Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, - SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), Settings.tele_period, - Settings.flag2.data, Settings.flag.data, ToHex_P((unsigned char*)Settings.param, PARAM8_SIZE, stemp2, sizeof(stemp2)), - Settings.flag3.data, Settings.flag4.data); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "3")); - } - - if ((0 == payload) || (4 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS4_MEMORY "\":{\"" D_JSON_PROGRAMSIZE "\":%d,\"" D_JSON_FREEMEMORY "\":%d,\"" D_JSON_HEAPSIZE "\":%d,\"" - D_JSON_PROGRAMFLASHSIZE "\":%d,\"" D_JSON_FLASHSIZE "\":%d,\"" D_JSON_FLASHCHIPID "\":\"%06X\",\"" D_JSON_FLASHMODE "\":%d,\"" - D_JSON_FEATURES "\":[\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\",\"%08X\"]"), - ESP.getSketchSize()/1024, ESP.getFreeSketchSpace()/1024, ESP.getFreeHeap()/1024, - ESP.getFlashChipSize()/1024, ESP.getFlashChipRealSize()/1024, ESP.getFlashChipId(), ESP.getFlashChipMode(), - LANGUAGE_LCID, feature_drv1, feature_drv2, feature_sns1, feature_sns2, feature5, feature6); - XsnsDriverState(); - ResponseAppend_P(PSTR(",\"Sensors\":")); - XsnsSensorState(); - ResponseJsonEndEnd(); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "4")); - } - - if ((0 == payload) || (5 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS5_NETWORK "\":{\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_GATEWAY "\":\"%s\",\"" - D_JSON_SUBNETMASK "\":\"%s\",\"" D_JSON_DNSSERVER "\":\"%s\",\"" D_JSON_MAC "\":\"%s\",\"" - D_CMND_WEBSERVER "\":%d,\"" D_CMND_WIFICONFIG "\":%d,\"" D_CMND_WIFIPOWER "\":%s}}"), - my_hostname, WiFi.localIP().toString().c_str(), IPAddress(Settings.ip_address[1]).toString().c_str(), - IPAddress(Settings.ip_address[2]).toString().c_str(), IPAddress(Settings.ip_address[3]).toString().c_str(), WiFi.macAddress().c_str(), - Settings.webserver, Settings.sta_config, WifiGetOutputPower().c_str()); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "5")); - } - - if (((0 == payload) || (6 == payload)) && Settings.flag.mqtt_enabled) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS6_MQTT "\":{\"" D_CMND_MQTTHOST "\":\"%s\",\"" D_CMND_MQTTPORT "\":%d,\"" D_CMND_MQTTCLIENT D_JSON_MASK "\":\"%s\",\"" - D_CMND_MQTTCLIENT "\":\"%s\",\"" D_CMND_MQTTUSER "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"MAX_PACKET_SIZE\":%d,\"KEEPALIVE\":%d}}"), - SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), - mqtt_client, SettingsText(SET_MQTT_USER), MqttConnectCount(), MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "6")); - } - - if ((0 == payload) || (7 == payload)) { - if (99 == Settings.timezone) { - snprintf_P(stemp, sizeof(stemp), PSTR("%d" ), Settings.timezone); - } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"%s\"" ), GetTimeZone().c_str()); - } -#if defined(USE_TIMERS) && defined(USE_SUNRISE) - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" - D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s,\"" D_JSON_SUNRISE "\":\"%s\",\"" D_JSON_SUNSET "\":\"%s\"}}"), - GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(), - GetTime(3).c_str(), stemp, GetSun(0).c_str(), GetSun(1).c_str()); -#else - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS7_TIME "\":{\"" D_JSON_UTC_TIME "\":\"%s\",\"" D_JSON_LOCAL_TIME "\":\"%s\",\"" D_JSON_STARTDST "\":\"%s\",\"" - D_JSON_ENDDST "\":\"%s\",\"" D_CMND_TIMEZONE "\":%s}}"), - GetTime(0).c_str(), GetTime(1).c_str(), GetTime(2).c_str(), - GetTime(3).c_str(), stemp); -#endif - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "7")); - } - -#if defined(USE_ENERGY_SENSOR) && defined(USE_ENERGY_MARGIN_DETECTION) - if (energy_flg) { - if ((0 == payload) || (9 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS9_MARGIN "\":{\"" D_CMND_POWERDELTA "\":%d,\"" D_CMND_POWERLOW "\":%d,\"" D_CMND_POWERHIGH "\":%d,\"" - D_CMND_VOLTAGELOW "\":%d,\"" D_CMND_VOLTAGEHIGH "\":%d,\"" D_CMND_CURRENTLOW "\":%d,\"" D_CMND_CURRENTHIGH "\":%d}}"), - Settings.energy_power_delta, Settings.energy_min_power, Settings.energy_max_power, - Settings.energy_min_voltage, Settings.energy_max_voltage, Settings.energy_min_current, Settings.energy_max_current); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "9")); - } - } -#endif - - if ((0 == payload) || (8 == payload) || (10 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS10_SENSOR "\":")); - MqttShowSensor(); - ResponseJsonEnd(); - if (8 == payload) { - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "8")); - } else { - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "10")); - } - } - - if ((0 == payload) || (11 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS11_STATUS "\":")); - MqttShowState(); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "11")); - } - - if (CrashFlag()) { - if ((0 == payload) || (12 == payload)) { - Response_P(PSTR("{\"" D_CMND_STATUS D_STATUS12_STATUS "\":")); - CrashDump(); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(option, PSTR(D_CMND_STATUS "12")); - } - } - -#ifdef USE_SCRIPT_STATUS - if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">U",2,mqtt_data); -#endif - mqtt_data[0] = '\0'; -} - -void CmndState(void) -{ - mqtt_data[0] = '\0'; - MqttShowState(); - if (Settings.flag3.hass_tele_on_power) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); - } -#ifdef USE_HOME_ASSISTANT - if (Settings.flag.hass_discovery) { - HAssPublishStatus(); - } -#endif -} - -void CmndTempOffset(void) -{ - if (XdrvMailbox.data_len > 0) { - int value = (int)(CharToFloat(XdrvMailbox.data) * 10); - if ((value > -127) && (value < 127)) { - Settings.temp_comp = value; - } - } - ResponseCmndFloat((float)(Settings.temp_comp) / 10, 1); -} - -void CmndSleep(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 251)) { - Settings.sleep = XdrvMailbox.payload; - sleep = XdrvMailbox.payload; - WiFiSetSleepMode(); - } - Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.sleep, sleep); - -} - -void CmndUpgrade(void) -{ - - - - - if (((1 == XdrvMailbox.data_len) && (1 == XdrvMailbox.payload)) || ((XdrvMailbox.data_len >= 3) && NewerVersion(XdrvMailbox.data))) { - ota_state_flag = 3; - char stemp1[TOPSZ]; - Response_P(PSTR("{\"%s\":\"" D_JSON_VERSION " %s " D_JSON_FROM " %s\"}"), XdrvMailbox.command, my_version, GetOtaUrl(stemp1, sizeof(stemp1))); - } else { - Response_P(PSTR("{\"%s\":\"" D_JSON_ONE_OR_GT "\"}"), XdrvMailbox.command, my_version); - } -} - -void CmndOtaUrl(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_OTAURL, (SC_DEFAULT == Shortcut()) ? OTA_URL : XdrvMailbox.data); - } - ResponseCmndChar(SettingsText(SET_OTAURL)); -} - -void CmndSeriallog(void) -{ - if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { - Settings.flag.mqtt_serial = 0; - SetSeriallog(XdrvMailbox.payload); - } - Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.seriallog_level, seriallog_level); -} - -void CmndRestart(void) -{ - switch (XdrvMailbox.payload) { - case 1: - restart_flag = 2; - ResponseCmndChar(D_JSON_RESTARTING); - break; - case -1: - CmndCrash(); - break; - case -2: - CmndWDT(); - break; - case -3: - CmndBlockedLoop(); - break; - case 99: - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); - EspRestart(); - break; - default: - ResponseCmndChar(D_JSON_ONE_TO_RESTART); - } -} - -void CmndPowerOnState(void) -{ - if (my_module_type != MOTOR) { - - - - - - - - if ((XdrvMailbox.payload >= POWER_ALL_OFF) && (XdrvMailbox.payload <= POWER_ALL_OFF_PULSETIME_ON)) { - Settings.poweronstate = XdrvMailbox.payload; - if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { - for (uint32_t i = 1; i <= devices_present; i++) { - ExecuteCommandPower(i, POWER_ON, SRC_IGNORE); - } - } - } - ResponseCmndNumber(Settings.poweronstate); - } -} - -void CmndPulsetime(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PULSETIMERS)) { - uint32_t items = 1; - if (!XdrvMailbox.usridx && !XdrvMailbox.data_len) { - items = MAX_PULSETIMERS; - } else { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) { - Settings.pulse_timer[XdrvMailbox.index -1] = XdrvMailbox.payload; - SetPulseTimer(XdrvMailbox.index -1, XdrvMailbox.payload); - } - } - mqtt_data[0] = '\0'; - for (uint32_t i = 0; i < items; i++) { - uint32_t index = (1 == items) ? XdrvMailbox.index : i +1; - ResponseAppend_P(PSTR("%c\"%s%d\":{\"" D_JSON_SET "\":%d,\"" D_JSON_REMAINING "\":%d}"), - (i) ? ',' : '{', - XdrvMailbox.command, index, - Settings.pulse_timer[index -1], GetPulseTimer(index -1)); - } - ResponseJsonEnd(); - } -} - -void CmndBlinktime(void) -{ - if ((XdrvMailbox.payload > 1) && (XdrvMailbox.payload <= 3600)) { - Settings.blinktime = XdrvMailbox.payload; - if (blink_timer > 0) { blink_timer = millis() + (100 * XdrvMailbox.payload); } - } - ResponseCmndNumber(Settings.blinktime); -} - -void CmndBlinkcount(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 65536)) { - Settings.blinkcount = XdrvMailbox.payload; - if (blink_counter) { blink_counter = Settings.blinkcount *2; } - } - ResponseCmndNumber(Settings.blinkcount); -} - -void CmndSavedata(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3600)) { - Settings.save_data = XdrvMailbox.payload; - save_data_counter = Settings.save_data; - } - SettingsSaveAll(); - char stemp1[TOPSZ]; - if (Settings.save_data > 1) { - snprintf_P(stemp1, sizeof(stemp1), PSTR(D_JSON_EVERY " %d " D_UNIT_SECOND), Settings.save_data); - } - ResponseCmndChar((Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data)); -} - -void CmndSetoption(void) -{ - if (XdrvMailbox.index < 114) { - uint32_t ptype; - uint32_t pindex; - if (XdrvMailbox.index <= 31) { - ptype = 2; - pindex = XdrvMailbox.index; - } - else if (XdrvMailbox.index <= 49) { - ptype = 1; - pindex = XdrvMailbox.index -32; - } - else if (XdrvMailbox.index <= 81) { - ptype = 3; - pindex = XdrvMailbox.index -50; - } - else { - ptype = 4; - pindex = XdrvMailbox.index -82; - } - - if (XdrvMailbox.payload >= 0) { - if (1 == ptype) { - uint32_t param_low = 0; - uint32_t param_high = 255; - switch (pindex) { - case P_HOLD_TIME: - case P_MAX_POWER_RETRY: - param_low = 1; - param_high = 250; - break; - } - if ((XdrvMailbox.payload >= param_low) && (XdrvMailbox.payload <= param_high)) { - Settings.param[pindex] = XdrvMailbox.payload; -#ifdef USE_LIGHT - if (P_RGB_REMAP == pindex) { - LightUpdateColorMapping(); - restart_flag = 2; - } -#endif -#if (defined(USE_IR_REMOTE) && defined(USE_IR_RECEIVE)) || defined(USE_IR_REMOTE_FULL) - if (P_IR_UNKNOW_THRESHOLD == pindex) { - IrReceiveUpdateThreshold(); - } -#endif - } else { - ptype = 99; - } - } else { - if (XdrvMailbox.payload <= 1) { - if (2 == ptype) { - switch (pindex) { - case 5: - case 6: - case 7: - case 9: - case 14: - case 22: - case 23: - case 25: - case 27: - ptype = 99; - break; - case 3: - case 15: - restart_flag = 2; - default: - bitWrite(Settings.flag.data, pindex, XdrvMailbox.payload); - } - if (12 == pindex) { - stop_flash_rotate = XdrvMailbox.payload; - SettingsSave(2); - } - #ifdef USE_HOME_ASSISTANT - if ((19 == pindex) || (30 == pindex)) { - HAssDiscover(); - } - #endif - } - else if (3 == ptype) { - bitWrite(Settings.flag3.data, pindex, XdrvMailbox.payload); - switch (pindex) { - case 5: - if (0 == XdrvMailbox.payload) { - restart_flag = 2; - } - break; - case 10: - WiFiSetSleepMode(); - break; - case 18: - case 25: - restart_flag = 2; - break; - } - } - else if (4 == ptype) { - bitWrite(Settings.flag4.data, pindex, XdrvMailbox.payload); - } - } else { - ptype = 99; - } - } - } - - if (ptype < 99) { - if (1 == ptype) { - ResponseCmndIdxNumber(Settings.param[pindex]); - } else { - uint32_t flag = Settings.flag.data; - if (3 == ptype) { - flag = Settings.flag3.data; - } - else if (4 == ptype) { - flag = Settings.flag4.data; - } - ResponseCmndIdxChar(GetStateText(bitRead(flag, pindex))); - } - } - } -} - -void CmndTemperatureResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.temperature_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.temperature_resolution); -} - -void CmndHumidityResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.humidity_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.humidity_resolution); -} - -void CmndPressureResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.pressure_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.pressure_resolution); -} - -void CmndPowerResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.wattage_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.wattage_resolution); -} - -void CmndVoltageResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.voltage_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.voltage_resolution); -} - -void CmndFrequencyResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.frequency_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.frequency_resolution); -} - -void CmndCurrentResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.current_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.current_resolution); -} - -void CmndEnergyResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { - Settings.flag2.energy_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.energy_resolution); -} - -void CmndWeightResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - Settings.flag2.weight_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.weight_resolution); -} - -void CmndModule(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAXMODULE)) { - bool present = false; - if (0 == XdrvMailbox.payload) { - XdrvMailbox.payload = USER_MODULE; - present = true; - } else { - XdrvMailbox.payload--; - present = ValidTemplateModule(XdrvMailbox.payload); - } - if (present) { - Settings.last_module = Settings.module; - Settings.module = XdrvMailbox.payload; - SetModuleType(); - if (Settings.last_module != XdrvMailbox.payload) { - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - Settings.my_gp.io[i] = GPIO_NONE; - } - } - restart_flag = 2; - } - } - Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, ModuleNr(), ModuleName().c_str()); -} - -void CmndModules(void) -{ - uint32_t midx = USER_MODULE; - uint32_t lines = 1; - bool jsflg = false; - for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { - if (i > 0) { midx = pgm_read_byte(kModuleNiceList + i -1); } - if (!jsflg) { - Response_P(PSTR("{\"" D_CMND_MODULES "%d\":{"), lines); - } else { - ResponseAppend_P(PSTR(",")); - } - jsflg = true; - uint32_t j = i ? midx +1 : 0; - if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), j, AnyModuleName(midx).c_str()) > (LOGSZ - TOPSZ)) || (i == sizeof(kModuleNiceList))) { - ResponseJsonEndEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); - jsflg = false; - lines++; - } - } - mqtt_data[0] = '\0'; -} - -void CmndGpio(void) -{ - if (XdrvMailbox.index < sizeof(Settings.my_gp)) { - myio cmodule; - ModuleGpios(&cmodule); - if (ValidGPIO(XdrvMailbox.index, cmodule.io[XdrvMailbox.index]) && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < GPIO_SENSOR_END)) { - bool present = false; - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { - uint32_t midx = pgm_read_byte(kGpioNiceList + i); - if (midx == XdrvMailbox.payload) { present = true; } - } - if (present) { - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if (ValidGPIO(i, cmodule.io[i]) && (Settings.my_gp.io[i] == XdrvMailbox.payload)) { - Settings.my_gp.io[i] = GPIO_NONE; - } - } - Settings.my_gp.io[XdrvMailbox.index] = XdrvMailbox.payload; - restart_flag = 2; - } - } - Response_P(PSTR("{")); - bool jsflg = false; - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if (ValidGPIO(i, cmodule.io[i]) || ((GPIO_USER == XdrvMailbox.payload) && !FlashPin(i))) { - if (jsflg) { ResponseAppend_P(PSTR(",")); } - jsflg = true; - uint8_t sensor_type = Settings.my_gp.io[i]; - if (!ValidGPIO(i, cmodule.io[i])) { - sensor_type = cmodule.io[i]; - if (GPIO_USER == sensor_type) { - sensor_type = GPIO_NONE; - } - } - uint8_t sensor_name_idx = sensor_type; - const char *sensor_names = kSensorNames; - if (sensor_type > GPIO_FIX_START) { - sensor_name_idx = sensor_type - GPIO_FIX_START -1; - sensor_names = kSensorNamesFixed; - } - char stemp1[TOPSZ]; - ResponseAppend_P(PSTR("\"" D_CMND_GPIO "%d\":{\"%d\":\"%s\"}"), - i, sensor_type, GetTextIndexed(stemp1, sizeof(stemp1), sensor_name_idx, sensor_names)); - } - } - if (jsflg) { - ResponseJsonEnd(); - } else { - ResponseCmndChar(D_JSON_NOT_SUPPORTED); - } - } -} - -void CmndGpios(void) -{ - myio cmodule; - ModuleGpios(&cmodule); - uint32_t lines = 1; - bool jsflg = false; - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { - uint32_t midx = pgm_read_byte(kGpioNiceList + i); - if ((XdrvMailbox.payload != 255) && GetUsedInModule(midx, cmodule.io)) { continue; } - if (!jsflg) { - Response_P(PSTR("{\"" D_CMND_GPIOS "%d\":{"), lines); - } else { - ResponseAppend_P(PSTR(",")); - } - jsflg = true; - char stemp1[TOPSZ]; - if ((ResponseAppend_P(PSTR("\"%d\":\"%s\""), midx, GetTextIndexed(stemp1, sizeof(stemp1), midx, kSensorNames)) > (LOGSZ - TOPSZ)) || (i == sizeof(kGpioNiceList) -1)) { - ResponseJsonEndEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, UpperCase(XdrvMailbox.command, XdrvMailbox.command)); - jsflg = false; - lines++; - } - } - mqtt_data[0] = '\0'; -} - -void CmndTemplate(void) -{ - - bool error = false; - - if (strstr(XdrvMailbox.data, "{") == nullptr) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= MAXMODULE)) { - XdrvMailbox.payload--; - if (ValidTemplateModule(XdrvMailbox.payload)) { - ModuleDefault(XdrvMailbox.payload); - if (USER_MODULE == Settings.module) { restart_flag = 2; } - } - } - else if (0 == XdrvMailbox.payload) { - if (Settings.module != USER_MODULE) { - ModuleDefault(Settings.module); - } - } - else if (255 == XdrvMailbox.payload) { - if (Settings.module != USER_MODULE) { - ModuleDefault(Settings.module); - } - snprintf_P(Settings.user_template.name, sizeof(Settings.user_template.name), PSTR("Merged")); - uint32_t j = 0; - for (uint32_t i = 0; i < sizeof(mycfgio); i++) { - if (6 == i) { j = 9; } - if (8 == i) { j = 12; } - if (my_module.io[j] > GPIO_NONE) { - Settings.user_template.gp.io[i] = my_module.io[j]; - } - j++; - } - } - } - else { - if (JsonTemplate(XdrvMailbox.data)) { - if (USER_MODULE == Settings.module) { restart_flag = 2; } - } else { - ResponseCmndChar(D_JSON_INVALID_JSON); - error = true; - } - } - if (!error) { TemplateJson(); } -} - -void CmndPwm(void) -{ - if (pwm_present && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_PWMS)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= Settings.pwm_range) && (pin[GPIO_PWM1 + XdrvMailbox.index -1] < 99)) { - Settings.pwm_value[XdrvMailbox.index -1] = XdrvMailbox.payload; - analogWrite(pin[GPIO_PWM1 + XdrvMailbox.index -1], bitRead(pwm_inverted, XdrvMailbox.index -1) ? Settings.pwm_range - XdrvMailbox.payload : XdrvMailbox.payload); - } - Response_P(PSTR("{")); - MqttShowPWMState(); - ResponseJsonEnd(); - } -} - -void CmndPwmfrequency(void) -{ - if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) { - Settings.pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload; - analogWriteFreq(Settings.pwm_frequency); - } - ResponseCmndNumber(Settings.pwm_frequency); -} - -void CmndPwmrange(void) -{ - if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload > 254) && (XdrvMailbox.payload < 1024))) { - Settings.pwm_range = (1 == XdrvMailbox.payload) ? PWM_RANGE : XdrvMailbox.payload; - for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (Settings.pwm_value[i] > Settings.pwm_range) { - Settings.pwm_value[i] = Settings.pwm_range; - } - } - analogWriteRange(Settings.pwm_range); - } - ResponseCmndNumber(Settings.pwm_range); -} - -void CmndButtonDebounce(void) -{ - if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) { - Settings.button_debounce = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.button_debounce); -} - -void CmndSwitchDebounce(void) -{ - if ((XdrvMailbox.payload > 39) && (XdrvMailbox.payload < 1001)) { - Settings.switch_debounce = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.switch_debounce); -} - -void CmndBaudrate(void) -{ - if (XdrvMailbox.payload >= 300) { - XdrvMailbox.payload /= 300; - uint32_t baudrate = (XdrvMailbox.payload & 0xFFFF) * 300; - SetSerialBaudrate(baudrate); - } - ResponseCmndNumber(Settings.baudrate * 300); -} - -void CmndSerialConfig(void) -{ - - - - - if (XdrvMailbox.data_len > 0) { - if (XdrvMailbox.data_len < 3) { - if ((XdrvMailbox.payload >= TS_SERIAL_5N1) && (XdrvMailbox.payload <= TS_SERIAL_8O2)) { - SetSerialConfig(XdrvMailbox.payload); - } - } - else if ((XdrvMailbox.payload >= 5) && (XdrvMailbox.payload <= 8)) { - uint8_t serial_config = XdrvMailbox.payload -5; - - bool valid = true; - char parity = (XdrvMailbox.data[1] & 0xdf); - if ('E' == parity) { - serial_config += 0x08; - } - else if ('O' == parity) { - serial_config += 0x10; - } - else if ('N' != parity) { - valid = false; - } - - if ('2' == XdrvMailbox.data[2]) { - serial_config += 0x04; - } - else if ('1' != XdrvMailbox.data[2]) { - valid = false; - } - - if (valid) { - SetSerialConfig(serial_config); - } - } - } - ResponseCmndChar(GetSerialConfig().c_str()); -} - -void CmndSerialSend(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { - SetSeriallog(LOG_LEVEL_NONE); - Settings.flag.mqtt_serial = 1; - Settings.flag.mqtt_serial_raw = (XdrvMailbox.index > 3) ? 1 : 0; - if (XdrvMailbox.data_len > 0) { - if (1 == XdrvMailbox.index) { - Serial.printf("%s\n", XdrvMailbox.data); - } - else if (2 == XdrvMailbox.index || 4 == XdrvMailbox.index) { - for (uint32_t i = 0; i < XdrvMailbox.data_len; i++) { - Serial.write(XdrvMailbox.data[i]); - } - } - else if (3 == XdrvMailbox.index) { - uint32_t dat_len = XdrvMailbox.data_len; - Serial.printf("%s", Unescape(XdrvMailbox.data, &dat_len)); - } - else if (5 == XdrvMailbox.index) { - SerialSendRaw(RemoveSpace(XdrvMailbox.data)); - } - ResponseCmndDone(); - } - } -} - -void CmndSerialDelimiter(void) -{ - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.payload < 256)) { - if (XdrvMailbox.payload > 0) { - Settings.serial_delimiter = XdrvMailbox.payload; - } else { - uint32_t dat_len = XdrvMailbox.data_len; - Unescape(XdrvMailbox.data, &dat_len); - Settings.serial_delimiter = XdrvMailbox.data[0]; - } - } - ResponseCmndNumber(Settings.serial_delimiter); -} - -void CmndSyslog(void) -{ - if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { - SetSyslog(XdrvMailbox.payload); - } - Response_P(S_JSON_COMMAND_NVALUE_ACTIVE_NVALUE, XdrvMailbox.command, Settings.syslog_level, syslog_level); -} - -void CmndLoghost(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_SYSLOG_HOST, (SC_DEFAULT == Shortcut()) ? SYS_LOG_HOST : XdrvMailbox.data); - } - ResponseCmndChar(SettingsText(SET_SYSLOG_HOST)); -} - -void CmndLogport(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { - Settings.syslog_port = (1 == XdrvMailbox.payload) ? SYS_LOG_PORT : XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.syslog_port); -} - -void CmndIpAddress(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { - uint32_t address; - if (ParseIp(&address, XdrvMailbox.data)) { - Settings.ip_address[XdrvMailbox.index -1] = address; - - } - char stemp1[TOPSZ]; - snprintf_P(stemp1, sizeof(stemp1), PSTR(" (%s)"), WiFi.localIP().toString().c_str()); - Response_P(S_JSON_COMMAND_INDEX_SVALUE_SVALUE, XdrvMailbox.command, XdrvMailbox.index, IPAddress(Settings.ip_address[XdrvMailbox.index -1]).toString().c_str(), (1 == XdrvMailbox.index) ? stemp1:""); - } -} - -void CmndNtpServer(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_NTP_SERVERS)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_NTPSERVER1, MAX_NTP_SERVERS); - } else { - uint32_t ntp_server = SET_NTPSERVER1 + XdrvMailbox.index -1; - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(ntp_server, - (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? NTP_SERVER1 : (2 == XdrvMailbox.index) ? NTP_SERVER2 : NTP_SERVER3 : XdrvMailbox.data); - SettingsUpdateText(ntp_server, ReplaceCommaWithDot(SettingsText(ntp_server))); - - ntp_force_sync = true; - } - ResponseCmndIdxChar(SettingsText(ntp_server)); - } - } -} - -void CmndAp(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { - switch (XdrvMailbox.payload) { - case 0: - Settings.sta_active ^= 1; - break; - case 1: - case 2: - Settings.sta_active = XdrvMailbox.payload -1; - } - restart_flag = 2; - } - Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active)); -} - -void CmndSsid(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SSIDS)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_STASSID1, MAX_SSIDS); - } else { - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_STASSID1 + XdrvMailbox.index -1, - (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_SSID1 : STA_SSID2 : XdrvMailbox.data); - Settings.sta_active = XdrvMailbox.index -1; - restart_flag = 2; - } - ResponseCmndIdxChar(SettingsText(SET_STASSID1 + XdrvMailbox.index -1)); - } - } -} - -void CmndPassword(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { - if ((XdrvMailbox.data_len > 4) || (SC_CLEAR == Shortcut()) || (SC_DEFAULT == Shortcut())) { - SettingsUpdateText(SET_STAPWD1 + XdrvMailbox.index -1, - (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? STA_PASS1 : STA_PASS2 : XdrvMailbox.data); - Settings.sta_active = XdrvMailbox.index -1; - restart_flag = 2; - ResponseCmndIdxChar(SettingsText(SET_STAPWD1 + XdrvMailbox.index -1)); - } else { - Response_P(S_JSON_COMMAND_INDEX_ASTERISK, XdrvMailbox.command, XdrvMailbox.index); - } - } -} - -void CmndHostname(void) -{ - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { - SettingsUpdateText(SET_HOSTNAME, (SC_DEFAULT == Shortcut()) ? WIFI_HOSTNAME : XdrvMailbox.data); - if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { - SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); - } - restart_flag = 2; - } - ResponseCmndChar(SettingsText(SET_HOSTNAME)); -} - -void CmndWifiConfig(void) -{ - if ((XdrvMailbox.payload >= WIFI_RESTART) && (XdrvMailbox.payload < MAX_WIFI_OPTION)) { - if ((EX_WIFI_SMARTCONFIG == XdrvMailbox.payload) || (EX_WIFI_WPSCONFIG == XdrvMailbox.payload)) { - XdrvMailbox.payload = WIFI_MANAGER; - } - Settings.sta_config = XdrvMailbox.payload; - wifi_state_flag = Settings.sta_config; - if (WifiState() > WIFI_RESTART) { - restart_flag = 2; - } - } - char stemp1[TOPSZ]; - Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_config, GetTextIndexed(stemp1, sizeof(stemp1), Settings.sta_config, kWifiConfig)); -} - -void CmndFriendlyname(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_FRIENDLYNAMES)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_FRIENDLYNAME1, MAX_FRIENDLYNAMES); - } else { - if (XdrvMailbox.data_len > 0) { - char stemp1[TOPSZ]; - if (1 == XdrvMailbox.index) { - snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME)); - } else { - snprintf_P(stemp1, sizeof(stemp1), PSTR(FRIENDLY_NAME "%d"), XdrvMailbox.index); - } - SettingsUpdateText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : (SC_DEFAULT == Shortcut()) ? stemp1 : XdrvMailbox.data); - } - ResponseCmndIdxChar(SettingsText(SET_FRIENDLYNAME1 + XdrvMailbox.index -1)); - } - } -} - -void CmndSwitchMode(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SWITCHES)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_SWITCH_OPTION)) { - Settings.switchmode[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.switchmode[XdrvMailbox.index-1]); - } -} - -void CmndInterlock(void) -{ - - uint32_t max_relays = devices_present; - if (light_type) { max_relays--; } - if (max_relays > sizeof(Settings.interlock[0]) * 8) { max_relays = sizeof(Settings.interlock[0]) * 8; } - if (max_relays > 1) { - if (XdrvMailbox.data_len > 0) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { - for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { Settings.interlock[i] = 0; } - char *group; - char *q; - uint32_t group_index = 0; - power_t relay_mask = 0; - for (group = strtok_r(XdrvMailbox.data, " ", &q); group && group_index < MAX_INTERLOCKS; group = strtok_r(nullptr, " ", &q)) { - char *str; - char *p; - for (str = strtok_r(group, ",", &p); str; str = strtok_r(nullptr, ",", &p)) { - int pbit = atoi(str); - if ((pbit > 0) && (pbit <= max_relays)) { - pbit--; - if (!bitRead(relay_mask, pbit)) { - bitSet(relay_mask, pbit); - bitSet(Settings.interlock[group_index], pbit); - } - } - } - group_index++; - } - for (uint32_t i = 0; i < group_index; i++) { - uint32_t minimal_bits = 0; - for (uint32_t j = 0; j < max_relays; j++) { - if (bitRead(Settings.interlock[i], j)) { minimal_bits++; } - } - if (minimal_bits < 2) { Settings.interlock[i] = 0; } - } - } else { - Settings.flag.interlock = XdrvMailbox.payload &1; - if (Settings.flag.interlock) { - SetDevicePower(power, SRC_IGNORE); - } - } -#ifdef USE_SHUTTER - if (Settings.flag3.shutter_mode) { - ShutterInit(); - } -#endif - } - Response_P(PSTR("{\"" D_CMND_INTERLOCK "\":\"%s\",\"" D_JSON_GROUPS "\":\""), GetStateText(Settings.flag.interlock)); - uint32_t anygroup = 0; - for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { - if (Settings.interlock[i]) { - anygroup++; - ResponseAppend_P(PSTR("%s"), (anygroup > 1) ? " " : ""); - uint32_t anybit = 0; - power_t mask = 1; - for (uint32_t j = 0; j < max_relays; j++) { - if (Settings.interlock[i] & mask) { - anybit++; - ResponseAppend_P(PSTR("%s%d"), (anybit > 1) ? "," : "", j +1); - } - mask <<= 1; - } - } - } - if (!anygroup) { - for (uint32_t j = 1; j <= max_relays; j++) { - ResponseAppend_P(PSTR("%s%d"), (j > 1) ? "," : "", j); - } - } - ResponseAppend_P(PSTR("\"}")); - } else { - Settings.flag.interlock = 0; - ResponseCmndStateText(Settings.flag.interlock); - } -} - -void CmndTeleperiod(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.tele_period = (1 == XdrvMailbox.payload) ? TELE_PERIOD : XdrvMailbox.payload; - if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) Settings.tele_period = 10; - tele_period = Settings.tele_period; - } - ResponseCmndNumber(Settings.tele_period); -} - -void CmndReset(void) -{ - switch (XdrvMailbox.payload) { - case 1: - restart_flag = 211; - ResponseCmndChar(D_JSON_RESET_AND_RESTARTING); - break; - case 2 ... 6: - restart_flag = 210 + XdrvMailbox.payload; - Response_P(PSTR("{\"" D_CMND_RESET "\":\"" D_JSON_ERASE ", " D_JSON_RESET_AND_RESTARTING "\"}")); - break; - case 99: - Settings.bootcount = 0; - Settings.bootcount_reset_time = 0; - ResponseCmndDone(); - break; - default: - ResponseCmndChar(D_JSON_ONE_TO_RESET); - } -} - -void CmndTime(void) -{ - - - - - - - - uint32_t format = Settings.flag2.time_format; - if (XdrvMailbox.data_len > 0) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4)) { - Settings.flag2.time_format = XdrvMailbox.payload -1; - format = Settings.flag2.time_format; - } else { - format = 1; - RtcSetTime(XdrvMailbox.payload); - } - } - mqtt_data[0] = '\0'; - ResponseAppendTimeFormat(format); - ResponseJsonEnd(); -} - -void CmndTimezone(void) -{ - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.payload >= -13)) { - Settings.timezone = XdrvMailbox.payload; - Settings.timezone_minutes = 0; - if (XdrvMailbox.payload < 15) { - char *p = strtok (XdrvMailbox.data, ":"); - if (p) { - p = strtok (nullptr, ":"); - if (p) { - Settings.timezone_minutes = strtol(p, nullptr, 10); - if (Settings.timezone_minutes > 59) { Settings.timezone_minutes = 59; } - } - } - } else { - Settings.timezone = 99; - } - ntp_force_sync = true; - } - if (99 == Settings.timezone) { - ResponseCmndNumber(Settings.timezone); - } else { - char stemp1[TOPSZ]; - snprintf_P(stemp1, sizeof(stemp1), PSTR("%+03d:%02d"), Settings.timezone, Settings.timezone_minutes); - ResponseCmndChar(stemp1); - } -} - -void CmndTimeStdDst(uint32_t ts) -{ - - if (XdrvMailbox.data_len > 0) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { - uint32_t tpos = 0; - int value = 0; - char *p = XdrvMailbox.data; - char *q = p; - while (p && (tpos < 7)) { - if (p > q) { - if (1 == tpos) { Settings.tflag[ts].hemis = value &1; } - if (2 == tpos) { Settings.tflag[ts].week = (value < 0) ? 0 : (value > 4) ? 4 : value; } - if (3 == tpos) { Settings.tflag[ts].month = (value < 1) ? 1 : (value > 12) ? 12 : value; } - if (4 == tpos) { Settings.tflag[ts].dow = (value < 1) ? 1 : (value > 7) ? 7 : value; } - if (5 == tpos) { Settings.tflag[ts].hour = (value < 0) ? 0 : (value > 23) ? 23 : value; } - if (6 == tpos) { Settings.toffset[ts] = (value < -900) ? -900 : (value > 900) ? 900 : value; } - } - p = Trim(p); - if (tpos && (*p == ',')) { p++; } - p = Trim(p); - q = p; - value = strtol(p, &p, 10); - tpos++; - } - ntp_force_sync = true; - } else { - if (0 == XdrvMailbox.payload) { - if (0 == ts) { - SettingsResetStd(); - } else { - SettingsResetDst(); - } - } - ntp_force_sync = true; - } - } - Response_P(PSTR("{\"%s\":{\"Hemisphere\":%d,\"Week\":%d,\"Month\":%d,\"Day\":%d,\"Hour\":%d,\"Offset\":%d}}"), - XdrvMailbox.command, Settings.tflag[ts].hemis, Settings.tflag[ts].week, Settings.tflag[ts].month, Settings.tflag[ts].dow, Settings.tflag[ts].hour, Settings.toffset[ts]); -} - -void CmndTimeStd(void) -{ - CmndTimeStdDst(0); -} - -void CmndTimeDst(void) -{ - CmndTimeStdDst(1); -} - -void CmndAltitude(void) -{ - if ((XdrvMailbox.data_len > 0) && ((XdrvMailbox.payload >= -30000) && (XdrvMailbox.payload <= 30000))) { - Settings.altitude = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.altitude); -} - -void CmndLedPower(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_LEDS)) { - if (99 == pin[GPIO_LEDLNK]) { XdrvMailbox.index = 1; } - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { - Settings.ledstate &= 8; - uint32_t mask = 1 << (XdrvMailbox.index -1); - switch (XdrvMailbox.payload) { - case 0: - led_power &= (0xFF ^ mask); - Settings.ledstate = 0; - break; - case 1: - led_power |= mask; - Settings.ledstate = 8; - break; - case 2: - led_power ^= mask; - Settings.ledstate ^= 8; - break; - } - blinks = 0; - if (99 == pin[GPIO_LEDLNK]) { - SetLedPower(Settings.ledstate &8); - } else { - SetLedPowerIdx(XdrvMailbox.index -1, (led_power & mask)); - } - } - bool state = bitRead(led_power, XdrvMailbox.index -1); - if (99 == pin[GPIO_LEDLNK]) { - state = bitRead(Settings.ledstate, 3); - } - ResponseCmndIdxChar(GetStateText(state)); - } -} - -void CmndLedState(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_LED_OPTION)) { - Settings.ledstate = XdrvMailbox.payload; - if (!Settings.ledstate) { - SetLedPowerAll(0); - SetLedLink(0); - } - } - ResponseCmndNumber(Settings.ledstate); -} - -void CmndLedMask(void) -{ - if (XdrvMailbox.data_len > 0) { - Settings.ledmask = XdrvMailbox.payload; - } - char stemp1[TOPSZ]; - snprintf_P(stemp1, sizeof(stemp1), PSTR("%d (0x%04X)"), Settings.ledmask, Settings.ledmask); - ResponseCmndChar(stemp1); -} - -void CmndWifiPower(void) -{ - if (XdrvMailbox.data_len > 0) { - Settings.wifi_output_power = (uint8_t)(CharToFloat(XdrvMailbox.data) * 10); - if (Settings.wifi_output_power > 205) { - Settings.wifi_output_power = 205; - } - WifiSetOutputPower(); - } - ResponseCmndChar(WifiGetOutputPower().c_str()); -} - -#ifdef USE_I2C -void CmndI2cScan(void) -{ - if (i2c_flg) { - I2cScan(mqtt_data, sizeof(mqtt_data)); - } -} - -void CmndI2cDriver(void) -{ - if (XdrvMailbox.index < MAX_I2C_DRIVERS) { - if (XdrvMailbox.payload >= 0) { - bitWrite(Settings.i2c_drivers[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); - restart_flag = 2; - } - } - Response_P(PSTR("{\"" D_CMND_I2CDRIVER "\":")); - I2cDriverState(); - ResponseJsonEnd(); -} -#endif - -void CmndSensor(void) -{ - XsnsCall(FUNC_COMMAND_SENSOR); -} - -void CmndDriver(void) -{ - XdrvCall(FUNC_COMMAND_DRIVER); -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_crash_recorder.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_crash_recorder.ino" -const uint32_t crash_magic = 0x53415400; -const uint32_t crash_rtc_offset = 32; -const uint32_t crash_dump_max_len = 31; - - - - - - -extern "C" void custom_crash_callback(struct rst_info * rst_info, uint32_t stack, uint32_t stack_end ) -{ - uint32_t addr_written = 0; - uint32_t value; - - for (uint32_t i = stack; i < stack_end; i += 4) { - value = *((uint32_t*) i); - if ((value >= 0x40000000) && (value < 0x40300000)) { - ESP.rtcUserMemoryWrite(crash_rtc_offset + addr_written, (uint32_t*)&value, sizeof(value)); - addr_written++; - if (addr_written >= crash_dump_max_len) { break; } - } - } - value = crash_magic + addr_written; - ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); -} - - -void CmndCrash(void) -{ - volatile uint32_t dummy; - dummy = *((uint32_t*) 0x00000000); -} - - -void CmndWDT(void) -{ - volatile uint32_t dummy = 0; - while (1) { - dummy++; - } -} - - -void CmndBlockedLoop(void) -{ - while (1) { - delay(1000); - } -} - - -void CrashDumpClear(void) -{ - uint32_t value = 0; - ESP.rtcUserMemoryWrite(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); -} - - - - - -bool CrashFlag(void) -{ - return ((ResetReason() == REASON_EXCEPTION_RST) || (ResetReason() == REASON_SOFT_WDT_RST) || oswatch_blocked_loop); -} - -void CrashDump(void) -{ - ResponseAppend_P(PSTR("{\"Exception\":%d,\"Reason\":\"%s\",\"EPC\":[\"%08x\",\"%08x\",\"%08x\"],\"EXCVADDR\":\"%08x\",\"DEPC\":\"%08x\""), - resetInfo.exccause, - GetResetReason().c_str(), - resetInfo.epc1, - resetInfo.epc2, - resetInfo.epc3, - resetInfo.excvaddr, - resetInfo.depc); - - uint32_t value; - ESP.rtcUserMemoryRead(crash_rtc_offset + crash_dump_max_len, (uint32_t*)&value, sizeof(value)); - if (crash_magic == (value & 0xFFFFFF00)) { - ResponseAppend_P(PSTR(",\"CallChain\":[")); - uint32_t count = value & 0x3F; - for (uint32_t i = 0; i < count; i++) { - ESP.rtcUserMemoryRead(crash_rtc_offset +i, (uint32_t*)&value, sizeof(value)); - if (i > 0) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"%08x\""), value); - } - ResponseAppend_P(PSTR("]")); - } - - ResponseJsonEnd(); -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_esptool.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_esptool.ino" -#define USE_ESPTOOL -#ifdef USE_ESPTOOL - - - - - - - -#define READ_REG(REG) (*((volatile uint32_t *)(REG))) -#define WRITE_REG(REG,VAL) *((volatile uint32_t *)(REG)) = (VAL) -#define REG_SET_MASK(reg,mask) WRITE_REG((reg), (READ_REG(reg)|(mask))) - -#define SPI_BASE_REG 0x60000200 - -#define SPI_CMD_REG (SPI_BASE_REG + 0x00) -#define SPI_FLASH_RDSR (1<<27) -#define SPI_FLASH_SE (1<<24) -#define SPI_FLASH_BE (1<<23) -#define SPI_FLASH_WREN (1<<30) - -#define SPI_ADDR_REG (SPI_BASE_REG + 0x04) -#define SPI_CTRL_REG (SPI_BASE_REG + 0x08) -#define SPI_RD_STATUS_REG (SPI_BASE_REG + 0x10) -#define SPI_W0_REG (SPI_BASE_REG + 0x40) -#define SPI_EXT2_REG (SPI_BASE_REG + 0xF8) - -#define SPI_ST 0x7 - - -#define SECTORS_PER_BLOCK (FLASH_BLOCK_SIZE / SPI_FLASH_SEC_SIZE) - - -static const uint32_t STATUS_WIP_BIT = (1 << 0); - - -inline static void spi_wait_ready(void) -{ - while((READ_REG(SPI_EXT2_REG) & SPI_ST)) { } -} - - - -static bool spiflash_is_ready(void) -{ - spi_wait_ready(); - WRITE_REG(SPI_RD_STATUS_REG, 0); - WRITE_REG(SPI_CMD_REG, SPI_FLASH_RDSR); - while(READ_REG(SPI_CMD_REG) != 0) { } - uint32_t status_value = READ_REG(SPI_RD_STATUS_REG); - return (status_value & STATUS_WIP_BIT) == 0; -} - -static void spi_write_enable(void) -{ - while(!spiflash_is_ready()) { } - WRITE_REG(SPI_CMD_REG, SPI_FLASH_WREN); - while(READ_REG(SPI_CMD_REG) != 0) { } -} - -bool EsptoolEraseSector(uint32_t sector) -{ - spi_write_enable(); - spi_wait_ready(); - - WRITE_REG(SPI_ADDR_REG, (sector * SPI_FLASH_SEC_SIZE) & 0xffffff); - WRITE_REG(SPI_CMD_REG, SPI_FLASH_SE); - while(READ_REG(SPI_CMD_REG) != 0) { } - while(!spiflash_is_ready()) { } - - return true; -} - -void EsptoolErase(uint32_t start_sector, uint32_t end_sector) -{ - int next_erase_sector = start_sector; - int remaining_erase_sector = end_sector - start_sector; - - while (remaining_erase_sector > 0) { - spi_write_enable(); - - uint32_t command = SPI_FLASH_SE; - uint32_t sectors_to_erase = 1; - if (remaining_erase_sector >= SECTORS_PER_BLOCK && - next_erase_sector % SECTORS_PER_BLOCK == 0) { - command = SPI_FLASH_BE; - sectors_to_erase = SECTORS_PER_BLOCK; - } - uint32_t addr = next_erase_sector * SPI_FLASH_SEC_SIZE; - - spi_wait_ready(); - WRITE_REG(SPI_ADDR_REG, addr & 0xffffff); - WRITE_REG(SPI_CMD_REG, command); - while(READ_REG(SPI_CMD_REG) != 0) { } - remaining_erase_sector -= sectors_to_erase; - next_erase_sector += sectors_to_erase; - - while (!spiflash_is_ready()) { } - yield(); - OsWatchLoop(); - } -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_features.ino" -# 24 "C:/shared/sonoff/Git/Tasmota/tasmota/support_features.ino" -void GetFeatures(void) -{ - feature_drv1 = 0x00000000; - -#ifdef USE_ENERGY_MARGIN_DETECTION - feature_drv1 |= 0x00000001; -#endif -#ifdef USE_LIGHT - feature_drv1 |= 0x00000002; -#endif -#ifdef USE_I2C - feature_drv1 |= 0x00000004; -#endif -#ifdef USE_SPI - feature_drv1 |= 0x00000008; -#endif -#ifdef USE_DISCOVERY - feature_drv1 |= 0x00000010; -#endif -#ifdef USE_ARDUINO_OTA - feature_drv1 |= 0x00000020; -#endif -#ifdef USE_MQTT_TLS - feature_drv1 |= 0x00000040; -#endif -#ifdef USE_WEBSERVER - feature_drv1 |= 0x00000080; -#endif -#ifdef WEBSERVER_ADVERTISE - feature_drv1 |= 0x00000100; -#endif -#ifdef USE_EMULATION_HUE - feature_drv1 |= 0x00000200; -#endif -#if (MQTT_LIBRARY_TYPE == MQTT_PUBSUBCLIENT) - feature_drv1 |= 0x00000400; -#endif -#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) - -#endif -#if (MQTT_LIBRARY_TYPE == MQTT_ESPMQTTARDUINO) - -#endif -#ifdef MQTT_HOST_DISCOVERY - feature_drv1 |= 0x00002000; -#endif -#ifdef USE_ARILUX_RF - feature_drv1 |= 0x00004000; -#endif -#if defined(USE_LIGHT) && defined(USE_WS2812) - feature_drv1 |= 0x00008000; -#endif -#ifdef USE_WS2812_DMA - feature_drv1 |= 0x00010000; -#endif -#if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL) - feature_drv1 |= 0x00020000; -#endif -#ifdef USE_IR_HVAC - feature_drv1 |= 0x00040000; -#endif -#ifdef USE_IR_RECEIVE - feature_drv1 |= 0x00080000; -#endif -#ifdef USE_DOMOTICZ - feature_drv1 |= 0x00100000; -#endif -#ifdef USE_DISPLAY - feature_drv1 |= 0x00200000; -#endif -#ifdef USE_HOME_ASSISTANT - feature_drv1 |= 0x00400000; -#endif -#ifdef USE_SERIAL_BRIDGE - feature_drv1 |= 0x00800000; -#endif -#ifdef USE_TIMERS - feature_drv1 |= 0x01000000; -#endif -#ifdef USE_SUNRISE - feature_drv1 |= 0x02000000; -#endif -#ifdef USE_TIMERS_WEB - feature_drv1 |= 0x04000000; -#endif -#ifdef USE_RULES - feature_drv1 |= 0x08000000; -#endif -#ifdef USE_KNX - feature_drv1 |= 0x10000000; -#endif -#ifdef USE_WPS - feature_drv1 |= 0x20000000; -#endif -#ifdef USE_SMARTCONFIG - feature_drv1 |= 0x40000000; -#endif -#ifdef USE_ENERGY_POWER_LIMIT - feature_drv1 |= 0x80000000; -#endif - - - - feature_drv2 = 0x00000000; - -#ifdef USE_CONFIG_OVERRIDE - feature_drv2 |= 0x00000001; -#endif -#ifdef FIRMWARE_MINIMAL - feature_drv2 |= 0x00000002; -#endif -#ifdef FIRMWARE_SENSORS - feature_drv2 |= 0x00000004; -#endif -#ifdef FIRMWARE_CLASSIC - feature_drv2 |= 0x00000008; -#endif -#ifdef FIRMWARE_KNX_NO_EMULATION - feature_drv2 |= 0x00000010; -#endif -#ifdef USE_DISPLAY_MODES1TO5 - feature_drv2 |= 0x00000020; -#endif -#ifdef USE_DISPLAY_GRAPH - feature_drv2 |= 0x00000040; -#endif -#ifdef USE_DISPLAY_LCD - feature_drv2 |= 0x00000080; -#endif -#ifdef USE_DISPLAY_SSD1306 - feature_drv2 |= 0x00000100; -#endif -#ifdef USE_DISPLAY_MATRIX - feature_drv2 |= 0x00000200; -#endif -#ifdef USE_DISPLAY_ILI9341 - feature_drv2 |= 0x00000400; -#endif -#ifdef USE_DISPLAY_EPAPER_29 - feature_drv2 |= 0x00000800; -#endif -#ifdef USE_DISPLAY_SH1106 - feature_drv2 |= 0x00001000; -#endif -#ifdef USE_MP3_PLAYER - feature_drv2 |= 0x00002000; -#endif -#ifdef USE_PCA9685 - feature_drv2 |= 0x00004000; -#endif -#if defined(USE_LIGHT) && defined(USE_TUYA_MCU) - feature_drv2 |= 0x00008000; -#endif -#ifdef USE_RC_SWITCH - feature_drv2 |= 0x00010000; -#endif -#if defined(USE_LIGHT) && defined(USE_ARMTRONIX_DIMMERS) - feature_drv2 |= 0x00020000; -#endif -#if defined(USE_LIGHT) && defined(USE_SM16716) - feature_drv2 |= 0x00040000; -#endif -#ifdef USE_SCRIPT - feature_drv2 |= 0x00080000; -#endif -#ifdef USE_EMULATION_WEMO - feature_drv2 |= 0x00100000; -#endif -#ifdef USE_SONOFF_IFAN - feature_drv2 |= 0x00200000; -#endif -#ifdef USE_ZIGBEE - feature_drv2 |= 0x00400000; -#endif -#ifdef NO_EXTRA_4K_HEAP - feature_drv2 |= 0x00800000; -#endif -#ifdef VTABLES_IN_IRAM - feature_drv2 |= 0x01000000; -#endif -#ifdef VTABLES_IN_DRAM - feature_drv2 |= 0x02000000; -#endif -#ifdef VTABLES_IN_FLASH - feature_drv2 |= 0x04000000; -#endif -#ifdef PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH - feature_drv2 |= 0x08000000; -#endif -#ifdef PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY - feature_drv2 |= 0x10000000; -#endif -#ifdef PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH - feature_drv2 |= 0x20000000; -#endif -#ifdef DEBUG_THEO - feature_drv2 |= 0x40000000; -#endif -#ifdef USE_DEBUG_DRIVER - feature_drv2 |= 0x80000000; -#endif - - - - feature_sns1 = 0x00000000; - -#ifdef USE_COUNTER - feature_sns1 |= 0x00000001; -#endif -#ifdef USE_ADC_VCC - feature_sns1 |= 0x00000002; -#endif -#ifdef USE_ENERGY_SENSOR - feature_sns1 |= 0x00000004; -#endif -#ifdef USE_PZEM004T - feature_sns1 |= 0x00000008; -#endif -#ifdef USE_DS18B20 - feature_sns1 |= 0x00000010; -#endif -#ifdef USE_DS18x20_LEGACY - feature_sns1 |= 0x00000020; -#endif -#ifdef USE_DS18x20 - feature_sns1 |= 0x00000040; -#endif -#ifdef USE_DHT - feature_sns1 |= 0x00000080; -#endif -#ifdef USE_SHT - feature_sns1 |= 0x00000100; -#endif -#ifdef USE_HTU - feature_sns1 |= 0x00000200; -#endif -#ifdef USE_BMP - feature_sns1 |= 0x00000400; -#endif -#ifdef USE_BME680 - feature_sns1 |= 0x00000800; -#endif -#ifdef USE_BH1750 - feature_sns1 |= 0x00001000; -#endif -#ifdef USE_VEML6070 - feature_sns1 |= 0x00002000; -#endif -#ifdef USE_ADS1115_I2CDEV - feature_sns1 |= 0x00004000; -#endif -#ifdef USE_ADS1115 - feature_sns1 |= 0x00008000; -#endif -#ifdef USE_INA219 - feature_sns1 |= 0x00010000; -#endif -#ifdef USE_SHT3X - feature_sns1 |= 0x00020000; -#endif -#ifdef USE_MHZ19 - feature_sns1 |= 0x00040000; -#endif -#ifdef USE_TSL2561 - feature_sns1 |= 0x00080000; -#endif -#ifdef USE_SENSEAIR - feature_sns1 |= 0x00100000; -#endif -#ifdef USE_PMS5003 - feature_sns1 |= 0x00200000; -#endif -#ifdef USE_MGS - feature_sns1 |= 0x00400000; -#endif -#ifdef USE_NOVA_SDS - feature_sns1 |= 0x00800000; -#endif -#ifdef USE_SGP30 - feature_sns1 |= 0x01000000; -#endif -#ifdef USE_SR04 - feature_sns1 |= 0x02000000; -#endif -#ifdef USE_SDM120 - feature_sns1 |= 0x04000000; -#endif -#ifdef USE_SI1145 - feature_sns1 |= 0x08000000; -#endif -#ifdef USE_SDM630 - feature_sns1 |= 0x10000000; -#endif -#ifdef USE_LM75AD - feature_sns1 |= 0x20000000; -#endif -#ifdef USE_APDS9960 - feature_sns1 |= 0x40000000; -#endif -#ifdef USE_TM1638 - feature_sns1 |= 0x80000000; -#endif - - - - feature_sns2 = 0x00000000; - -#ifdef USE_MCP230xx - feature_sns2 |= 0x00000001; -#endif -#ifdef USE_MPR121 - feature_sns2 |= 0x00000002; -#endif -#ifdef USE_CCS811 - feature_sns2 |= 0x00000004; -#endif -#ifdef USE_MPU6050 - feature_sns2 |= 0x00000008; -#endif -#ifdef USE_MCP230xx_OUTPUT - feature_sns2 |= 0x00000010; -#endif -#ifdef USE_MCP230xx_DISPLAYOUTPUT - feature_sns2 |= 0x00000020; -#endif -#ifdef USE_HLW8012 - feature_sns2 |= 0x00000040; -#endif -#ifdef USE_CSE7766 - feature_sns2 |= 0x00000080; -#endif -#ifdef USE_MCP39F501 - feature_sns2 |= 0x00000100; -#endif -#ifdef USE_PZEM_AC - feature_sns2 |= 0x00000200; -#endif -#ifdef USE_DS3231 - feature_sns2 |= 0x00000400; -#endif -#ifdef USE_HX711 - feature_sns2 |= 0x00000800; -#endif -#ifdef USE_PZEM_DC - feature_sns2 |= 0x00001000; -#endif -#ifdef USE_TX20_WIND_SENSOR - feature_sns2 |= 0x00002000; -#endif -#ifdef USE_MGC3130 - feature_sns2 |= 0x00004000; -#endif -#ifdef USE_RF_SENSOR - feature_sns2 |= 0x00008000; -#endif -#ifdef USE_THEO_V2 - feature_sns2 |= 0x00010000; -#endif -#ifdef USE_ALECTO_V2 - feature_sns2 |= 0x00020000; -#endif -#ifdef USE_AZ7798 - feature_sns2 |= 0x00040000; -#endif -#ifdef USE_MAX31855 - feature_sns2 |= 0x00080000; -#endif -#ifdef USE_PN532_HSU - feature_sns2 |= 0x00100000; -#endif -#ifdef USE_MAX44009 - feature_sns2 |= 0x00200000; -#endif -#ifdef USE_SCD30 - feature_sns2 |= 0x00400000; -#endif -#ifdef USE_HRE - feature_sns2 |= 0x00800000; -#endif -#ifdef USE_ADE7953 - feature_sns2 |= 0x01000000; -#endif -#ifdef USE_SPS30 - feature_sns2 |= 0x02000000; -#endif -#ifdef USE_VL53L0X - feature_sns2 |= 0x04000000; -#endif -#ifdef USE_MLX90614 - feature_sns2 |= 0x08000000; -#endif -#ifdef USE_MAX31865 - feature_sns2 |= 0x10000000; -#endif -#ifdef USE_CHIRP - feature_sns2 |= 0x20000000; -#endif -#ifdef USE_SOLAX_X1 - feature_sns2 |= 0x40000000; -#endif -#ifdef USE_PAJ7620 - feature_sns2 |= 0x80000000; -#endif - - - - feature5 = 0x00000000; - -#ifdef USE_BUZZER - feature5 |= 0x00000001; -#endif -#ifdef USE_RDM6300 - feature5 |= 0x00000002; -#endif -#ifdef USE_IBEACON - feature5 |= 0x00000004; -#endif -#ifdef USE_SML_M - feature5 |= 0x00000008; -#endif -#ifdef USE_INA226 - feature5 |= 0x00000010; -#endif -#ifdef USE_A4988_STEPPER - feature5 |= 0x00000020; -#endif -#ifdef USE_DDS2382 - feature5 |= 0x00000040; -#endif -#ifdef USE_SM2135 - feature5 |= 0x00000080; -#endif -#ifdef USE_SHUTTER - feature5 |= 0x00000100; -#endif -#ifdef USE_PCF8574 - feature5 |= 0x00000200; -#endif -#ifdef USE_DDSU666 - feature5 |= 0x00000400; -#endif -#ifdef USE_DEEPSLEEP - feature5 |= 0x00000800; -#endif -#ifdef USE_SONOFF_SC - feature5 |= 0x00001000; -#endif -#ifdef USE_SONOFF_RF - feature5 |= 0x00002000; -#endif -#ifdef USE_SONOFF_L1 - feature5 |= 0x00004000; -#endif -#ifdef USE_EXS_DIMMER - feature5 |= 0x00008000; -#endif -#ifdef USE_ARDUINO_SLAVE - feature5 |= 0x00010000; -#endif -#ifdef USE_HIH6 - feature5 |= 0x00020000; -#endif -#ifdef USE_HPMA - feature5 |= 0x00040000; -#endif -#ifdef USE_TSL2591 - feature5 |= 0x00080000; -#endif -#ifdef USE_DHT12 - feature5 |= 0x00100000; -#endif -#ifdef USE_DS1624 - feature5 |= 0x00200000; -#endif -#ifdef USE_GPS - feature5 |= 0x00400000; -#endif -#ifdef USE_HOTPLUG - feature5 |= 0x00800000; -#endif -#ifdef USE_NRF24 - feature5 |= 0x01000000; -#endif -#ifdef USE_MIBLE - feature5 |= 0x02000000; -#endif -#ifdef USE_HM10 - feature5 |= 0x04000000; -#endif -#ifdef USE_LE01MR - feature5 |= 0x08000000; -#endif -#ifdef USE_AHT10 - feature5 |= 0x10000000; -#endif - - - - - - - - feature6 = 0x00000000; -# 568 "C:/shared/sonoff/Git/Tasmota/tasmota/support_features.ino" -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_flash_log.ino" -# 39 "C:/shared/sonoff/Git/Tasmota/tasmota/support_flash_log.ino" -#ifdef USE_FLOG - -class FLOG - -#define MAGIC_WORD_FL 0x464c - -{ - -struct header_t{ - uint16_t magic_word; - uint16_t padding; - uint32_t physical_start_sector:10; - uint32_t number:10; - uint32_t buf_pointer:12; - }; - -private: -void _readSector(uint8_t one_sector); -void _eraseSector(uint8_t one_sector); -void _writeSector(uint8_t one_sector); -void _clearBuffer(void); -void _searchSaves(void); -void _findFirstErasedSector(void); -void _showBuffer(void); -void _initBuffer(void); -void _saveBufferToSector(void); -header_t _saved_header; - -public: - uint32_t size; - uint32_t start; - uint32_t end; - uint16_t num_sectors; - - uint16_t first_erased_sector; - uint16_t current_sector; - - uint16_t bytes_left; - uint16_t sectors_left; - - uint8_t mode = 0; - bool found_saved_data = false; - bool ready = false; - bool running_download = false; - bool recording = false; - - union sector_t{ - uint32_t dword_buffer[FLASH_SECTOR_SIZE/4]; - uint8_t byte_buffer[FLASH_SECTOR_SIZE]; - header_t header; - } sector; - - void init(void); - void addToBuffer(uint8_t src[], uint32_t size); - void startRecording(bool append); - void stopRecording(void); - - typedef void (*CallbackNoArgs) (); - typedef void (*CallbackWithArgs) (uint8_t *_record); - - void startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter); -}; - -extern "C" uint32_t _SPIFFS_start; -extern "C" uint32_t _FS_start; - - - - -void FLOG::init(void) -{ -DEBUG_SENSOR_LOG(PSTR("FLOG: init ...")); -size = ESP.getSketchSize(); - -start = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) -end = (uint32_t)&_SPIFFS_start - 0x40200000; -#else -end = (uint32_t)&_FS_start - 0x40200000; -#endif -num_sectors = (end - start)/FLASH_SECTOR_SIZE; -DEBUG_SENSOR_LOG(PSTR("FLOG: size: 0x%lx, start: 0x%lx, end: 0x%lx, num_sectors(dec): %lu"), size, start, end, num_sectors ); -_findFirstErasedSector(); -if(first_erased_sector == 0xffff){ - _eraseSector(0); - first_erased_sector = 0; -} -_searchSaves(); -_initBuffer(); -ready = true; -} -# 142 "C:/shared/sonoff/Git/Tasmota/tasmota/support_flash_log.ino" -void FLOG::_readSector(uint8_t one_sector){ - DEBUG_SENSOR_LOG(PSTR("FLOG: read sector number: %u" ), one_sector); - ESP.flashRead(start+(one_sector * FLASH_SECTOR_SIZE),(uint32_t *)§or.dword_buffer, FLASH_SECTOR_SIZE); -} - - - - - -void FLOG::_eraseSector(uint8_t one_sector){ - DEBUG_SENSOR_LOG(PSTR("FLOG: erasing sector number: %u" ), one_sector); - ESP.flashEraseSector((start/FLASH_SECTOR_SIZE)+one_sector); -} - - - - - -void FLOG::_writeSector(uint8_t one_sector){ - DEBUG_SENSOR_LOG(PSTR("FLOG: write buffer to sector number: %u" ), one_sector); - ESP.flashWrite(start+(one_sector * FLASH_SECTOR_SIZE),(uint32_t *)§or.dword_buffer, FLASH_SECTOR_SIZE); -} - - - - -void FLOG::_clearBuffer(){ - for (uint32_t i = sizeof(sector.header)/4; i<(sizeof(sector.dword_buffer)/4); i++){ - sector.dword_buffer[i] = 0; - } - sector.header.buf_pointer = sizeof(sector.header); - -} - - - - -void FLOG::_saveBufferToSector(){ - DEBUG_SENSOR_LOG(PSTR("FLOG: write buffer to current sector: %u" ),current_sector); - _writeSector(current_sector); - if(current_sector == num_sectors){ - current_sector = 0; - } - else{ - current_sector++; - } - _eraseSector(current_sector); - _clearBuffer(); - sector.header.number++; - DEBUG_SENSOR_LOG(PSTR("FLOG: new sector header number: %u" ),sector.header.number); -} - - - - - -void FLOG::_findFirstErasedSector(){ - for (uint32_t i = 0; i3){ - break; - } - } -} - - - - - - - -void FLOG::addToBuffer(uint8_t src[], uint32_t size){ - if(mode == 0){ - if(sector.header.number == num_sectors && !ready){ - return; - } - } - if((FLASH_SECTOR_SIZE-sector.header.buf_pointer-sizeof(sector.header))>size){ - - - - memcpy(sector.byte_buffer + sector.header.buf_pointer, src, size); - sector.header.buf_pointer+=size; - - } - else{ - DEBUG_SENSOR_LOG(PSTR("FLOG: save buffer to flash sector: %u"), current_sector); - DEBUG_SENSOR_LOG(PSTR("FLOG: current buf_pointer: %u"), sector.header.buf_pointer); - _saveBufferToSector(); - sectors_left++; - - if((FLASH_SECTOR_SIZE-sector.header.buf_pointer-sizeof(sector.header))>size){ - memcpy(sector.byte_buffer + sector.header.buf_pointer, src, size); - sector.header.buf_pointer+=size; - } - } -} - - - - - - -void FLOG::startRecording(bool append){ - if(recording){ - DEBUG_SENSOR_LOG(PSTR("FLOG: already recording")); - return; - } - recording = true; - DEBUG_SENSOR_LOG(PSTR("FLOG: start recording")); - _initBuffer(); - if(!found_saved_data) { - append = false; - } - if(append){ - sector.header.number = _saved_header.number+1; - sector.header.physical_start_sector = _saved_header.physical_start_sector; - } - else{ - sector.header.physical_start_sector = (uint16_t)first_erased_sector; - found_saved_data = false; - sectors_left = 0; - } - } - - - - - -void FLOG::stopRecording(void){ - _saveBufferToSector(); - _findFirstErasedSector(); - _searchSaves(); - _initBuffer(); - recording = false; - found_saved_data = true; - } -# 381 "C:/shared/sonoff/Git/Tasmota/tasmota/support_flash_log.ino" - void FLOG::startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter){ - - _readSector(sector.header.physical_start_sector); - uint32_t next_sector = sector.header.physical_start_sector; - bytes_left = sector.header.buf_pointer - sizeof(sector.header); - DEBUG_SENSOR_LOG(PSTR("FLOG: create file for download, will process %u bytes"), bytes_left); - running_download = true; - - sendHeader(); - - while(sectors_left){ - DEBUG_SENSOR_LOG(PSTR("FLOG: next sector: %u"), next_sector); - - uint32_t k = sizeof(sector.header); - while(bytes_left){ - - uint8_t *_record_start = (uint8_t*)§or.byte_buffer[k]; - - sendRecord(_record_start); - if(k%128 == 0){ - - OsWatchLoop(); - delay(sleep); - } - k+=size; - if(bytes_left>7){ - bytes_left-=size; - } - else{ - bytes_left = 0; - DEBUG_SENSOR_LOG(PSTR("FLOG: Flog->bytes_left not dividable by 8 ??????")); - } - } - next_sector++; - if(next_sector>num_sectors){ - next_sector = 0; - } - sectors_left--; - _readSector(next_sector); - bytes_left = sector.header.buf_pointer - sizeof(sector.header); - OsWatchLoop(); - delay(sleep); - } - running_download = false; - - sendFooter(); - - _searchSaves(); - _initBuffer(); - } - - #endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" -# 23 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" -float fmodf(float x, float y) -{ - - union {float f; uint32_t i;} ux = {x}, uy = {y}; - int ex = ux.i>>23 & 0xff; - int ey = uy.i>>23 & 0xff; - uint32_t sx = ux.i & 0x80000000; - uint32_t i; - uint32_t uxi = ux.i; - - if (uy.i<<1 == 0 || isnan(y) || ex == 0xff) - return (x*y)/(x*y); - if (uxi<<1 <= uy.i<<1) { - if (uxi<<1 == uy.i<<1) - return 0*x; - return x; - } - - - if (!ex) { - for (i = uxi<<9; i>>31 == 0; ex--, i <<= 1); - uxi <<= -ex + 1; - } else { - uxi &= -1U >> 9; - uxi |= 1U << 23; - } - if (!ey) { - for (i = uy.i<<9; i>>31 == 0; ey--, i <<= 1); - uy.i <<= -ey + 1; - } else { - uy.i &= -1U >> 9; - uy.i |= 1U << 23; - } - - - for (; ex > ey; ex--) { - i = uxi - uy.i; - if (i >> 31 == 0) { - if (i == 0) - return 0*x; - uxi = i; - } - uxi <<= 1; - } - i = uxi - uy.i; - if (i >> 31 == 0) { - if (i == 0) - return 0*x; - uxi = i; - } - for (; uxi>>23 == 0; uxi <<= 1, ex--); - - - if (ex > 0) { - uxi -= 1U << 23; - uxi |= (uint32_t)ex << 23; - } else { - uxi >>= -ex + 1; - } - uxi |= sx; - ux.i = uxi; - return ux.f; -} - - -double FastPrecisePow(double a, double b) -{ - - - int e = abs((int)b); - union { - double d; - int x[2]; - } u = { a }; - u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447); - u.x[0] = 0; - - - double r = 1.0; - while (e) { - if (e & 1) { - r *= a; - } - a *= a; - e >>= 1; - } - return r * u.d; -} - -float FastPrecisePowf(const float x, const float y) -{ - - return (float)FastPrecisePow(x, y); -} - -double TaylorLog(double x) -{ - - - if (x <= 0.0) { return NAN; } - double z = (x + 1) / (x - 1); - double step = ((x - 1) * (x - 1)) / ((x + 1) * (x + 1)); - double totalValue = 0; - double powe = 1; - for (uint32_t count = 0; count < 10; count++) { - z *= step; - double y = (1 / powe) * z; - totalValue = totalValue + y; - powe = powe + 2; - } - totalValue *= 2; -# 144 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" - return totalValue; -} -# 154 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" -inline float sinf(float x) { return sin_52(x); } -inline float cosf(float x) { return cos_52(x); } -inline float tanf(float x) { return tan_56(x); } -inline float atanf(float x) { return atan_66(x); } -inline float asinf(float x) { return asinf1(x); } -inline float acosf(float x) { return acosf1(x); } -inline float sqrtf(float x) { return sqrt1(x); } -inline float powf(float x, float y) { return FastPrecisePow(x, y); } - - -double const f_pi = 3.1415926535897932384626433; -double const f_twopi = 2.0 * f_pi; -double const f_two_over_pi = 2.0 / f_pi; -double const f_halfpi = f_pi / 2.0; -double const f_threehalfpi = 3.0 * f_pi / 2.0; -double const f_four_over_pi = 4.0 / f_pi; -double const f_qtrpi = f_pi / 4.0; -double const f_sixthpi = f_pi / 6.0; -double const f_tansixthpi = tan(f_sixthpi); -double const f_twelfthpi = f_pi / 12.0; -double const f_tantwelfthpi = tan(f_twelfthpi); -float const f_180pi = 180 / f_pi; -# 194 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" -float cos_52s(float x) -{ - const float c1 = 0.9999932946; - const float c2 = -0.4999124376; - const float c3 = 0.0414877472; - const float c4 = -0.0012712095; - - float x2 = x * x; - return (c1 + x2 * (c2 + x2 * (c3 + c4 * x2))); -} - - - - - - -float cos_52(float x) -{ - x = fmodf(x, f_twopi); - if (x < 0) { x = -x; } - int quad = int(x * (float)f_two_over_pi); - switch (quad) { - case 0: return cos_52s(x); - case 1: return -cos_52s((float)f_pi - x); - case 2: return -cos_52s(x-(float)f_pi); - case 3: return cos_52s((float)f_twopi - x); - } -} - - - - -float sin_52(float x) -{ - return cos_52((float)f_halfpi - x); -} -# 247 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" -float tan_56s(float x) -{ - const float c1 = -3.16783027; - const float c2 = 0.134516124; - const float c3 = -4.033321984; - - float x2 = x * x; - return (x * (c1 + c2 * x2) / (c3 + x2)); -} -# 267 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" -float tan_56(float x) -{ - x = fmodf(x, (float)f_twopi); - int octant = int(x * (float)f_four_over_pi); - switch (octant){ - case 0: return tan_56s(x * (float)f_four_over_pi); - case 1: return 1.0f / tan_56s(((float)f_halfpi - x) * (float)f_four_over_pi); - case 2: return -1.0f / tan_56s((x-(float)f_halfpi) * (float)f_four_over_pi); - case 3: return - tan_56s(((float)f_pi - x) * (float)f_four_over_pi); - case 4: return tan_56s((x-(float)f_pi) * (float)f_four_over_pi); - case 5: return 1.0f / tan_56s(((float)f_threehalfpi - x) * (float)f_four_over_pi); - case 6: return -1.0f / tan_56s((x-(float)f_threehalfpi) * (float)f_four_over_pi); - case 7: return - tan_56s(((float)f_twopi - x) * (float)f_four_over_pi); - } -} -# 296 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" -float atan_66s(float x) -{ - const float c1 = 1.6867629106; - const float c2 = 0.4378497304; - const float c3 = 1.6867633134; - - float x2 = x * x; - return (x * (c1 + x2 * c2) / (c3 + x2)); -} - - - - - -float atan_66(float x) -{ - float y; - bool complement= false; - bool region= false; - bool sign= false; - - if (x < 0) { - x = -x; - sign = true; - } - if (x > 1.0) { - x = 1.0 / x; - complement = true; - } - if (x > (float)f_tantwelfthpi) { - x = (x - (float)f_tansixthpi) / (1 + (float)f_tansixthpi * x); - region = true; - } - - y = atan_66s(x); - if (region) { y += (float)f_sixthpi; } - if (complement) { y = (float)f_halfpi-y; } - if (sign) { y = -y; } - return (y); -} - -float asinf1(float x) -{ - float d = 1.0f - x * x; - if (d < 0.0f) { return NAN; } - return 2 * atan_66(x / (1 + sqrt1(d))); -} - -float acosf1(float x) -{ - float d = 1.0f - x * x; - if (d < 0.0f) { return NAN; } - float y = asinf1(sqrt1(d)); - if (x >= 0.0f) { - return y; - } else { - return (float)f_pi - y; - } -} - - -float sqrt1(const float x) -{ - union { - int i; - float x; - } u; - u.x = x; - u.i = (1 << 29) + (u.i >> 1) - (1 << 22); - - - - - u.x = u.x + x / u.x; - u.x = 0.25f * u.x + x / u.x; - - return u.x; -} -# 387 "C:/shared/sonoff/Git/Tasmota/tasmota/support_float.ino" -uint16_t changeUIntScale(uint16_t inum, uint16_t ifrom_min, uint16_t ifrom_max, - uint16_t ito_min, uint16_t ito_max) { - - if (ifrom_min >= ifrom_max) { - if (ito_min > ito_max) { - return ito_max; - } else { - return ito_min; - } - } - - uint32_t num = inum; - uint32_t from_min = ifrom_min; - uint32_t from_max = ifrom_max; - uint32_t to_min = ito_min; - uint32_t to_max = ito_max; - - - num = (num > from_max ? from_max : (num < from_min ? from_min : num)); - - - if (to_min > to_max) { - - num = (from_max - num) + from_min; - to_min = ito_max; - to_max = ito_min; - } - - uint32_t numerator = (num - from_min) * (to_max - to_min); - uint32_t result; - if (numerator >= 0x80000000L) { - - result = numerator / (from_max - from_min) + to_min; - } else { - result = (((numerator * 2) / (from_max - from_min)) + 1) / 2 + to_min; - } - return (uint32_t) (result > to_max ? to_max : (result < to_min ? to_min : result)); -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_legacy_cores.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_legacy_cores.ino" -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - - - - - -void* memchr(const void* ptr, int value, size_t num) -{ - unsigned char *p = (unsigned char*)ptr; - while (num--) { - if (*p != (unsigned char)value) { - p++; - } else { - return p; - } - } - return 0; -} - - - -size_t strcspn(const char *str1, const char *str2) -{ - size_t ret = 0; - while (*str1) { - if (strchr(str2, *str1)) { - return ret; - } else { - str1++; - ret++; - } - } - return ret; -} - - - -char* strpbrk(const char *s1, const char *s2) -{ - while(*s1) { - if (strchr(s2, *s1++)) { - return (char*)--s1; - } - } - return 0; -} - - - -#ifndef __LONG_LONG_MAX__ -#define __LONG_LONG_MAX__ 9223372036854775807LL -#endif -#ifndef ULLONG_MAX -#define ULLONG_MAX (__LONG_LONG_MAX__ * 2ULL + 1) -#endif - -unsigned long long strtoull(const char *__restrict nptr, char **__restrict endptr, int base) -{ - const char *s = nptr; - char c; - do { c = *s++; } while (isspace((unsigned char)c)); - - int neg = 0; - if (c == '-') { - neg = 1; - c = *s++; - } else { - if (c == '+') { - c = *s++; - } - } - - if ((base == 0 || base == 16) && (c == '0') && (*s == 'x' || *s == 'X')) { - c = s[1]; - s += 2; - base = 16; - } - if (base == 0) { base = (c == '0') ? 8 : 10; } - - unsigned long long acc = 0; - int any = 0; - if (base > 1 && base < 37) { - unsigned long long cutoff = ULLONG_MAX / base; - int cutlim = ULLONG_MAX % base; - for ( ; ; c = *s++) { - if (c >= '0' && c <= '9') - c -= '0'; - else if (c >= 'A' && c <= 'Z') - c -= 'A' - 10; - else if (c >= 'a' && c <= 'z') - c -= 'a' - 10; - else - break; - - if (c >= base) - break; - - if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) - any = -1; - else { - any = 1; - acc *= base; - acc += c; - } - } - if (any < 0) { - acc = ULLONG_MAX; - } - else if (any && neg) { - acc = -acc; - } - } - - if (endptr != nullptr) { *endptr = (char *)(any ? s - 1 : nptr); } - - return acc; -} - -#endif - - - -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) || defined(ARDUINO_ESP8266_RELEASE_2_5_0) || defined(ARDUINO_ESP8266_RELEASE_2_5_1) || defined(ARDUINO_ESP8266_RELEASE_2_5_2) - - - - - -void* memmove_P(void *dest, const void *src, size_t n) -{ - if (src > (void*)0x40000000) { - return memcpy_P(dest, src, n); - } else { - return memmove(dest, src, n); - } -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_rotary.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_rotary.ino" -#ifdef USE_LIGHT - -#ifdef ROTARY_V1 - - - - -struct ROTARY { - unsigned long debounce = 0; - uint8_t present = 0; - uint8_t state = 0; - uint8_t position = 128; - uint8_t last_position = 128; - uint8_t interrupts_in_use_count = 0; - uint8_t changed = 0; -} Rotary; - - - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 -void update_rotary(void) ICACHE_RAM_ATTR; -#endif - -void update_rotary(void) -{ - if (MI_DESK_LAMP == my_module_type) { - if (LightPowerIRAM()) { - - - - - uint8_t s = Rotary.state & 3; - if (digitalRead(pin[GPIO_ROT1A])) { s |= 4; } - if (digitalRead(pin[GPIO_ROT1B])) { s |= 8; } - switch (s) { - case 0: case 5: case 10: case 15: - break; - case 1: case 7: case 8: case 14: - Rotary.position++; break; - case 2: case 4: case 11: case 13: - Rotary.position--; break; - case 3: case 12: - Rotary.position = Rotary.position + 2; break; - default: - Rotary.position = Rotary.position - 2; break; - } - Rotary.state = (s >> 2); - } - } -} - -bool RotaryButtonPressed(void) -{ - if ((MI_DESK_LAMP == my_module_type) && (Rotary.changed) && LightPower()) { - Rotary.changed = 0; - return true; - } - return false; -} - -void RotaryInit(void) -{ - Rotary.present = 0; - if ((pin[GPIO_ROT1A] < 99) && (pin[GPIO_ROT1B] < 99)) { - Rotary.present++; - pinMode(pin[GPIO_ROT1A], INPUT_PULLUP); - pinMode(pin[GPIO_ROT1B], INPUT_PULLUP); - - - - - if ((pin[GPIO_ROT1A] < 6) || (pin[GPIO_ROT1A] > 11)) { - attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1A]), update_rotary, CHANGE); - Rotary.interrupts_in_use_count++; - } - if ((pin[GPIO_ROT1B] < 6) || (pin[GPIO_ROT1B] > 11)) { - attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1B]), update_rotary, CHANGE); - Rotary.interrupts_in_use_count++; - } - } -} - - - - - -void RotaryHandler(void) -{ - if (Rotary.interrupts_in_use_count < 2) { - noInterrupts(); - update_rotary(); - } else { - noInterrupts(); - } - if (Rotary.last_position != Rotary.position) { - if (MI_DESK_LAMP == my_module_type) { - if (Button.hold_timer[0]) { - Rotary.changed = 1; - - int16_t t = LightGetColorTemp(); - t = t + (Rotary.position - Rotary.last_position); - if (t < 153) { - t = 153; - } - if (t > 500) { - t = 500; - } - DEBUG_CORE_LOG(PSTR("ROT: " D_CMND_COLORTEMPERATURE " %d"), Rotary.position - Rotary.last_position); - LightSetColorTemp((uint16_t)t); - } else { - int8_t d = Settings.light_dimmer; - d = d + (Rotary.position - Rotary.last_position); - if (d < 1) { - d = 1; - } - if (d > 100) { - d = 100; - } - DEBUG_CORE_LOG(PSTR("ROT: " D_CMND_DIMMER " %d"), Rotary.position - Rotary.last_position); - - LightSetDimmer((uint8_t)d); - Settings.light_dimmer = d; - } - } - Rotary.last_position = 128; - Rotary.position = 128; - } - interrupts(); -} - -void RotaryLoop(void) -{ - if (Rotary.present) { - if (TimeReached(Rotary.debounce)) { - SetNextTimeInterval(Rotary.debounce, Settings.button_debounce); - RotaryHandler(); - } - } -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_rtc.ino" -# 25 "C:/shared/sonoff/Git/Tasmota/tasmota/support_rtc.ino" -const uint32_t SECS_PER_MIN = 60UL; -const uint32_t SECS_PER_HOUR = 3600UL; -const uint32_t SECS_PER_DAY = SECS_PER_HOUR * 24UL; -const uint32_t MINS_PER_HOUR = 60UL; - -#define LEAP_YEAR(Y) (((1970+Y)>0) && !((1970+Y)%4) && (((1970+Y)%100) || !((1970+Y)%400))) - -extern "C" { -#include "sntp.h" -} -#include - -Ticker TickerRtc; - -static const uint8_t kDaysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; -static const char kMonthNamesEnglish[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; - -struct RTC { - uint32_t utc_time = 0; - uint32_t local_time = 0; - uint32_t daylight_saving_time = 0; - uint32_t standard_time = 0; - uint32_t ntp_time = 0; - uint32_t midnight = 0; - uint32_t restart_time = 0; - int32_t drift_time = 0; - int32_t time_timezone = 0; - uint8_t ntp_sync_minute = 0; - bool midnight_now = false; - bool user_time_entry = false; -} Rtc; - -uint32_t UtcTime(void) -{ - return Rtc.utc_time; -} - -uint32_t LocalTime(void) -{ - return Rtc.local_time; -} - -int32_t DriftTime(void) -{ - return Rtc.drift_time; -} - -uint32_t Midnight(void) -{ - return Rtc.midnight; -} - -bool MidnightNow(void) -{ - if (Rtc.midnight_now) { - Rtc.midnight_now = false; - return true; - } - return false; -} - -bool IsDst(void) -{ - if (Rtc.time_timezone == Settings.toffset[1]) { - return true; - } - return false; -} - -String GetBuildDateAndTime(void) -{ - - char bdt[21]; - char *p; - char mdate[] = __DATE__; - char *smonth = mdate; - int day = 0; - int year = 0; - - - uint8_t i = 0; - for (char *str = strtok_r(mdate, " ", &p); str && i < 3; str = strtok_r(nullptr, " ", &p)) { - switch (i++) { - case 0: - smonth = str; - break; - case 1: - day = atoi(str); - break; - case 2: - year = atoi(str); - } - } - int month = (strstr(kMonthNamesEnglish, smonth) -kMonthNamesEnglish) /3 +1; - snprintf_P(bdt, sizeof(bdt), PSTR("%d" D_YEAR_MONTH_SEPARATOR "%02d" D_MONTH_DAY_SEPARATOR "%02d" D_DATE_TIME_SEPARATOR "%s"), year, month, day, __TIME__); - return String(bdt); -} - -String GetMinuteTime(uint32_t minutes) -{ - char tm[6]; - snprintf_P(tm, sizeof(tm), PSTR("%02d:%02d"), minutes / 60, minutes % 60); - - return String(tm); -} - -String GetTimeZone(void) -{ - char tz[7]; - snprintf_P(tz, sizeof(tz), PSTR("%+03d:%02d"), Rtc.time_timezone / 60, abs(Rtc.time_timezone % 60)); - - return String(tz); -} - -String GetDuration(uint32_t time) -{ - char dt[16]; - - TIME_T ut; - BreakTime(time, ut); - - - - - - - snprintf_P(dt, sizeof(dt), PSTR("%dT%02d:%02d:%02d"), ut.days, ut.hour, ut.minute, ut.second); - - return String(dt); -} - -String GetDT(uint32_t time) -{ - - - char dt[20]; - TIME_T tmpTime; - - BreakTime(time, tmpTime); - snprintf_P(dt, sizeof(dt), PSTR("%04d-%02d-%02dT%02d:%02d:%02d"), - tmpTime.year +1970, tmpTime.month, tmpTime.day_of_month, tmpTime.hour, tmpTime.minute, tmpTime.second); - - return String(dt); -} -# 181 "C:/shared/sonoff/Git/Tasmota/tasmota/support_rtc.ino" -String GetDateAndTime(uint8_t time_type) -{ - - uint32_t time = Rtc.local_time; - - switch (time_type) { - case DT_BOOTCOUNT: - time = Settings.bootcount_reset_time; - break; - case DT_ENERGY: - time = Settings.energy_kWhtotal_time; - break; - case DT_UTC: - time = Rtc.utc_time; - break; - case DT_RESTART: - if (Rtc.restart_time == 0) { - return ""; - } - time = Rtc.restart_time; - break; - } - String dt = GetDT(time); - if (Settings.flag3.time_append_timezone && (DT_LOCAL == time_type)) { - dt += GetTimeZone(); - } - return dt; -} - -String GetTime(int type) -{ - - - - - char stime[25]; - - uint32_t time = Rtc.utc_time; - if (1 == type) time = Rtc.local_time; - if (2 == type) time = Rtc.daylight_saving_time; - if (3 == type) time = Rtc.standard_time; - snprintf_P(stime, sizeof(stime), sntp_get_real_time(time)); - - return String(stime); -} - -uint32_t UpTime(void) -{ - if (Rtc.restart_time) { - return Rtc.utc_time - Rtc.restart_time; - } else { - return uptime; - } -} - -uint32_t MinutesUptime(void) -{ - return (UpTime() / 60); -} - -String GetUptime(void) -{ - return GetDuration(UpTime()); -} - -uint32_t MinutesPastMidnight(void) -{ - uint32_t minutes = 0; - - if (RtcTime.valid) { - minutes = (RtcTime.hour *60) + RtcTime.minute; - } - return minutes; -} - -void BreakTime(uint32_t time_input, TIME_T &tm) -{ - - - - - uint8_t year; - uint8_t month; - uint8_t month_length; - uint32_t time; - unsigned long days; - - time = time_input; - tm.second = time % 60; - time /= 60; - tm.minute = time % 60; - time /= 60; - tm.hour = time % 24; - time /= 24; - tm.days = time; - tm.day_of_week = ((time + 4) % 7) + 1; - - year = 0; - days = 0; - while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { - year++; - } - tm.year = year; - - days -= LEAP_YEAR(year) ? 366 : 365; - time -= days; - tm.day_of_year = time; - - for (month = 0; month < 12; month++) { - if (1 == month) { - if (LEAP_YEAR(year)) { - month_length = 29; - } else { - month_length = 28; - } - } else { - month_length = kDaysInMonth[month]; - } - - if (time >= month_length) { - time -= month_length; - } else { - break; - } - } - strlcpy(tm.name_of_month, kMonthNames + (month *3), 4); - tm.month = month + 1; - tm.day_of_month = time + 1; - tm.valid = (time_input > START_VALID_TIME); -} - -uint32_t MakeTime(TIME_T &tm) -{ - - - - int i; - uint32_t seconds; - - - seconds = tm.year * (SECS_PER_DAY * 365); - for (i = 0; i < tm.year; i++) { - if (LEAP_YEAR(i)) { - seconds += SECS_PER_DAY; - } - } - - - for (i = 1; i < tm.month; i++) { - if ((2 == i) && LEAP_YEAR(tm.year)) { - seconds += SECS_PER_DAY * 29; - } else { - seconds += SECS_PER_DAY * kDaysInMonth[i-1]; - } - } - seconds+= (tm.day_of_month - 1) * SECS_PER_DAY; - seconds+= tm.hour * SECS_PER_HOUR; - seconds+= tm.minute * SECS_PER_MIN; - seconds+= tm.second; - return seconds; -} - -uint32_t RuleToTime(TimeRule r, int yr) -{ - TIME_T tm; - uint32_t t; - uint8_t m; - uint8_t w; - - m = r.month; - w = r.week; - if (0 == w) { - if (++m > 12) { - m = 1; - yr++; - } - w = 1; - } - - tm.hour = r.hour; - tm.minute = 0; - tm.second = 0; - tm.day_of_month = 1; - tm.month = m; - tm.year = yr - 1970; - t = MakeTime(tm); - BreakTime(t, tm); - t += (7 * (w - 1) + (r.dow - tm.day_of_week + 7) % 7) * SECS_PER_DAY; - if (0 == r.week) { - t -= 7 * SECS_PER_DAY; - } - return t; -} - -void RtcSecond(void) -{ - TIME_T tmpTime; - - if (!Rtc.user_time_entry && !global_state.wifi_down) { - uint8_t uptime_minute = (uptime / 60) % 60; - if ((Rtc.ntp_sync_minute > 59) && (uptime_minute > 2)) { - Rtc.ntp_sync_minute = 1; - } - uint8_t offset = (uptime < 30) ? RtcTime.second : (((ESP.getChipId() & 0xF) * 3) + 3) ; - if ( (((offset == RtcTime.second) && ( (RtcTime.year < 2016) || - (Rtc.ntp_sync_minute == uptime_minute))) || - ntp_force_sync ) ) { - Rtc.ntp_time = sntp_get_current_timestamp(); - if (Rtc.ntp_time > START_VALID_TIME) { - ntp_force_sync = false; - if (Rtc.utc_time > START_VALID_TIME) { Rtc.drift_time = Rtc.ntp_time - Rtc.utc_time; } - Rtc.utc_time = Rtc.ntp_time; - Rtc.ntp_sync_minute = 60; - if (Rtc.restart_time == 0) { - Rtc.restart_time = Rtc.utc_time - uptime; - } - BreakTime(Rtc.utc_time, tmpTime); - RtcTime.year = tmpTime.year + 1970; - Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); - Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); - - - PrepLog_P2(LOG_LEVEL_DEBUG, PSTR("NTP: Drift %d, (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), - DriftTime(), GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); - - if (Rtc.local_time < START_VALID_TIME) { - rules_flag.time_init = 1; - } else { - rules_flag.time_set = 1; - } - } else { - Rtc.ntp_sync_minute++; - } - } - } - - Rtc.utc_time++; - Rtc.local_time = Rtc.utc_time; - if (Rtc.local_time > START_VALID_TIME) { - int16_t timezone_minutes = Settings.timezone_minutes; - if (Settings.timezone < 0) { timezone_minutes *= -1; } - Rtc.time_timezone = (Settings.timezone * SECS_PER_HOUR) + (timezone_minutes * SECS_PER_MIN); - if (99 == Settings.timezone) { - int32_t dstoffset = Settings.toffset[1] * SECS_PER_MIN; - int32_t stdoffset = Settings.toffset[0] * SECS_PER_MIN; - if (Settings.tflag[1].hemis) { - - if ((Rtc.utc_time >= (Rtc.standard_time - dstoffset)) && (Rtc.utc_time < (Rtc.daylight_saving_time - stdoffset))) { - Rtc.time_timezone = stdoffset; - } else { - Rtc.time_timezone = dstoffset; - } - } else { - - if ((Rtc.utc_time >= (Rtc.daylight_saving_time - stdoffset)) && (Rtc.utc_time < (Rtc.standard_time - dstoffset))) { - Rtc.time_timezone = dstoffset; - } else { - Rtc.time_timezone = stdoffset; - } - } - } - Rtc.local_time += Rtc.time_timezone; - Rtc.time_timezone /= 60; - if (!Settings.energy_kWhtotal_time) { - Settings.energy_kWhtotal_time = Rtc.local_time; - } - if (Settings.bootcount_reset_time < START_VALID_TIME) { - Settings.bootcount_reset_time = Rtc.local_time; - } - } - - BreakTime(Rtc.local_time, RtcTime); - if (RtcTime.valid) { - if (!Rtc.midnight) { - Rtc.midnight = Rtc.local_time - (RtcTime.hour * 3600) - (RtcTime.minute * 60) - RtcTime.second; - } - if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { - Rtc.midnight = Rtc.local_time; - Rtc.midnight_now = true; - } - } - - RtcTime.year += 1970; -} - -void RtcSetTime(uint32_t epoch) -{ - if (epoch < START_VALID_TIME) { - Rtc.user_time_entry = false; - ntp_force_sync = true; - sntp_init(); - } else { - sntp_stop(); - Rtc.user_time_entry = true; - Rtc.utc_time = epoch -1; - } - RtcSecond(); -} - -void RtcInit(void) -{ - sntp_setservername(0, SettingsText(SET_NTPSERVER1)); - sntp_setservername(1, SettingsText(SET_NTPSERVER2)); - sntp_setservername(2, SettingsText(SET_NTPSERVER3)); - sntp_stop(); - sntp_set_timezone(0); - sntp_init(); - Rtc.utc_time = 0; - BreakTime(Rtc.utc_time, RtcTime); - TickerRtc.attach(1, RtcSecond); -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_static_buffer.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_static_buffer.ino" -typedef struct SBuffer_impl { - uint16_t size; - uint16_t len; - uint8_t buf[]; -} SBuffer_impl; - - - -typedef class SBuffer { - -protected: - SBuffer(void) { - - } - -public: - SBuffer(const size_t size) { - _buf = (SBuffer_impl*) new char[size+4]; - _buf->size = size; - _buf->len = 0; - - } - - inline size_t getSize(void) const { return _buf->size; } - inline size_t size(void) const { return _buf->size; } - inline size_t getLen(void) const { return _buf->len; } - inline size_t len(void) const { return _buf->len; } - inline uint8_t *getBuffer(void) const { return _buf->buf; } - inline uint8_t *buf(size_t i = 0) const { return &_buf->buf[i]; } - inline char *charptr(size_t i = 0) const { return (char*) &_buf->buf[i]; } - - virtual ~SBuffer(void) { - delete[] _buf; - } - - inline void setLen(const size_t len) { - uint16_t old_len = _buf->len; - _buf->len = (len <= _buf->size) ? len : _buf->size; - if (old_len < _buf->len) { - memset((void*) &_buf->buf[old_len], 0, _buf->len - old_len); - } - } - - void set8(const size_t offset, const uint8_t data) { - if (offset < _buf->len) { - _buf->buf[offset] = data; - } - } - - size_t add8(const uint8_t data) { - if (_buf->len < _buf->size) { - _buf->buf[_buf->len++] = data; - } - return _buf->len; - } - size_t add16(const uint16_t data) { - if (_buf->len < _buf->size - 1) { - _buf->buf[_buf->len++] = data; - _buf->buf[_buf->len++] = data >> 8; - } - return _buf->len; - } - size_t add32(const uint32_t data) { - if (_buf->len < _buf->size - 3) { - _buf->buf[_buf->len++] = data; - _buf->buf[_buf->len++] = data >> 8; - _buf->buf[_buf->len++] = data >> 16; - _buf->buf[_buf->len++] = data >> 24; - } - return _buf->len; - } - size_t add64(const uint64_t data) { - if (_buf->len < _buf->size - 7) { - _buf->buf[_buf->len++] = data; - _buf->buf[_buf->len++] = data >> 8; - _buf->buf[_buf->len++] = data >> 16; - _buf->buf[_buf->len++] = data >> 24; - _buf->buf[_buf->len++] = data >> 32; - _buf->buf[_buf->len++] = data >> 40; - _buf->buf[_buf->len++] = data >> 48; - _buf->buf[_buf->len++] = data >> 56; - } - return _buf->len; - } - - size_t addBuffer(const SBuffer &buf2) { - if (len() + buf2.len() <= size()) { - for (uint32_t i = 0; i < buf2.len(); i++) { - _buf->buf[_buf->len++] = buf2.buf()[i]; - } - } - return _buf->len; - } - - size_t addBuffer(const uint8_t *buf2, size_t len2) { - if (len() + len2 <= size()) { - for (uint32_t i = 0; i < len2; i++) { - _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); - } - } - return _buf->len; - } - - size_t addBuffer(const char *buf2, size_t len2) { - if (len() + len2 <= size()) { - for (uint32_t i = 0; i < len2; i++) { - _buf->buf[_buf->len++] = pgm_read_byte(&buf2[i]); - } - } - return _buf->len; - } - - uint8_t get8(size_t offset) const { - if (offset < _buf->len) { - return _buf->buf[offset]; - } else { - return 0; - } - } - uint8_t read8(const size_t offset) const { - if (offset < len()) { - return _buf->buf[offset]; - } - return 0; - } - uint16_t get16(const size_t offset) const { - if (offset < len() - 1) { - return _buf->buf[offset] | (_buf->buf[offset+1] << 8); - } - return 0; - } - uint32_t get32(const size_t offset) const { - if (offset < len() - 3) { - return _buf->buf[offset] | (_buf->buf[offset+1] << 8) | - (_buf->buf[offset+2] << 16) | (_buf->buf[offset+3] << 24); - } - return 0; - } - uint64_t get64(const size_t offset) const { - if (offset < len() - 7) { - return (uint64_t)_buf->buf[offset] | ((uint64_t)_buf->buf[offset+1] << 8) | - ((uint64_t)_buf->buf[offset+2] << 16) | ((uint64_t)_buf->buf[offset+3] << 24) | - ((uint64_t)_buf->buf[offset+4] << 32) | ((uint64_t)_buf->buf[offset+5] << 40) | - ((uint64_t)_buf->buf[offset+6] << 48) | ((uint64_t)_buf->buf[offset+7] << 56); - } - return 0; - } - - - inline size_t strlen(const size_t offset) const { - return strnlen((const char*) &_buf->buf[offset], len() - offset); - } - - size_t strlen_s(const size_t offset) const { - size_t slen = this->strlen(offset); - if (slen == len() - offset) { - return 0; - } else { - return slen; - } - } - - SBuffer subBuffer(const size_t start, size_t len) const { - if (start >= _buf->len) { - len = 0; - } else if (start + len > _buf->len) { - len = _buf->len - start; - } - - SBuffer buf2(len); - memcpy(buf2.buf(), buf()+start, len); - buf2._buf->len = len; - return buf2; - } - - static SBuffer SBufferFromHex(const char *hex, size_t len) { - size_t buf_len = (len + 3) / 2; - SBuffer buf2(buf_len); - uint8_t val; - - for (; len > 1; len -= 2) { - val = asc2byte(*hex++) << 4; - val |= asc2byte(*hex++); - buf2.add8(val); - } - return buf2; - } - -protected: - - static uint8_t asc2byte(char chr) { - uint8_t rVal = 0; - if (isdigit(chr)) { rVal = chr - '0'; } - else if (chr >= 'A' && chr <= 'F') { rVal = chr + 10 - 'A'; } - else if (chr >= 'a' && chr <= 'f') { rVal = chr + 10 - 'a'; } - return rVal; - } - - static void unHex(const char* in, uint8_t *out, size_t len) { - } - -protected: - SBuffer_impl * _buf; - -} SBuffer; - -typedef class PreAllocatedSBuffer : public SBuffer { - -public: - PreAllocatedSBuffer(const size_t size, void * buffer) { - _buf = (SBuffer_impl*) buffer; - _buf->size = size - 4; - _buf->len = 0; - } - - ~PreAllocatedSBuffer(void) { - - _buf = nullptr; - } -} PreAllocatedSBuffer; -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_statistics.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_statistics.ino" -#define USE_STATS_CODE - -#ifdef USE_STATS_CODE - - - - -String GetStatistics(void) -{ - char data[40]; - snprintf_P(data, sizeof(data), PSTR(",\"CR\":\"%d/%d\""), GetSettingsTextLen(), settings_text_size); - return String(data); -} - -#else - -String GetStatistics(void) -{ - return String(""); -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_switch.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_switch.ino" -#define SWITCH_V2 -#ifdef SWITCH_V2 - - - - - - -const uint8_t SWITCH_PROBE_INTERVAL = 10; - -#include - -Ticker TickerSwitch; - -struct SWITCH { - unsigned long debounce = 0; - uint16_t no_pullup_mask = 0; - uint8_t state[MAX_SWITCHES] = { 0 }; - uint8_t last_state[MAX_SWITCHES]; - uint8_t hold_timer[MAX_SWITCHES] = { 0 }; - uint8_t virtual_state[MAX_SWITCHES]; - uint8_t present = 0; -} Switch; - - - -void SwitchPullupFlag(uint16 switch_bit) -{ - bitSet(Switch.no_pullup_mask, switch_bit); -} - -void SwitchSetVirtual(uint32_t index, uint8_t state) -{ - Switch.virtual_state[index] = state; -} - -uint8_t SwitchGetVirtual(uint32_t index) -{ - return Switch.virtual_state[index]; -} - -uint8_t SwitchLastState(uint32_t index) -{ - return Switch.last_state[index]; -} - -bool SwitchState(uint32_t index) -{ - uint32_t switchmode = Settings.switchmode[index]; - return ((FOLLOW_INV == switchmode) || - (PUSHBUTTON_INV == switchmode) || - (PUSHBUTTONHOLD_INV == switchmode) || - (FOLLOWMULTI_INV == switchmode) || - (PUSHHOLDMULTI_INV == switchmode) - ) ^ Switch.last_state[index]; -} - - - -void SwitchProbe(void) -{ - if (uptime < 4) { return; } - - uint8_t state_filter = Settings.switch_debounce / SWITCH_PROBE_INTERVAL; - uint8_t force_high = (Settings.switch_debounce % 50) &1; - uint8_t force_low = (Settings.switch_debounce % 50) &2; - - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - if (pin[GPIO_SWT1 +i] < 99) { - - if (1 == digitalRead(pin[GPIO_SWT1 +i])) { - - if (force_high) { - if (1 == Switch.virtual_state[i]) { - Switch.state[i] = state_filter; - } - } - - if (Switch.state[i] < state_filter) { - Switch.state[i]++; - if (state_filter == Switch.state[i]) { - Switch.virtual_state[i] = 1; - } - } - } else { - - if (force_low) { - if (0 == Switch.virtual_state[i]) { - Switch.state[i] = 0; - } - } - - if (Switch.state[i] > 0) { - Switch.state[i]--; - if (0 == Switch.state[i]) { - Switch.virtual_state[i] = 0; - } - } - } - } - } - TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); -} - -void SwitchInit(void) -{ - Switch.present = 0; - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - Switch.last_state[i] = 1; - if (pin[GPIO_SWT1 +i] < 99) { - Switch.present++; - pinMode(pin[GPIO_SWT1 +i], bitRead(Switch.no_pullup_mask, i) ? INPUT : ((16 == pin[GPIO_SWT1 +i]) ? INPUT_PULLDOWN_16 : INPUT_PULLUP)); - Switch.last_state[i] = digitalRead(pin[GPIO_SWT1 +i]); - } - Switch.virtual_state[i] = Switch.last_state[i]; - } - if (Switch.present) { TickerSwitch.attach_ms(SWITCH_PROBE_INTERVAL, SwitchProbe); } -} - - - - - -void SwitchHandler(uint8_t mode) -{ - if (uptime < 4) { return; } - - uint16_t loops_per_second = 1000 / Settings.switch_debounce; - - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - if ((pin[GPIO_SWT1 +i] < 99) || (mode)) { - uint8_t button = Switch.virtual_state[i]; - uint8_t switchflag = POWER_TOGGLE +1; - - if (Switch.hold_timer[i]) { - Switch.hold_timer[i]--; - if (0 == Switch.hold_timer[i]) { - - switch (Settings.switchmode[i]) { - case TOGGLEMULTI: - switchflag = POWER_TOGGLE; - break; - case FOLLOWMULTI: - switchflag = button &1; - break; - case FOLLOWMULTI_INV: - switchflag = ~button &1; - break; - case PUSHHOLDMULTI: - if (NOT_PRESSED == button){ - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 25; - SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); - } - else - SendKey(KEY_SWITCH, i +1, POWER_CLEAR); - break; - case PUSHHOLDMULTI_INV: - if (PRESSED == button){ - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 25; - SendKey(KEY_SWITCH, i +1, POWER_INCREMENT); - } - else - SendKey(KEY_SWITCH, i +1, POWER_CLEAR); - break; - default: - SendKey(KEY_SWITCH, i +1, POWER_HOLD); - break; - } - } - } - - - - if (button != Switch.last_state[i]) { - switch (Settings.switchmode[i]) { - case TOGGLE: - case PUSHBUTTON_TOGGLE: - switchflag = POWER_TOGGLE; - break; - case FOLLOW: - switchflag = button &1; - break; - case FOLLOW_INV: - switchflag = ~button &1; - break; - case PUSHBUTTON: - if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i])) { - switchflag = POWER_TOGGLE; - } - break; - case PUSHBUTTON_INV: - if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i])) { - switchflag = POWER_TOGGLE; - } - break; - case PUSHBUTTONHOLD: - if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i])) { - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i]) && (Switch.hold_timer[i])) { - Switch.hold_timer[i] = 0; - switchflag = POWER_TOGGLE; - } - break; - case PUSHBUTTONHOLD_INV: - if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i])) { - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i]) && (Switch.hold_timer[i])) { - Switch.hold_timer[i] = 0; - switchflag = POWER_TOGGLE; - } - break; - case TOGGLEMULTI: - case FOLLOWMULTI: - case FOLLOWMULTI_INV: - if (Switch.hold_timer[i]) { - Switch.hold_timer[i] = 0; - SendKey(KEY_SWITCH, i +1, POWER_HOLD); - } else { - Switch.hold_timer[i] = loops_per_second / 2; - } - break; - case PUSHHOLDMULTI: - if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i])) { - if(Switch.hold_timer[i]!=0) - SendKey(KEY_SWITCH, i +1, POWER_INV); - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i])) { - if(Switch.hold_timer[i] > loops_per_second * Settings.param[P_HOLD_TIME] / 25) - switchflag = POWER_TOGGLE; - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - break; - case PUSHHOLDMULTI_INV: - if ((PRESSED == button) && (NOT_PRESSED == Switch.last_state[i])) { - if(Switch.hold_timer[i]!=0) - SendKey(KEY_SWITCH, i +1, POWER_INV); - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - if ((NOT_PRESSED == button) && (PRESSED == Switch.last_state[i])) { - if(Switch.hold_timer[i] > loops_per_second * Settings.param[P_HOLD_TIME] / 25) - switchflag = POWER_TOGGLE; - Switch.hold_timer[i] = loops_per_second * Settings.param[P_HOLD_TIME] / 10; - } - break; - } - Switch.last_state[i] = button; - } - if (switchflag <= POWER_TOGGLE) { - if (!SendKey(KEY_SWITCH, i +1, switchflag)) { - ExecuteCommandPower(i +1, switchflag, SRC_SWITCH); - } - } - } - } -} - -void SwitchLoop(void) -{ - if (Switch.present) { - if (TimeReached(Switch.debounce)) { - SetNextTimeInterval(Switch.debounce, Settings.switch_debounce); - SwitchHandler(0); - } - } -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" -const char kSleepMode[] PROGMEM = "Dynamic|Normal"; -const char kPrefixes[] PROGMEM = D_CMND "|" D_STAT "|" D_TELE; - -char* Format(char* output, const char* input, int size) -{ - char *token; - uint32_t digits = 0; - - if (strstr(input, "%") != nullptr) { - strlcpy(output, input, size); - token = strtok(output, "%"); - if (strstr(input, "%") == input) { - output[0] = '\0'; - } else { - token = strtok(nullptr, ""); - } - if (token != nullptr) { - digits = atoi(token); - if (digits) { - char tmp[size]; - if (strchr(token, 'd')) { - snprintf_P(tmp, size, PSTR("%s%c0%dd"), output, '%', digits); - snprintf_P(output, size, tmp, ESP.getChipId() & 0x1fff); - } else { - snprintf_P(tmp, size, PSTR("%s%c0%dX"), output, '%', digits); - snprintf_P(output, size, tmp, ESP.getChipId()); - } - } else { - if (strchr(token, 'd')) { - snprintf_P(output, size, PSTR("%s%d"), output, ESP.getChipId()); - digits = 8; - } - } - } - } - if (!digits) { - strlcpy(output, input, size); - } - return output; -} - -char* GetOtaUrl(char *otaurl, size_t otaurl_size) -{ - if (strstr(SettingsText(SET_OTAURL), "%04d") != nullptr) { - snprintf(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP.getChipId() & 0x1fff); - } - else if (strstr(SettingsText(SET_OTAURL), "%d") != nullptr) { - snprintf_P(otaurl, otaurl_size, SettingsText(SET_OTAURL), ESP.getChipId()); - } - else { - strlcpy(otaurl, SettingsText(SET_OTAURL), otaurl_size); - } - - return otaurl; -} - -char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopic) -{ -# 88 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" - char romram[CMDSZ]; - String fulltopic; - - snprintf_P(romram, sizeof(romram), subtopic); - if (fallback_topic_flag || (prefix > 3)) { - bool fallback = (prefix < 8); - prefix &= 3; - char stemp[11]; - fulltopic = GetTextIndexed(stemp, sizeof(stemp), prefix, kPrefixes); - fulltopic += F("/"); - if (fallback) { - fulltopic += mqtt_client; - fulltopic += F("_fb"); - } else { - fulltopic += topic; - } - } else { - fulltopic = SettingsText(SET_MQTT_FULLTOPIC); - if ((0 == prefix) && (-1 == fulltopic.indexOf(FPSTR(MQTT_TOKEN_PREFIX)))) { - fulltopic += F("/"); - fulltopic += FPSTR(MQTT_TOKEN_PREFIX); - } - for (uint32_t i = 0; i < MAX_MQTT_PREFIXES; i++) { - if (!strlen(SettingsText(SET_MQTTPREFIX1 + i))) { - char temp[TOPSZ]; - SettingsUpdateText(SET_MQTTPREFIX1 + i, GetTextIndexed(temp, sizeof(temp), i, kPrefixes)); - } - } - fulltopic.replace(FPSTR(MQTT_TOKEN_PREFIX), SettingsText(SET_MQTTPREFIX1 + prefix)); - - fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), topic); - fulltopic.replace(F("%hostname%"), my_hostname); - String token_id = WiFi.macAddress(); - token_id.replace(":", ""); - fulltopic.replace(F("%id%"), token_id); - } - fulltopic.replace(F("#"), ""); - fulltopic.replace(F("//"), "/"); - if (!fulltopic.endsWith("/")) { - fulltopic += "/"; - } - snprintf_P(stopic, TOPSZ, PSTR("%s%s"), fulltopic.c_str(), romram); - return stopic; -} - -char* GetGroupTopic_P(char *stopic, const char* subtopic) -{ - - - return GetTopic_P(stopic, (Settings.flag3.grouptopic_mode) ? CMND +8 : CMND, SettingsText(SET_MQTT_GRP_TOPIC), subtopic); -} - -char* GetFallbackTopic_P(char *stopic, const char* subtopic) -{ - return GetTopic_P(stopic, CMND +4, nullptr, subtopic); -} - -char* GetStateText(uint32_t state) -{ - if (state >= MAX_STATE_TEXT) { - state = 1; - } - return SettingsText(SET_STATE_TXT1 + state); -} - - - -void SetLatchingRelay(power_t lpower, uint32_t state) -{ - - - - - - if (state && !latching_relay_pulse) { - latching_power = lpower; - latching_relay_pulse = 2; - } - - for (uint32_t i = 0; i < devices_present; i++) { - uint32_t port = (i << 1) + ((latching_power >> i) &1); - DigitalWrite(GPIO_REL1 +port, bitRead(rel_inverted, port) ? !state : state); - } -} - -void SetDevicePower(power_t rpower, uint32_t source) -{ - ShowSource(source); - last_source = source; - - if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { - power = (1 << devices_present) -1; - rpower = power; - } - - if (Settings.flag.interlock) { - for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { - power_t mask = 1; - uint32_t count = 0; - for (uint32_t j = 0; j < devices_present; j++) { - if ((Settings.interlock[i] & mask) && (rpower & mask)) { - count++; - } - mask <<= 1; - } - if (count > 1) { - mask = ~Settings.interlock[i]; - power &= mask; - rpower &= mask; - } - } - } - - if (rpower) { - last_power = rpower; - } - - XdrvMailbox.index = rpower; - XdrvCall(FUNC_SET_POWER); - - XdrvMailbox.index = rpower; - XdrvMailbox.payload = source; - if (XdrvCall(FUNC_SET_DEVICE_POWER)) { - - } - else if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { - Serial.write(0xA0); - Serial.write(0x04); - Serial.write(rpower &0xFF); - Serial.write(0xA1); - Serial.write('\n'); - Serial.flush(); - } - else if (EXS_RELAY == my_module_type) { - SetLatchingRelay(rpower, 1); - } - else { - for (uint32_t i = 0; i < devices_present; i++) { - power_t state = rpower &1; - if (i < MAX_RELAYS) { - DigitalWrite(GPIO_REL1 +i, bitRead(rel_inverted, i) ? !state : state); - } - rpower >>= 1; - } - } -} - -void RestorePower(bool publish_power, uint32_t source) -{ - if (power != last_power) { - power = last_power; - SetDevicePower(power, source); - if (publish_power) { - MqttPublishAllPowerState(); - } - } -} - -void SetAllPower(uint32_t state, uint32_t source) -{ -# 256 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" - bool publish_power = true; - if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { - state &= 3; - publish_power = false; - } - if ((state >= POWER_OFF) && (state <= POWER_TOGGLE)) { - power_t all_on = (1 << devices_present) -1; - switch (state) { - case POWER_OFF: - power = 0; - break; - case POWER_ON: - power = all_on; - break; - case POWER_TOGGLE: - power ^= all_on; - } - SetDevicePower(power, source); - } - if (publish_power) { - MqttPublishAllPowerState(); - } -} - -void SetPowerOnState(void) -{ - if (MOTOR == my_module_type) { - Settings.poweronstate = POWER_ALL_ON; - } - if (POWER_ALL_ALWAYS_ON == Settings.poweronstate) { - SetDevicePower(1, SRC_RESTART); - } else { - if ((ResetReason() == REASON_DEFAULT_RST) || (ResetReason() == REASON_EXT_SYS_RST)) { - switch (Settings.poweronstate) { - case POWER_ALL_OFF: - case POWER_ALL_OFF_PULSETIME_ON: - power = 0; - SetDevicePower(power, SRC_RESTART); - break; - case POWER_ALL_ON: - power = (1 << devices_present) -1; - SetDevicePower(power, SRC_RESTART); - break; - case POWER_ALL_SAVED_TOGGLE: - power = (Settings.power & ((1 << devices_present) -1)) ^ POWER_MASK; - if (Settings.flag.save_state) { - SetDevicePower(power, SRC_RESTART); - } - break; - case POWER_ALL_SAVED: - power = Settings.power & ((1 << devices_present) -1); - if (Settings.flag.save_state) { - SetDevicePower(power, SRC_RESTART); - } - break; - } - } else { - power = Settings.power & ((1 << devices_present) -1); - if (Settings.flag.save_state) { - SetDevicePower(power, SRC_RESTART); - } - } - } - - - for (uint32_t i = 0; i < devices_present; i++) { - if (!Settings.flag3.no_power_feedback) { - if ((i < MAX_RELAYS) && (pin[GPIO_REL1 +i] < 99)) { - bitWrite(power, i, digitalRead(pin[GPIO_REL1 +i]) ^ bitRead(rel_inverted, i)); - } - } - if ((i < MAX_PULSETIMERS) && (bitRead(power, i) || (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate))) { - SetPulseTimer(i, Settings.pulse_timer[i]); - } - } - blink_powersave = power; -} - -void SetLedPowerIdx(uint32_t led, uint32_t state) -{ - if ((99 == pin[GPIO_LEDLNK]) && (0 == led)) { - if (pin[GPIO_LED2] < 99) { - led = 1; - } - } - if (pin[GPIO_LED1 + led] < 99) { - uint32_t mask = 1 << led; - if (state) { - state = 1; - led_power |= mask; - } else { - led_power &= (0xFF ^ mask); - } - DigitalWrite(GPIO_LED1 + led, bitRead(led_inverted, led) ? !state : state); - } -#ifdef USE_BUZZER - if (led == 0) { - BuzzerSetStateToLed(state); - } -#endif -} - -void SetLedPower(uint32_t state) -{ - if (99 == pin[GPIO_LEDLNK]) { - SetLedPowerIdx(0, state); - } else { - power_t mask = 1; - for (uint32_t i = 0; i < leds_present; i++) { - bool tstate = (power & mask); - SetLedPowerIdx(i, tstate); - mask <<= 1; - } - } -} - -void SetLedPowerAll(uint32_t state) -{ - for (uint32_t i = 0; i < leds_present; i++) { - SetLedPowerIdx(i, state); - } -} - -void SetLedLink(uint32_t state) -{ - uint32_t led_pin = pin[GPIO_LEDLNK]; - uint32_t led_inv = ledlnk_inverted; - if (99 == led_pin) { - led_pin = pin[GPIO_LED1]; - led_inv = bitRead(led_inverted, 0); - } - if (led_pin < 99) { - if (state) { state = 1; } - digitalWrite(led_pin, (led_inv) ? !state : state); - } -#ifdef USE_BUZZER - BuzzerSetStateToLed(state); -#endif -} - -void SetPulseTimer(uint32_t index, uint32_t time) -{ - pulse_timer[index] = (time > 111) ? millis() + (1000 * (time - 100)) : (time > 0) ? millis() + (100 * time) : 0L; -} - -uint32_t GetPulseTimer(uint32_t index) -{ - long time = TimePassedSince(pulse_timer[index]); - if (time < 0) { - time *= -1; - return (time > 11100) ? (time / 1000) + 100 : (time > 0) ? time / 100 : 0; - } - return 0; -} - - - -bool SendKey(uint32_t key, uint32_t device, uint32_t state) -{ -# 423 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" - char stopic[TOPSZ]; - char scommand[CMDSZ]; - char key_topic[TOPSZ]; - bool result = false; - - char *tmp = (key) ? SettingsText(SET_MQTT_SWITCH_TOPIC) : SettingsText(SET_MQTT_BUTTON_TOPIC); - Format(key_topic, tmp, sizeof(key_topic)); - if (Settings.flag.mqtt_enabled && MqttIsConnected() && (strlen(key_topic) != 0) && strcmp(key_topic, "0")) { - if (!key && (device > devices_present)) { - device = 1; - } - GetTopic_P(stopic, CMND, key_topic, - GetPowerDevice(scommand, device, sizeof(scommand), (key + Settings.flag.device_index_enable))); - if (CLEAR_RETAIN == state) { - mqtt_data[0] = '\0'; - } else { - if ((Settings.flag3.button_switch_force_local || - !strcmp(mqtt_topic, key_topic) || - !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), key_topic)) && - (POWER_TOGGLE == state)) { - state = ~(power >> (device -1)) &1; - } - snprintf_P(mqtt_data, sizeof(mqtt_data), GetStateText(state)); - } -#ifdef USE_DOMOTICZ - if (!(DomoticzSendKey(key, device, state, strlen(mqtt_data)))) { -#endif - MqttPublish(stopic, ((key) ? Settings.flag.mqtt_switch_retain - : Settings.flag.mqtt_button_retain) && - (state != POWER_HOLD || !Settings.flag3.no_hold_retain)); -#ifdef USE_DOMOTICZ - } -#endif - result = !Settings.flag3.button_switch_force_local; - } else { - Response_P(PSTR("{\"%s%d\":{\"State\":%d}}"), (key) ? "Switch" : "Button", device, state); - result = XdrvRulesProcess(); - } - int32_t payload_save = XdrvMailbox.payload; - XdrvMailbox.payload = key << 16 | state << 8 | device; - XdrvCall(FUNC_ANY_KEY); - XdrvMailbox.payload = payload_save; - return result; -} - -void ExecuteCommandPower(uint32_t device, uint32_t state, uint32_t source) -{ -# 483 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { - blink_mask &= 1; - Settings.flag.interlock = 0; - Settings.pulse_timer[1] = 0; - Settings.pulse_timer[2] = 0; - Settings.pulse_timer[3] = 0; - } -#endif - - bool publish_power = true; - if ((state >= POWER_OFF_NO_STATE) && (state <= POWER_TOGGLE_NO_STATE)) { - state &= 3; - publish_power = false; - } - - if ((device < 1) || (device > devices_present)) { - device = 1; - } - active_device = device; - - if (device <= MAX_PULSETIMERS) { - SetPulseTimer(device -1, 0); - } - power_t mask = 1 << (device -1); - if (state <= POWER_TOGGLE) { - if ((blink_mask & mask)) { - blink_mask &= (POWER_MASK ^ mask); - MqttPublishPowerBlinkState(device); - } - - if (Settings.flag.interlock && - !interlock_mutex && - ((POWER_ON == state) || ((POWER_TOGGLE == state) && !(power & mask))) - ) { - interlock_mutex = true; - for (uint32_t i = 0; i < MAX_INTERLOCKS; i++) { - if (Settings.interlock[i] & mask) { - for (uint32_t j = 0; j < devices_present; j++) { - power_t imask = 1 << j; - if ((Settings.interlock[i] & imask) && (power & imask) && (mask != imask)) { - ExecuteCommandPower(j +1, POWER_OFF, SRC_IGNORE); - delay(50); - } - } - break; - } - } - interlock_mutex = false; - } - - switch (state) { - case POWER_OFF: { - power &= (POWER_MASK ^ mask); - break; } - case POWER_ON: - power |= mask; - break; - case POWER_TOGGLE: - power ^= mask; - } - SetDevicePower(power, source); -#ifdef USE_DOMOTICZ - DomoticzUpdatePowerState(device); -#endif -#ifdef USE_KNX - KnxUpdatePowerState(device, power); -#endif - if (publish_power && Settings.flag3.hass_tele_on_power) { - MqttPublishTeleState(); - } - if (device <= MAX_PULSETIMERS) { - SetPulseTimer(device -1, (((POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? ~power : power) & mask) ? Settings.pulse_timer[device -1] : 0); - } - } - else if (POWER_BLINK == state) { - if (!(blink_mask & mask)) { - blink_powersave = (blink_powersave & (POWER_MASK ^ mask)) | (power & mask); - blink_power = (power >> (device -1))&1; - } - blink_timer = millis() + 100; - blink_counter = ((!Settings.blinkcount) ? 64000 : (Settings.blinkcount *2)) +1; - blink_mask |= mask; - MqttPublishPowerBlinkState(device); - return; - } - else if (POWER_BLINK_STOP == state) { - bool flag = (blink_mask & mask); - blink_mask &= (POWER_MASK ^ mask); - MqttPublishPowerBlinkState(device); - if (flag) { - ExecuteCommandPower(device, (blink_powersave >> (device -1))&1, SRC_IGNORE); - } - return; - } - if (publish_power) { - MqttPublishPowerState(device); - } -} - -void StopAllPowerBlink(void) -{ - power_t mask; - - for (uint32_t i = 1; i <= devices_present; i++) { - mask = 1 << (i -1); - if (blink_mask & mask) { - blink_mask &= (POWER_MASK ^ mask); - MqttPublishPowerBlinkState(i); - ExecuteCommandPower(i, (blink_powersave >> (i -1))&1, SRC_IGNORE); - } - } -} - -void MqttShowPWMState(void) -{ - ResponseAppend_P(PSTR("\"" D_CMND_PWM "\":{")); - bool first = true; - for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 + i] < 99) { - ResponseAppend_P(PSTR("%s\"" D_CMND_PWM "%d\":%d"), first ? "" : ",", i+1, Settings.pwm_value[i]); - first = false; - } - } - ResponseJsonEnd(); -} - -void MqttShowState(void) -{ - char stemp1[TOPSZ]; - - ResponseAppendTime(); - ResponseAppend_P(PSTR(",\"" D_JSON_UPTIME "\":\"%s\",\"UptimeSec\":%u"), GetUptime().c_str(), UpTime()); - -#ifdef USE_ADC_VCC - dtostrfd((double)ESP.getVcc()/1000, 3, stemp1); - ResponseAppend_P(PSTR(",\"" D_JSON_VCC "\":%s"), stemp1); -#endif - - ResponseAppend_P(PSTR(",\"" D_JSON_HEAPSIZE "\":%d,\"SleepMode\":\"%s\",\"Sleep\":%u,\"LoadAvg\":%u,\"MqttCount\":%u"), - ESP.getFreeHeap()/1024, GetTextIndexed(stemp1, sizeof(stemp1), Settings.flag3.sleep_normal, kSleepMode), - sleep, loop_load_avg, MqttConnectCount()); - - for (uint32_t i = 1; i <= devices_present; i++) { -#ifdef USE_LIGHT - if ((LightDevice()) && (i >= LightDevice())) { - if (i == LightDevice()) { LightState(1); } - } else { -#endif - ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i, sizeof(stemp1), Settings.flag.device_index_enable), - GetStateText(bitRead(power, i-1))); -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { - ResponseAppend_P(PSTR(",\"" D_CMND_FANSPEED "\":%d"), GetFanspeed()); - break; - } -#endif -#ifdef USE_LIGHT - } -#endif - } - - if (pwm_present) { - ResponseAppend_P(PSTR(",")); - MqttShowPWMState(); - } - - ResponseAppend_P(PSTR(",\"" D_JSON_WIFI "\":{\"" D_JSON_AP "\":%d,\"" D_JSON_SSID "\":\"%s\",\"" D_JSON_BSSID "\":\"%s\",\"" D_JSON_CHANNEL "\":%d,\"" D_JSON_RSSI "\":%d,\"" D_JSON_SIGNAL "\":%d,\"" D_JSON_LINK_COUNT "\":%d,\"" D_JSON_DOWNTIME "\":\"%s\"}}"), - Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), WiFi.BSSIDstr().c_str(), WiFi.channel(), - WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI(), WifiLinkCount(), WifiDowntime().c_str()); -} - -void MqttPublishTeleState(void) -{ - mqtt_data[0] = '\0'; - MqttShowState(); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_STATE), MQTT_TELE_RETAIN); -#if defined(USE_RULES) || defined(USE_SCRIPT) - RulesTeleperiod(); -#endif -} - -bool MqttShowSensor(void) -{ - ResponseAppendTime(); - - int json_data_start = strlen(mqtt_data); - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { -#ifdef USE_TM1638 - if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { -#else - if (pin[GPIO_SWT1 +i] < 99) { -#endif - ResponseAppend_P(PSTR(",\"" D_JSON_SWITCH "%d\":\"%s\""), i +1, GetStateText(SwitchState(i))); - } - } - XsnsCall(FUNC_JSON_APPEND); - XdrvCall(FUNC_JSON_APPEND); - - bool json_data_available = (strlen(mqtt_data) - json_data_start); - if (strstr_P(mqtt_data, PSTR(D_JSON_PRESSURE)) != nullptr) { - ResponseAppend_P(PSTR(",\"" D_JSON_PRESSURE_UNIT "\":\"%s\""), PressureUnit().c_str()); - } - if (strstr_P(mqtt_data, PSTR(D_JSON_TEMPERATURE)) != nullptr) { - ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE_UNIT "\":\"%c\""), TempUnit()); - } - ResponseJsonEnd(); - - if (json_data_available) { XdrvCall(FUNC_SHOW_SENSOR); } - return json_data_available; -} - -void MqttPublishSensor(void) -{ - mqtt_data[0] = '\0'; - if (MqttShowSensor()) { - MqttPublishTeleSensor(); - } -} -# 710 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" -void PerformEverySecond(void) -{ - uptime++; - - if (POWER_CYCLE_TIME == uptime) { - UpdateQuickPowerCycle(false); - } - - if (BOOT_LOOP_TIME == uptime) { - RtcRebootReset(); - -#ifdef USE_DEEPSLEEP - if (!(DeepSleepEnabled() && !Settings.flag3.bootcount_update)) { -#endif - Settings.bootcount++; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_BOOT_COUNT " %d"), Settings.bootcount); -#ifdef USE_DEEPSLEEP - } -#endif - } - - if (mqtt_cmnd_blocked_reset) { - mqtt_cmnd_blocked_reset--; - if (!mqtt_cmnd_blocked_reset) { - mqtt_cmnd_blocked = 0; - } - } - - if (seriallog_timer) { - seriallog_timer--; - if (!seriallog_timer) { - if (seriallog_level) { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SERIAL_LOGGING_DISABLED)); - } - seriallog_level = 0; - } - } - - if (syslog_timer) { - syslog_timer--; - if (!syslog_timer) { - syslog_level = Settings.syslog_level; - if (Settings.syslog_level) { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_LOGGING_REENABLED)); - } - } - } - - ResetGlobalValues(); - - if (Settings.tele_period) { - if (tele_period >= 9999) { - if (!global_state.wifi_down) { - tele_period = 0; - } - } else { - tele_period++; - if (tele_period >= Settings.tele_period) { - tele_period = 0; - - MqttPublishTeleState(); - - mqtt_data[0] = '\0'; - if (MqttShowSensor()) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); -#if defined(USE_RULES) || defined(USE_SCRIPT) - RulesTeleperiod(); -#endif - } - - XdrvCall(FUNC_AFTER_TELEPERIOD); - } - } - } -} - - - - - -void Every100mSeconds(void) -{ - - power_t power_now; - - if (prepped_loglevel) { - AddLog(prepped_loglevel); - prepped_loglevel = 0; - } - - if (latching_relay_pulse) { - latching_relay_pulse--; - if (!latching_relay_pulse) SetLatchingRelay(0, 0); - } - - for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) { - if (pulse_timer[i] != 0L) { - if (TimeReached(pulse_timer[i])) { - pulse_timer[i] = 0L; - ExecuteCommandPower(i +1, (POWER_ALL_OFF_PULSETIME_ON == Settings.poweronstate) ? POWER_ON : POWER_OFF, SRC_PULSETIMER); - } - } - } - - if (blink_mask) { - if (TimeReached(blink_timer)) { - SetNextTimeInterval(blink_timer, 100 * Settings.blinktime); - blink_counter--; - if (!blink_counter) { - StopAllPowerBlink(); - } else { - blink_power ^= 1; - power_now = (power & (POWER_MASK ^ blink_mask)) | ((blink_power) ? blink_mask : 0); - SetDevicePower(power_now, SRC_IGNORE); - } - } - } -} - - - - - -void Every250mSeconds(void) -{ - - - uint32_t blinkinterval = 1; - - state_250mS++; - state_250mS &= 0x3; - - if (!Settings.flag.global_state) { - if (global_state.data) { - if (global_state.mqtt_down) { blinkinterval = 7; } - if (global_state.wifi_down) { blinkinterval = 3; } - blinks = 201; - } - } - if (blinks || restart_flag || ota_state_flag) { - if (restart_flag || ota_state_flag) { - blinkstate = true; - } else { - blinkspeed--; - if (!blinkspeed) { - blinkspeed = blinkinterval; - blinkstate ^= 1; - } - } - if ((!(Settings.ledstate &0x08)) && ((Settings.ledstate &0x06) || (blinks > 200) || (blinkstate))) { - SetLedLink(blinkstate); - } - if (!blinkstate) { - blinks--; - if (200 == blinks) blinks = 0; - } - } - if (Settings.ledstate &1 && (pin[GPIO_LEDLNK] < 99 || !(blinks || restart_flag || ota_state_flag)) ) { - bool tstate = power & Settings.ledmask; - if ((SONOFF_TOUCH == my_module_type) || (SONOFF_T11 == my_module_type) || (SONOFF_T12 == my_module_type) || (SONOFF_T13 == my_module_type)) { - tstate = (!power) ? 1 : 0; - } - SetLedPower(tstate); - } - - - - - - switch (state_250mS) { - case 0: - if (ota_state_flag && BACKLOG_EMPTY) { - ota_state_flag--; - if (2 == ota_state_flag) { - RtcSettings.ota_loader = 0; - ota_retry_counter = OTA_ATTEMPTS; - ESPhttpUpdate.rebootOnUpdate(false); - SettingsSave(1); - } - if (ota_state_flag <= 0) { -#ifdef USE_WEBSERVER - if (Settings.webserver) StopWebserver(); -#endif -#ifdef USE_ARILUX_RF - AriluxRfDisable(); -#endif - ota_state_flag = 92; - ota_result = 0; - ota_retry_counter--; - if (ota_retry_counter) { - strlcpy(mqtt_data, GetOtaUrl(log_data, sizeof(log_data)), sizeof(mqtt_data)); -#ifndef FIRMWARE_MINIMAL - if (RtcSettings.ota_loader) { -# 915 "C:/shared/sonoff/Git/Tasmota/tasmota/support_tasmota.ino" - char *bch = strrchr(mqtt_data, '/'); - if (bch == nullptr) { bch = mqtt_data; } - - char *ech = strrchr(bch, '.'); - if ((ech != nullptr) && (0 == strncasecmp_P(ech, PSTR(".GZ"), 3))) { - char *fch = ech; - *fch = '\0'; - ech = strrchr(bch, '.'); - *fch = '.'; - } - if (ech == nullptr) { ech = mqtt_data + strlen(mqtt_data); } - char ota_url_type[strlen(ech) +1]; - strncpy(ota_url_type, ech, sizeof(ota_url_type)); - - char *pch = strrchr(bch, '-'); - if (pch == nullptr) { pch = ech; } - *pch = '\0'; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s-" D_JSON_MINIMAL "%s"), mqtt_data, ota_url_type); - } -#endif - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPLOAD "%s"), mqtt_data); -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) - ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(mqtt_data)); -#else - - WiFiClient OTAclient; - ota_result = (HTTP_UPDATE_FAILED != ESPhttpUpdate.update(OTAclient, mqtt_data)); -#endif - if (!ota_result) { -#ifndef FIRMWARE_MINIMAL - int ota_error = ESPhttpUpdate.getLastError(); - DEBUG_CORE_LOG(PSTR("OTA: Error %d"), ota_error); - if ((HTTP_UE_TOO_LESS_SPACE == ota_error) || (HTTP_UE_BIN_FOR_WRONG_FLASH == ota_error)) { - RtcSettings.ota_loader = 1; - } -#endif - ota_state_flag = 2; - } - } - } - if (90 == ota_state_flag) { - ota_state_flag = 0; - Response_P(PSTR("{\"" D_CMND_UPGRADE "\":\"")); - if (ota_result) { - - if (!VersionCompatible()) { - ResponseAppend_P(PSTR(D_JSON_FAILED " " D_UPLOAD_ERR_14)); - } else { - ResponseAppend_P(PSTR(D_JSON_SUCCESSFUL ". " D_JSON_RESTARTING)); - restart_flag = 2; - } - } else { - ResponseAppend_P(PSTR(D_JSON_FAILED " %s"), ESPhttpUpdate.getLastErrorString().c_str()); - } - ResponseAppend_P(PSTR("\"}")); - - MqttPublishPrefixTopic_P(STAT, PSTR(D_CMND_UPGRADE)); - } - } - break; - case 1: - if (MidnightNow()) { - XsnsCall(FUNC_SAVE_AT_MIDNIGHT); - } - if (save_data_counter && BACKLOG_EMPTY) { - save_data_counter--; - if (save_data_counter <= 0) { - if (Settings.flag.save_state) { - power_t mask = POWER_MASK; - for (uint32_t i = 0; i < MAX_PULSETIMERS; i++) { - if ((Settings.pulse_timer[i] > 0) && (Settings.pulse_timer[i] < 30)) { - mask &= ~(1 << i); - } - } - if (!((Settings.power &mask) == (power &mask))) { - Settings.power = power; - } - } else { - Settings.power = 0; - } - SettingsSave(0); - save_data_counter = Settings.save_data; - } - } - if (restart_flag && BACKLOG_EMPTY) { - if ((214 == restart_flag) || (215 == restart_flag) || (216 == restart_flag)) { - - char storage_ssid1[strlen(SettingsText(SET_STASSID1)) +1]; - strncpy(storage_ssid1, SettingsText(SET_STASSID1), sizeof(storage_ssid1)); - char storage_ssid2[strlen(SettingsText(SET_STASSID2)) +1]; - strncpy(storage_ssid2, SettingsText(SET_STASSID2), sizeof(storage_ssid2)); - char storage_pass1[strlen(SettingsText(SET_STAPWD1)) +1]; - strncpy(storage_pass1, SettingsText(SET_STAPWD1), sizeof(storage_pass1)); - char storage_pass2[strlen(SettingsText(SET_STAPWD2)) +1]; - strncpy(storage_pass2, SettingsText(SET_STAPWD2), sizeof(storage_pass2)); - - char storage_mqtthost[strlen(SettingsText(SET_MQTT_HOST)) +1]; - strncpy(storage_mqtthost, SettingsText(SET_MQTT_HOST), sizeof(storage_mqtthost)); - char storage_mqttuser[strlen(SettingsText(SET_MQTT_USER)) +1]; - strncpy(storage_mqttuser, SettingsText(SET_MQTT_USER), sizeof(storage_mqttuser)); - char storage_mqttpwd[strlen(SettingsText(SET_MQTT_PWD)) +1]; - strncpy(storage_mqttpwd, SettingsText(SET_MQTT_PWD), sizeof(storage_mqttpwd)); - char storage_mqtttopic[strlen(SettingsText(SET_MQTT_TOPIC)) +1]; - strncpy(storage_mqtttopic, SettingsText(SET_MQTT_TOPIC), sizeof(storage_mqtttopic)); - uint16_t mqtt_port = Settings.mqtt_port; - - - - - if ((215 == restart_flag) || (216 == restart_flag)) { - SettingsErase(0); - } - SettingsDefault(); - - SettingsUpdateText(SET_STASSID1, storage_ssid1); - SettingsUpdateText(SET_STASSID2, storage_ssid2); - SettingsUpdateText(SET_STAPWD1, storage_pass1); - SettingsUpdateText(SET_STAPWD2, storage_pass2); - if (216 == restart_flag) { - - SettingsUpdateText(SET_MQTT_HOST, storage_mqtthost); - SettingsUpdateText(SET_MQTT_USER, storage_mqttuser); - SettingsUpdateText(SET_MQTT_PWD, storage_mqttpwd); - SettingsUpdateText(SET_MQTT_TOPIC, storage_mqtttopic); - Settings.mqtt_port = mqtt_port; - } - restart_flag = 2; - } - else if (213 == restart_flag) { - SettingsSdkErase(); - restart_flag = 2; - } - else if (212 == restart_flag) { - SettingsErase(0); - restart_flag = 211; - } - if (211 == restart_flag) { - SettingsDefault(); - restart_flag = 2; - } - if (2 == restart_flag) { - SettingsSaveAll(); - } - restart_flag--; - if (restart_flag <= 0) { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RESTARTING)); - EspRestart(); - } - } - break; - case 2: - WifiCheck(wifi_state_flag); - wifi_state_flag = WIFI_RESTART; - break; - case 3: - if (!global_state.wifi_down) { MqttCheck(); } - break; - } -} - -#ifdef USE_ARDUINO_OTA - - - - - - - -bool arduino_ota_triggered = false; -uint16_t arduino_ota_progress_dot_count = 0; - -void ArduinoOTAInit(void) -{ - ArduinoOTA.setPort(8266); - ArduinoOTA.setHostname(my_hostname); - if (strlen(SettingsText(SET_WEBPWD))) { - ArduinoOTA.setPassword(SettingsText(SET_WEBPWD)); - } - - ArduinoOTA.onStart([]() - { - SettingsSave(1); -#ifdef USE_WEBSERVER - if (Settings.webserver) { StopWebserver(); } -#endif -#ifdef USE_ARILUX_RF - AriluxRfDisable(); -#endif - if (Settings.flag.mqtt_enabled) { - MqttDisconnect(); - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_UPLOAD_STARTED)); - arduino_ota_triggered = true; - arduino_ota_progress_dot_count = 0; - delay(100); - }); - - 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) - { - - - - - 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); - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA %s. " D_RESTARTING), error_str); - EspRestart(); - }); - - ArduinoOTA.onEnd([]() - { - if ((LOG_LEVEL_DEBUG <= seriallog_level)) { Serial.println(); } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_SUCCESSFUL ". " D_RESTARTING)); - EspRestart(); - }); - - ArduinoOTA.begin(); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD "Arduino OTA " D_ENABLED " " D_PORT " 8266")); -} - -void ArduinoOtaLoop(void) -{ - MDNS.update(); - ArduinoOTA.handle(); - - while (arduino_ota_triggered) { ArduinoOTA.handle(); } -} -#endif - - - -void SerialInput(void) -{ - while (Serial.available()) { - - delay(0); - serial_in_byte = Serial.read(); - - - - - if ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type)) { - serial_in_byte = ButtonSerial(serial_in_byte); - } - - - - if (XdrvCall(FUNC_SERIAL)) { - serial_in_byte_counter = 0; - Serial.flush(); - return; - } - - - - if (serial_in_byte > 127 && !Settings.flag.mqtt_serial_raw) { - serial_in_byte_counter = 0; - Serial.flush(); - return; - } - if (!Settings.flag.mqtt_serial) { - if (isprint(serial_in_byte)) { - if (serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - } else { - serial_in_byte_counter = 0; - } - } - } else { - if (serial_in_byte || Settings.flag.mqtt_serial_raw) { - if ((serial_in_byte_counter < INPUT_BUFFER_SIZE -1) && - ((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || - ((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || - Settings.flag.mqtt_serial_raw)) { - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - serial_polling_window = millis(); - } else { - serial_polling_window = 0; - break; - } - } - } - -#ifdef USE_SONOFF_SC - - - - if (SONOFF_SC == my_module_type) { - if (serial_in_byte == '\x1B') { - serial_in_buffer[serial_in_byte_counter] = 0; - SonoffScSerialInput(serial_in_buffer); - serial_in_byte_counter = 0; - Serial.flush(); - return; - } - } else -#endif - - - if (!Settings.flag.mqtt_serial && (serial_in_byte == '\n')) { - serial_in_buffer[serial_in_byte_counter] = 0; - seriallog_level = (Settings.seriallog_level < LOG_LEVEL_INFO) ? (uint8_t)LOG_LEVEL_INFO : Settings.seriallog_level; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), serial_in_buffer); - ExecuteCommand(serial_in_buffer, SRC_SERIAL); - serial_in_byte_counter = 0; - serial_polling_window = 0; - Serial.flush(); - return; - } - } - - if (Settings.flag.mqtt_serial && serial_in_byte_counter && (millis() > (serial_polling_window + SERIAL_POLLING))) { - serial_in_buffer[serial_in_byte_counter] = 0; - char hex_char[(serial_in_byte_counter * 2) + 2]; - bool assume_json = (!Settings.flag.mqtt_serial_raw && (serial_in_buffer[0] == '{')); - Response_P(PSTR("{\"" D_JSON_SERIALRECEIVED "\":%s%s%s}"), - (assume_json) ? "" : """", - (Settings.flag.mqtt_serial_raw) ? ToHex_P((unsigned char*)serial_in_buffer, serial_in_byte_counter, hex_char, sizeof(hex_char)) : serial_in_buffer, - (assume_json) ? "" : """"); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SERIALRECEIVED)); - XdrvRulesProcess(); - serial_in_byte_counter = 0; - } -} - - - -void GpioInit(void) -{ - uint32_t mpin; - - if (!ValidModule(Settings.module)) { - uint32_t module = MODULE; - if (!ValidModule(MODULE)) { module = SONOFF_BASIC; } - Settings.module = module; - Settings.last_module = module; - } - SetModuleType(); - - if (Settings.module != Settings.last_module) { - Settings.baudrate = APP_BAUDRATE / 300; - Settings.serial_config = TS_SERIAL_8N1; - } - - for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { - if ((Settings.user_template.gp.io[i] >= GPIO_SENSOR_END) && (Settings.user_template.gp.io[i] < GPIO_USER)) { - Settings.user_template.gp.io[i] = GPIO_USER; - } - } - - myio def_gp; - ModuleGpios(&def_gp); - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if ((Settings.my_gp.io[i] >= GPIO_SENSOR_END) && (Settings.my_gp.io[i] < GPIO_USER)) { - Settings.my_gp.io[i] = GPIO_NONE; - } - else if (Settings.my_gp.io[i] > GPIO_NONE) { - my_module.io[i] = Settings.my_gp.io[i]; - } - if ((def_gp.io[i] > GPIO_NONE) && (def_gp.io[i] < GPIO_USER)) { - my_module.io[i] = def_gp.io[i]; - } - } - if ((Settings.my_adc0 >= ADC0_END) && (Settings.my_adc0 < ADC0_USER)) { - Settings.my_adc0 = ADC0_NONE; - } - else if (Settings.my_adc0 > ADC0_NONE) { - my_adc0 = Settings.my_adc0; - } - my_module_flag = ModuleFlag(); - uint32_t template_adc0 = my_module_flag.data &15; - if ((template_adc0 > ADC0_NONE) && (template_adc0 < ADC0_USER)) { - my_adc0 = template_adc0; - } - - for (uint32_t i = 0; i < GPIO_MAX; i++) { - pin[i] = 99; - } - for (uint32_t i = 0; i < sizeof(my_module.io); i++) { - mpin = ValidPin(i, my_module.io[i]); - - DEBUG_CORE_LOG(PSTR("INI: gpio pin %d, mpin %d"), i, mpin); - - if (mpin) { - XdrvMailbox.index = mpin; - XdrvMailbox.payload = i; - - if ((mpin >= GPIO_SWT1_NP) && (mpin < (GPIO_SWT1_NP + MAX_SWITCHES))) { - SwitchPullupFlag(mpin - GPIO_SWT1_NP); - mpin -= (GPIO_SWT1_NP - GPIO_SWT1); - } - else if ((mpin >= GPIO_KEY1_NP) && (mpin < (GPIO_KEY1_NP + MAX_KEYS))) { - ButtonPullupFlag(mpin - GPIO_KEY1_NP); - mpin -= (GPIO_KEY1_NP - GPIO_KEY1); - } - else if ((mpin >= GPIO_KEY1_INV) && (mpin < (GPIO_KEY1_INV + MAX_KEYS))) { - ButtonInvertFlag(mpin - GPIO_KEY1_INV); - mpin -= (GPIO_KEY1_INV - GPIO_KEY1); - } - else if ((mpin >= GPIO_KEY1_INV_NP) && (mpin < (GPIO_KEY1_INV_NP + MAX_KEYS))) { - ButtonPullupFlag(mpin - GPIO_KEY1_INV_NP); - ButtonInvertFlag(mpin - GPIO_KEY1_INV_NP); - mpin -= (GPIO_KEY1_INV_NP - GPIO_KEY1); - } - else if ((mpin >= GPIO_REL1_INV) && (mpin < (GPIO_REL1_INV + MAX_RELAYS))) { - bitSet(rel_inverted, mpin - GPIO_REL1_INV); - mpin -= (GPIO_REL1_INV - GPIO_REL1); - } - else if ((mpin >= GPIO_LED1_INV) && (mpin < (GPIO_LED1_INV + MAX_LEDS))) { - bitSet(led_inverted, mpin - GPIO_LED1_INV); - mpin -= (GPIO_LED1_INV - GPIO_LED1); - } - else if (mpin == GPIO_LEDLNK_INV) { - ledlnk_inverted = 1; - mpin -= (GPIO_LEDLNK_INV - GPIO_LEDLNK); - } - else if ((mpin >= GPIO_PWM1_INV) && (mpin < (GPIO_PWM1_INV + MAX_PWMS))) { - bitSet(pwm_inverted, mpin - GPIO_PWM1_INV); - mpin -= (GPIO_PWM1_INV - GPIO_PWM1); - } - else if (XdrvCall(FUNC_PIN_STATE)) { - mpin = XdrvMailbox.index; - } - else if (XsnsCall(FUNC_PIN_STATE)) { - mpin = XdrvMailbox.index; - }; - } - if (mpin) pin[mpin] = i; - } - - if ((2 == pin[GPIO_TXD]) || (H801 == my_module_type)) { Serial.set_tx(2); } - - analogWriteRange(Settings.pwm_range); - analogWriteFreq(Settings.pwm_frequency); - -#ifdef USE_SPI - spi_flg = ((((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CS] > 14)) || (pin[GPIO_SPI_CS] < 12)) || (((pin[GPIO_SPI_DC] < 99) && (pin[GPIO_SPI_DC] > 14)) || (pin[GPIO_SPI_DC] < 12))); - if (spi_flg) { - for (uint32_t i = 0; i < GPIO_MAX; i++) { - if ((pin[i] >= 12) && (pin[i] <=14)) pin[i] = 99; - } - my_module.io[12] = GPIO_SPI_MISO; - pin[GPIO_SPI_MISO] = 12; - my_module.io[13] = GPIO_SPI_MOSI; - pin[GPIO_SPI_MOSI] = 13; - my_module.io[14] = GPIO_SPI_CLK; - pin[GPIO_SPI_CLK] = 14; - } - soft_spi_flg = ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && ((pin[GPIO_SSPI_MOSI] < 99) || (pin[GPIO_SSPI_MOSI] < 99))); -#endif - -#ifdef USE_I2C - i2c_flg = ((pin[GPIO_I2C_SCL] < 99) && (pin[GPIO_I2C_SDA] < 99)); - if (i2c_flg) { - Wire.begin(pin[GPIO_I2C_SDA], pin[GPIO_I2C_SCL]); - } -#endif - - devices_present = 0; - light_type = LT_BASIC; - if (XdrvCall(FUNC_MODULE_INIT)) { - - } - else if (YTF_IR_BRIDGE == my_module_type) { - ClaimSerial(); - - } - else if (SONOFF_DUAL == my_module_type) { - devices_present = 2; - SetSerial(19200, TS_SERIAL_8N1); - } - else if (CH4 == my_module_type) { - devices_present = 4; - SetSerial(19200, TS_SERIAL_8N1); - } -#ifdef USE_SONOFF_SC - else if (SONOFF_SC == my_module_type) { - SetSerial(19200, TS_SERIAL_8N1); - } -#endif - - for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 +i] < 99) { - pinMode(pin[GPIO_PWM1 +i], OUTPUT); - if (light_type) { - - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range : 0); - } else { - pwm_present = true; - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); - } - } - } - - for (uint32_t i = 0; i < MAX_RELAYS; i++) { - if (pin[GPIO_REL1 +i] < 99) { - pinMode(pin[GPIO_REL1 +i], OUTPUT); - devices_present++; - if (EXS_RELAY == my_module_type) { - digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0); - if (i &1) { devices_present--; } - } - } - } - - for (uint32_t i = 0; i < MAX_LEDS; i++) { - if (pin[GPIO_LED1 +i] < 99) { -#ifdef USE_ARILUX_RF - if ((3 == i) && (leds_present < 2) && (99 == pin[GPIO_ARIRFSEL])) { - pin[GPIO_ARIRFSEL] = pin[GPIO_LED4]; - pin[GPIO_LED4] = 99; - } else { -#endif - pinMode(pin[GPIO_LED1 +i], OUTPUT); - leds_present++; - digitalWrite(pin[GPIO_LED1 +i], bitRead(led_inverted, i)); -#ifdef USE_ARILUX_RF - } -#endif - } - } - if (pin[GPIO_LEDLNK] < 99) { - pinMode(pin[GPIO_LEDLNK], OUTPUT); - digitalWrite(pin[GPIO_LEDLNK], ledlnk_inverted); - } - - ButtonInit(); - SwitchInit(); -#ifdef ROTARY_V1 - RotaryInit(); -#endif - - SetLedPower(Settings.ledstate &8); - SetLedLink(Settings.ledstate &8); - - XdrvCall(FUNC_PRE_INIT); -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_udp.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/support_udp.ino" -#ifdef USE_EMULATION - -#define UDP_BUFFER_SIZE 200 -#define UDP_MSEARCH_SEND_DELAY 1500 - -#include -Ticker TickerMSearch; - -IPAddress udp_remote_ip; -uint16_t udp_remote_port; - -bool udp_connected = false; -bool udp_response_mutex = false; - - - - - -const char URN_BELKIN_DEVICE[] PROGMEM = "urn:belkin:device:**"; -const char URN_BELKIN_DEVICE_CAP[] PROGMEM = "urn:Belkin:device:**"; -const char UPNP_ROOTDEVICE[] PROGMEM = "upnp:rootdevice"; -const char SSDPSEARCH_ALL[] PROGMEM = "ssdpsearch:all"; -const char SSDP_ALL[] PROGMEM = "ssdp:all"; - - - - - -bool UdpDisconnect(void) -{ - if (udp_connected) { - PortUdp.flush(); - WiFiUDP::stopAll(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_DISABLED)); - udp_connected = false; - } - return udp_connected; -} - -bool UdpConnect(void) -{ - if (!udp_connected) { - - if (PortUdp.beginMulticast(WiFi.localIP(), IPAddress(239,255,255,250), 1900)) { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED)); - udp_response_mutex = false; - udp_connected = true; - } else { - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_JOIN_FAILED)); - udp_connected = false; - } - } - return udp_connected; -} - -void PollUdp(void) -{ - if (udp_connected) { - while (PortUdp.parsePacket()) { - char packet_buffer[UDP_BUFFER_SIZE]; - - int len = PortUdp.read(packet_buffer, UDP_BUFFER_SIZE -1); - packet_buffer[len] = 0; - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("UDP: Packet (%d)"), len); - - - -#ifdef USE_SCRIPT_HUE - if (!udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) { -#else - if (devices_present && !udp_response_mutex && (strstr_P(packet_buffer, PSTR("M-SEARCH")) != nullptr)) { -#endif - udp_response_mutex = true; - - udp_remote_ip = PortUdp.remoteIP(); - udp_remote_port = PortUdp.remotePort(); - - - - - uint32_t response_delay = UDP_MSEARCH_SEND_DELAY + ((millis() &0x7) * 100); - - LowerCase(packet_buffer, packet_buffer); - RemoveSpace(packet_buffer); - -#ifdef USE_EMULATION_WEMO - if (EMUL_WEMO == Settings.flag2.emulation) { - if (strstr_P(packet_buffer, URN_BELKIN_DEVICE) != nullptr) { - TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 1); - return; - } - else if ((strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || - (strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) || - (strstr_P(packet_buffer, SSDP_ALL) != nullptr)) { - TickerMSearch.attach_ms(response_delay, WemoRespondToMSearch, 2); - return; - } - } -#endif - -#ifdef USE_EMULATION_HUE - if (EMUL_HUE == Settings.flag2.emulation) { - if ((strstr_P(packet_buffer, PSTR(":device:basic:1")) != nullptr) || - (strstr_P(packet_buffer, UPNP_ROOTDEVICE) != nullptr) || - (strstr_P(packet_buffer, SSDPSEARCH_ALL) != nullptr) || - (strstr_P(packet_buffer, SSDP_ALL) != nullptr)) { - TickerMSearch.attach_ms(response_delay, HueRespondToMSearch); - return; - } - } -#endif - - udp_response_mutex = false; - } - - } - optimistic_yield(100); - } -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/support_wifi.ino" -# 24 "C:/shared/sonoff/Git/Tasmota/tasmota/support_wifi.ino" -#ifndef WIFI_RSSI_THRESHOLD - -#define WIFI_RSSI_THRESHOLD 5 -#endif -#ifndef WIFI_RESCAN_MINUTES - -#define WIFI_RESCAN_MINUTES 5 -#endif - -const uint8_t WIFI_CONFIG_SEC = 180; - -const uint8_t WIFI_CHECK_SEC = 5; -const uint8_t WIFI_RETRY_OFFSET_SEC = 20; - -#include -#if LWIP_IPV6 -#include -#endif - -struct WIFI { - uint32_t last_event = 0; - uint32_t downtime = 0; - uint16_t link_count = 0; - uint8_t counter; - uint8_t retry_init; - uint8_t retry; - uint8_t status; - uint8_t config_type = 0; - uint8_t config_counter = 0; - uint8_t mdns_begun = 0; - uint8_t scan_state; - uint8_t bssid[6] = {0}; - uint8_t bssid_last[6] = {0}; - int8_t best_network_db; -} Wifi; - -int WifiGetRssiAsQuality(int rssi) -{ - int quality = 0; - - if (rssi <= -100) { - quality = 0; - } else if (rssi >= -50) { - quality = 100; - } else { - quality = 2 * (rssi + 100); - } - return quality; -} - -bool WifiConfigCounter(void) -{ - if (Wifi.config_counter) { - Wifi.config_counter = WIFI_CONFIG_SEC; - } - return (Wifi.config_counter); -} - -void WifiConfig(uint8_t type) -{ - if (!Wifi.config_type) { - if ((WIFI_RETRY == type) || (WIFI_WAIT == type)) { return; } -#ifdef USE_EMULATION - UdpDisconnect(); -#endif - WiFi.disconnect(); - Wifi.config_type = type; - -#ifndef USE_WEBSERVER - if (WIFI_MANAGER == Wifi.config_type) { - Wifi.config_type = WIFI_SERIAL; - } -#endif - - Wifi.config_counter = WIFI_CONFIG_SEC; - Wifi.counter = Wifi.config_counter +5; - blinks = 1999; - if (WIFI_RESTART == Wifi.config_type) { - restart_flag = 2; - } - else if (WIFI_SERIAL == Wifi.config_type) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_6_SERIAL " " D_ACTIVE_FOR_3_MINUTES)); - } -#ifdef USE_WEBSERVER - else if (WIFI_MANAGER == Wifi.config_type || WIFI_MANAGER_RESET_ONLY == Wifi.config_type) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_WCFG_2_WIFIMANAGER " " D_ACTIVE_FOR_3_MINUTES)); - WifiManagerBegin(WIFI_MANAGER_RESET_ONLY == Wifi.config_type); - } -#endif - } -} - -void WifiSetMode(WiFiMode_t wifi_mode) -{ - if (WiFi.getMode() == wifi_mode) { return; } - - if (wifi_mode != WIFI_OFF) { - - WiFi.forceSleepWake(); - delay(100); - } - - uint32_t retry = 2; - while (!WiFi.mode(wifi_mode) && retry--) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR("Retry set Mode...")); - delay(100); - } - - if (wifi_mode == WIFI_OFF) { - delay(1000); - WiFi.forceSleepBegin(); - delay(1); - } else { - delay(30); - } -} - -void WiFiSetSleepMode(void) -{ -# 156 "C:/shared/sonoff/Git/Tasmota/tasmota/support_wifi.ino" -#if defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) -#else - if (sleep && Settings.flag3.sleep_normal) { - WiFi.setSleepMode(WIFI_LIGHT_SLEEP); - } else { - WiFi.setSleepMode(WIFI_MODEM_SLEEP); - } -#endif - WifiSetOutputPower(); -} - -void WifiBegin(uint8_t flag, uint8_t channel) -{ - const char kWifiPhyMode[] = " BGN"; - -#ifdef USE_EMULATION - UdpDisconnect(); -#endif - -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR(D_PATCH_ISSUE_2186)); - - WifiSetMode(WIFI_OFF); -#endif - - WiFi.persistent(false); - WiFi.disconnect(true); - delay(200); - - WifiSetMode(WIFI_STA); - WiFiSetSleepMode(); - - - if (!WiFi.getAutoConnect()) { WiFi.setAutoConnect(true); } - - - - - - switch (flag) { - case 0: - case 1: - Settings.sta_active = flag; - break; - case 2: - Settings.sta_active ^= 1; - } - if (!strlen(SettingsText(SET_STASSID1 + Settings.sta_active))) { - Settings.sta_active ^= 1; - } - if (Settings.ip_address[0]) { - WiFi.config(Settings.ip_address[0], Settings.ip_address[1], Settings.ip_address[2], Settings.ip_address[3]); - } - WiFi.hostname(my_hostname); - - char stemp[40] = { 0 }; - if (channel) { - WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active), channel, Wifi.bssid); - - char hex_char[18]; - snprintf_P(stemp, sizeof(stemp), PSTR(" Channel %d BSSId %s"), channel, ToHex_P((unsigned char*)Wifi.bssid, 6, hex_char, sizeof(hex_char), ':')); - } else { - WiFi.begin(SettingsText(SET_STASSID1 + Settings.sta_active), SettingsText(SET_STAPWD1 + Settings.sta_active)); - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP "%d %s%s " D_IN_MODE " 11%c " D_AS " %s..."), - Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), stemp, kWifiPhyMode[WiFi.getPhyMode() & 0x3], my_hostname); - -#if LWIP_IPV6 - for (bool configured = false; !configured;) { - uint16_t cfgcnt = 0; - for (auto addr : addrList) { - if ((configured = !addr.isLocal() && addr.isV6()) || cfgcnt==30) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI "Got IPv6 global address %s"), addr.toString().c_str()); - break; - } - delay(500); - cfgcnt++; - } - } -#endif -} - -void WifiBeginAfterScan(void) -{ - - if (0 == Wifi.scan_state) { return; } - - if (1 == Wifi.scan_state) { - memset((void*) &Wifi.bssid, 0, sizeof(Wifi.bssid)); - Wifi.best_network_db = -127; - Wifi.scan_state = 3; - } - - if (2 == Wifi.scan_state) { - uint8_t* bssid = WiFi.BSSID(); - memcpy((void*) &Wifi.bssid, (void*) bssid, sizeof(Wifi.bssid)); - Wifi.best_network_db = WiFi.RSSI(); - if (Wifi.best_network_db < -WIFI_RSSI_THRESHOLD) { - Wifi.best_network_db += WIFI_RSSI_THRESHOLD; - } - Wifi.scan_state = 3; - } - - if (3 == Wifi.scan_state) { - if (WiFi.scanComplete() != WIFI_SCAN_RUNNING) { - WiFi.scanNetworks(true); - Wifi.scan_state++; - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR("Network (re)scan started...")); - return; - } - } - int8_t wifi_scan_result = WiFi.scanComplete(); - - if (4 == Wifi.scan_state) { - if (wifi_scan_result != WIFI_SCAN_RUNNING) { - Wifi.scan_state++; - } - } - - if (5 == Wifi.scan_state) { - uint32_t number_known = 0; - int32_t channel_max = 0; - int8_t ap_max = 3; - uint8_t bssid_max[6]; - memcpy((void*) &bssid_max, (void*) &Wifi.bssid, sizeof(bssid_max)); - - int32_t channel = 0; - int8_t ap = 3; - uint8_t last_bssid[6]; - memcpy((void*) &last_bssid, (void*) &Wifi.bssid, sizeof(last_bssid)); - - if (wifi_scan_result > 0) { - - for (uint32_t i = 0; i < wifi_scan_result; ++i) { - - String ssid_scan; - int32_t rssi_scan; - uint8_t sec_scan; - uint8_t* bssid_scan; - int32_t chan_scan; - bool hidden_scan; - - WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, bssid_scan, chan_scan, hidden_scan); - - bool known = false; - uint32_t j; - for (j = 0; j < MAX_SSIDS; j++) { - if (ssid_scan == SettingsText(SET_STASSID1 + j)) { - known = true; - number_known++; - if (rssi_scan > Wifi.best_network_db) { - if (sec_scan == ENC_TYPE_NONE || SettingsText(SET_STAPWD1 + j)) { - - memcpy((void*) &bssid_max, (void*) bssid_scan, sizeof(bssid_max)); - channel_max = chan_scan; - ap_max = j; - - for (uint32_t i = 0; i < sizeof(Wifi.bssid_last); i++) { - if (bssid_scan[i] != Wifi.bssid_last[i]) { - Wifi.best_network_db = (int8_t)rssi_scan; - channel = chan_scan; - ap = j; - memcpy((void*) &Wifi.bssid, (void*) bssid_scan, sizeof(Wifi.bssid)); - - memcpy((void*) &Wifi.bssid_last, (void*) bssid_scan, sizeof(Wifi.bssid_last)); - break; - } - } - } - } - break; - } - } - char hex_char[18]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network %d, AP%c, SSId %s, Channel %d, BSSId %s, RSSI %d, Encryption %d"), - i, - (known) ? (j) ? '2' : '1' : '-', - ssid_scan.c_str(), - chan_scan, - ToHex_P((unsigned char*)bssid_scan, 6, hex_char, sizeof(hex_char), ':'), - rssi_scan, - (sec_scan == ENC_TYPE_NONE) ? 0 : 1); - delay(0); - } - WiFi.scanDelete(); - delay(0); - } - - - if (number_known == 1) { - - memset((void*) &Wifi.bssid_last, 0, sizeof(Wifi.bssid_last)); - memcpy((void*) &Wifi.bssid, (void*) bssid_max, sizeof(Wifi.bssid)); - channel = channel_max; - ap = ap_max; - } - - Wifi.scan_state = 0; - - for (uint32_t i = 0; i < sizeof(Wifi.bssid); i++) { - if (last_bssid[i] != Wifi.bssid[i]) { - WifiBegin(ap, channel); - break; - } - } - } -} - -uint16_t WifiLinkCount(void) -{ - return Wifi.link_count; -} - -String WifiDowntime(void) -{ - return GetDuration(Wifi.downtime); -} - -void WifiSetState(uint8_t state) -{ - if (state == global_state.wifi_down) { - if (state) { - rules_flag.wifi_connected = 1; - Wifi.link_count++; - Wifi.downtime += UpTime() - Wifi.last_event; - } else { - rules_flag.wifi_disconnected = 1; - Wifi.last_event = UpTime(); - } - } - global_state.wifi_down = state ^1; -} - -#if LWIP_IPV6 -bool WifiCheckIPv6(void) -{ - bool ipv6_global=false; - - for (auto a : addrList) { - if(!a.isLocal() && a.isV6()) ipv6_global=true; - } - return ipv6_global; -} - -String WifiGetIPv6(void) -{ - for (auto a : addrList) { - if(!a.isLocal() && a.isV6()) return a.toString(); - } - return ""; -} - -bool WifiCheckIPAddrStatus(void) -{ - bool ip_global=false; - - for (auto a : addrList) { - if(!a.isLocal()) ip_global=true; - } - return ip_global; -} -#endif - -void WifiCheckIp(void) -{ -#if LWIP_IPV6 - if(WifiCheckIPAddrStatus()) { - Wifi.status = WL_CONNECTED; -#else - if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0)) { -#endif - - memset((void*) &Wifi.bssid_last, 0, sizeof(Wifi.bssid_last)); - WifiSetState(1); - Wifi.counter = WIFI_CHECK_SEC; - Wifi.retry = Wifi.retry_init; - if (Wifi.status != WL_CONNECTED) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECTED)); - } - Wifi.status = WL_CONNECTED; -#ifdef USE_DISCOVERY -#ifdef WEBSERVER_ADVERTISE - if (2 == Wifi.mdns_begun) { - MDNS.update(); - AddLog_P(LOG_LEVEL_DEBUG_MORE, D_LOG_MDNS, "MDNS.update"); - } -#endif -#endif - } else { - WifiSetState(0); - uint8_t wifi_config_tool = Settings.sta_config; - Wifi.status = WiFi.status(); - switch (Wifi.status) { - case WL_CONNECTED: - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_NO_IP_ADDRESS)); - - - wifi_station_dhcpc_start(); - break; - case WL_NO_SSID_AVAIL: - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_NOT_REACHED)); - - if (WIFI_WAIT == Settings.sta_config) { - Wifi.retry = Wifi.retry_init; - } else { - if (Wifi.retry > (Wifi.retry_init / 2)) { - Wifi.retry = Wifi.retry_init / 2; - } - else if (Wifi.retry) { - Wifi.retry = 0; - } - } - - break; - case WL_CONNECT_FAILED: - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_WRONG_PASSWORD)); - - if (Wifi.retry > (Wifi.retry_init / 2)) { - Wifi.retry = Wifi.retry_init / 2; - } - else if (Wifi.retry) { - Wifi.retry = 0; - } - - break; - default: - - if (!Wifi.retry || ((Wifi.retry_init / 2) == Wifi.retry)) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_WIFI, PSTR(D_CONNECT_FAILED_AP_TIMEOUT)); - } else { - if (!strlen(SettingsText(SET_STASSID1)) && !strlen(SettingsText(SET_STASSID2))) { - wifi_config_tool = WIFI_MANAGER; - Wifi.retry = 0; - } else { - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, PSTR(D_ATTEMPTING_CONNECTION)); - } - } - } - - if (Wifi.retry) { - if (Settings.flag3.use_wifi_scan) { - - if ((Wifi.retry_init == Wifi.retry) || ((Wifi.retry_init / 2) == Wifi.retry)){ - Wifi.scan_state = 1; - } - } else { - if (Wifi.retry_init == Wifi.retry) { - WifiBegin(3, 0); - } - if ((Settings.sta_config != WIFI_WAIT) && ((Wifi.retry_init / 2) == Wifi.retry)) { - WifiBegin(2, 0); - } - } - Wifi.counter = 1; - Wifi.retry--; - } else { - WifiConfig(wifi_config_tool); - Wifi.counter = 1; - Wifi.retry = Wifi.retry_init; - } - } -} - -void WifiCheck(uint8_t param) -{ - Wifi.counter--; - switch (param) { - case WIFI_SERIAL: - case WIFI_MANAGER: - WifiConfig(param); - break; - default: - if (Wifi.config_counter) { - Wifi.config_counter--; - Wifi.counter = Wifi.config_counter +5; - if (Wifi.config_counter) { - if (!Wifi.config_counter) { - if (strlen(WiFi.SSID().c_str())) { - SettingsUpdateText(SET_STASSID1, WiFi.SSID().c_str()); - } - if (strlen(WiFi.psk().c_str())) { - SettingsUpdateText(SET_STAPWD1, WiFi.psk().c_str()); - } - Settings.sta_active = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_WCFG_2_WIFIMANAGER D_CMND_SSID "1 %s"), SettingsText(SET_STASSID1)); - } - } - if (!Wifi.config_counter) { - - restart_flag = 2; - } - } else { - if (Wifi.scan_state) { WifiBeginAfterScan(); } - - if (Wifi.counter <= 0) { - AddLog_P(LOG_LEVEL_DEBUG_MORE, S_LOG_WIFI, PSTR(D_CHECKING_CONNECTION)); - Wifi.counter = WIFI_CHECK_SEC; - WifiCheckIp(); - } -#if LWIP_IPV6 - if (WifiCheckIPAddrStatus()) { -#else - if ((WL_CONNECTED == WiFi.status()) && (static_cast(WiFi.localIP()) != 0) && !Wifi.config_type) { -#endif - WifiSetState(1); - - if (Settings.flag3.use_wifi_rescan) { - if (!(uptime % (60 * WIFI_RESCAN_MINUTES))) { - Wifi.scan_state = 2; - } - } - -#ifdef FIRMWARE_MINIMAL - if (1 == RtcSettings.ota_loader) { - RtcSettings.ota_loader = 0; - ota_state_flag = 3; - } -#endif - -#ifdef USE_DISCOVERY - if (Settings.flag3.mdns_enabled) { - if (!Wifi.mdns_begun) { - - - - - - Wifi.mdns_begun = (uint8_t)MDNS.begin(my_hostname); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS "%s"), (Wifi.mdns_begun) ? D_INITIALIZED : D_FAILED); - - } - } -#endif - -#ifdef USE_WEBSERVER - if (Settings.webserver) { - StartWebserver(Settings.webserver, WiFi.localIP()); -#ifdef USE_DISCOVERY -#ifdef WEBSERVER_ADVERTISE - if (1 == Wifi.mdns_begun) { - Wifi.mdns_begun = 2; - MDNS.addService("http", "tcp", WEB_PORT); - } -#endif -#endif - } else { - StopWebserver(); - } -#ifdef USE_EMULATION - if (Settings.flag2.emulation) { UdpConnect(); } -#endif -#endif - -#ifdef USE_KNX - if (!knx_started && Settings.flag.knx_enabled) { - KNXStart(); - knx_started = true; - } -#endif - - } else { - WifiSetState(0); -#ifdef USE_EMULATION - UdpDisconnect(); -#endif - Wifi.mdns_begun = 0; -#ifdef USE_KNX - knx_started = false; -#endif - } - } - } -} - -int WifiState(void) -{ - int state = -1; - - if (!global_state.wifi_down) { state = WIFI_RESTART; } - if (Wifi.config_type) { state = Wifi.config_type; } - return state; -} - -String WifiGetOutputPower(void) -{ - char stemp1[TOPSZ]; - dtostrfd((float)(Settings.wifi_output_power) / 10, 1, stemp1); - return String(stemp1); -} -void WifiSetOutputPower(void) -{ - WiFi.setOutputPower((float)(Settings.wifi_output_power) / 10); -} - -void WifiConnect(void) -{ - WifiSetState(0); - WifiSetOutputPower(); - WiFi.persistent(false); - Wifi.status = 0; - - - Wifi.retry_init = WIFI_RETRY_OFFSET_SEC + (ESP.getChipId() & 0xF); - Wifi.retry = Wifi.retry_init; - Wifi.counter = 1; -} - - - -void WifiDisconnect(void) -{ - - WiFi.persistent(true); - ETS_UART_INTR_DISABLE(); - wifi_station_disconnect(); - ETS_UART_INTR_ENABLE(); - WiFi.persistent(false); -} - -void WifiShutdown(void) -{ - delay(100); - if (Settings.flag.mqtt_enabled) { - MqttDisconnect(); - } - WifiDisconnect(); -} - -void EspRestart(void) -{ - WifiShutdown(); - CrashDumpClear(); - - ESP.reset(); -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota_ca.ino" -# 24 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota_ca.ino" -#ifdef USE_MQTT_TLS_CA_CERT - -#ifndef USE_MQTT_AWS_IOT -# 38 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota_ca.ino" -static const unsigned char PROGMEM TA0_DN[] = { - 0x30, 0x4A, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, - 0x13, 0x0D, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, - 0x13, 0x1A, 0x4C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x45, 0x6E, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, - 0x79, 0x20, 0x58, 0x33 -}; - -static const unsigned char PROGMEM TA0_RSA_N[] = { - 0x9C, 0xD3, 0x0C, 0xF0, 0x5A, 0xE5, 0x2E, 0x47, 0xB7, 0x72, 0x5D, 0x37, - 0x83, 0xB3, 0x68, 0x63, 0x30, 0xEA, 0xD7, 0x35, 0x26, 0x19, 0x25, 0xE1, - 0xBD, 0xBE, 0x35, 0xF1, 0x70, 0x92, 0x2F, 0xB7, 0xB8, 0x4B, 0x41, 0x05, - 0xAB, 0xA9, 0x9E, 0x35, 0x08, 0x58, 0xEC, 0xB1, 0x2A, 0xC4, 0x68, 0x87, - 0x0B, 0xA3, 0xE3, 0x75, 0xE4, 0xE6, 0xF3, 0xA7, 0x62, 0x71, 0xBA, 0x79, - 0x81, 0x60, 0x1F, 0xD7, 0x91, 0x9A, 0x9F, 0xF3, 0xD0, 0x78, 0x67, 0x71, - 0xC8, 0x69, 0x0E, 0x95, 0x91, 0xCF, 0xFE, 0xE6, 0x99, 0xE9, 0x60, 0x3C, - 0x48, 0xCC, 0x7E, 0xCA, 0x4D, 0x77, 0x12, 0x24, 0x9D, 0x47, 0x1B, 0x5A, - 0xEB, 0xB9, 0xEC, 0x1E, 0x37, 0x00, 0x1C, 0x9C, 0xAC, 0x7B, 0xA7, 0x05, - 0xEA, 0xCE, 0x4A, 0xEB, 0xBD, 0x41, 0xE5, 0x36, 0x98, 0xB9, 0xCB, 0xFD, - 0x6D, 0x3C, 0x96, 0x68, 0xDF, 0x23, 0x2A, 0x42, 0x90, 0x0C, 0x86, 0x74, - 0x67, 0xC8, 0x7F, 0xA5, 0x9A, 0xB8, 0x52, 0x61, 0x14, 0x13, 0x3F, 0x65, - 0xE9, 0x82, 0x87, 0xCB, 0xDB, 0xFA, 0x0E, 0x56, 0xF6, 0x86, 0x89, 0xF3, - 0x85, 0x3F, 0x97, 0x86, 0xAF, 0xB0, 0xDC, 0x1A, 0xEF, 0x6B, 0x0D, 0x95, - 0x16, 0x7D, 0xC4, 0x2B, 0xA0, 0x65, 0xB2, 0x99, 0x04, 0x36, 0x75, 0x80, - 0x6B, 0xAC, 0x4A, 0xF3, 0x1B, 0x90, 0x49, 0x78, 0x2F, 0xA2, 0x96, 0x4F, - 0x2A, 0x20, 0x25, 0x29, 0x04, 0xC6, 0x74, 0xC0, 0xD0, 0x31, 0xCD, 0x8F, - 0x31, 0x38, 0x95, 0x16, 0xBA, 0xA8, 0x33, 0xB8, 0x43, 0xF1, 0xB1, 0x1F, - 0xC3, 0x30, 0x7F, 0xA2, 0x79, 0x31, 0x13, 0x3D, 0x2D, 0x36, 0xF8, 0xE3, - 0xFC, 0xF2, 0x33, 0x6A, 0xB9, 0x39, 0x31, 0xC5, 0xAF, 0xC4, 0x8D, 0x0D, - 0x1D, 0x64, 0x16, 0x33, 0xAA, 0xFA, 0x84, 0x29, 0xB6, 0xD4, 0x0B, 0xC0, - 0xD8, 0x7D, 0xC3, 0x93 -}; - -static const unsigned char TA0_RSA_E[] = { - 0x01, 0x00, 0x01 -}; - -static const br_x509_trust_anchor PROGMEM LetsEncryptX3CrossSigned_TA = { - { (unsigned char *)TA0_DN, sizeof TA0_DN }, - BR_X509_TA_CA, - { - BR_KEYTYPE_RSA, - { .rsa = { - (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N, - (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E, - } } - } -}; - -#define TAs_NUM 1 - -#endif - -#ifdef USE_MQTT_AWS_IOT -# 106 "C:/shared/sonoff/Git/Tasmota/tasmota/tasmota_ca.ino" -const unsigned char PROGMEM TA0_DN[] = { - 0x30, 0x39, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x55, 0x53, 0x31, 0x0F, 0x30, 0x0D, 0x06, 0x03, 0x55, 0x04, 0x0A, - 0x13, 0x06, 0x41, 0x6D, 0x61, 0x7A, 0x6F, 0x6E, 0x31, 0x19, 0x30, 0x17, - 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x41, 0x6D, 0x61, 0x7A, 0x6F, - 0x6E, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31 -}; - -const unsigned char PROGMEM TA0_RSA_N[] = { - 0xB2, 0x78, 0x80, 0x71, 0xCA, 0x78, 0xD5, 0xE3, 0x71, 0xAF, 0x47, 0x80, - 0x50, 0x74, 0x7D, 0x6E, 0xD8, 0xD7, 0x88, 0x76, 0xF4, 0x99, 0x68, 0xF7, - 0x58, 0x21, 0x60, 0xF9, 0x74, 0x84, 0x01, 0x2F, 0xAC, 0x02, 0x2D, 0x86, - 0xD3, 0xA0, 0x43, 0x7A, 0x4E, 0xB2, 0xA4, 0xD0, 0x36, 0xBA, 0x01, 0xBE, - 0x8D, 0xDB, 0x48, 0xC8, 0x07, 0x17, 0x36, 0x4C, 0xF4, 0xEE, 0x88, 0x23, - 0xC7, 0x3E, 0xEB, 0x37, 0xF5, 0xB5, 0x19, 0xF8, 0x49, 0x68, 0xB0, 0xDE, - 0xD7, 0xB9, 0x76, 0x38, 0x1D, 0x61, 0x9E, 0xA4, 0xFE, 0x82, 0x36, 0xA5, - 0xE5, 0x4A, 0x56, 0xE4, 0x45, 0xE1, 0xF9, 0xFD, 0xB4, 0x16, 0xFA, 0x74, - 0xDA, 0x9C, 0x9B, 0x35, 0x39, 0x2F, 0xFA, 0xB0, 0x20, 0x50, 0x06, 0x6C, - 0x7A, 0xD0, 0x80, 0xB2, 0xA6, 0xF9, 0xAF, 0xEC, 0x47, 0x19, 0x8F, 0x50, - 0x38, 0x07, 0xDC, 0xA2, 0x87, 0x39, 0x58, 0xF8, 0xBA, 0xD5, 0xA9, 0xF9, - 0x48, 0x67, 0x30, 0x96, 0xEE, 0x94, 0x78, 0x5E, 0x6F, 0x89, 0xA3, 0x51, - 0xC0, 0x30, 0x86, 0x66, 0xA1, 0x45, 0x66, 0xBA, 0x54, 0xEB, 0xA3, 0xC3, - 0x91, 0xF9, 0x48, 0xDC, 0xFF, 0xD1, 0xE8, 0x30, 0x2D, 0x7D, 0x2D, 0x74, - 0x70, 0x35, 0xD7, 0x88, 0x24, 0xF7, 0x9E, 0xC4, 0x59, 0x6E, 0xBB, 0x73, - 0x87, 0x17, 0xF2, 0x32, 0x46, 0x28, 0xB8, 0x43, 0xFA, 0xB7, 0x1D, 0xAA, - 0xCA, 0xB4, 0xF2, 0x9F, 0x24, 0x0E, 0x2D, 0x4B, 0xF7, 0x71, 0x5C, 0x5E, - 0x69, 0xFF, 0xEA, 0x95, 0x02, 0xCB, 0x38, 0x8A, 0xAE, 0x50, 0x38, 0x6F, - 0xDB, 0xFB, 0x2D, 0x62, 0x1B, 0xC5, 0xC7, 0x1E, 0x54, 0xE1, 0x77, 0xE0, - 0x67, 0xC8, 0x0F, 0x9C, 0x87, 0x23, 0xD6, 0x3F, 0x40, 0x20, 0x7F, 0x20, - 0x80, 0xC4, 0x80, 0x4C, 0x3E, 0x3B, 0x24, 0x26, 0x8E, 0x04, 0xAE, 0x6C, - 0x9A, 0xC8, 0xAA, 0x0D -}; - -static const unsigned char PROGMEM TA0_RSA_E[] = { - 0x01, 0x00, 0x01 -}; - -const br_x509_trust_anchor PROGMEM AmazonRootCA1_TA = { - { (unsigned char *)TA0_DN, sizeof TA0_DN }, - BR_X509_TA_CA, - { - BR_KEYTYPE_RSA, - { .rsa = { - (unsigned char *)TA0_RSA_N, sizeof TA0_RSA_N, - (unsigned char *)TA0_RSA_E, sizeof TA0_RSA_E, - } } - } -}; - -#define TAs_NUM 1 - -#endif - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_01_webserver.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_01_webserver.ino" -#ifdef USE_WEBSERVER - - - - - - - -#define XDRV_01 1 - -#ifndef WIFI_SOFT_AP_CHANNEL -#define WIFI_SOFT_AP_CHANNEL 1 -#endif - -const uint16_t CHUNKED_BUFFER_SIZE = 400; - -const uint16_t HTTP_REFRESH_TIME = 2345; -#define HTTP_RESTART_RECONNECT_TIME 9000 -#define HTTP_OTA_RESTART_RECONNECT_TIME 20000 - -#include -#include - -#ifdef USE_RF_FLASH -uint8_t *efm8bb1_update = nullptr; -#endif - -enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1, UPL_TASMOTASLAVE }; - -static const char * HEADER_KEYS[] = { "User-Agent", }; - -const char HTTP_HEADER[] PROGMEM = - "" - "" - "" - "" - "%s - %s" - - ""; - -const char HTTP_HEAD_STYLE1[] PROGMEM = - "" - - "" - "" - "
" -#ifdef FIRMWARE_MINIMAL - "

" D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "

" -#endif - "
" -#ifdef LANGUAGE_MODULE_NAME - "

" D_MODULE " %s

" -#else - "

%s " D_MODULE "

" -#endif - "

%s

"; - -const char HTTP_MSG_SLIDER_GRADIENT[] PROGMEM = - "
" - "" - "
"; -const char HTTP_MSG_SLIDER_SHUTTER[] PROGMEM = - "
" D_CLOSE "" D_OPEN "
" - "
"; - -const char HTTP_MSG_RSTRT[] PROGMEM = - "
" D_DEVICE_WILL_RESTART "

"; - -const char HTTP_FORM_LOGIN[] PROGMEM = - "
" - "" - "

" D_USER "

" - "

" D_PASSWORD "

" - "
" - "" - "
"; - -const char HTTP_FORM_TEMPLATE[] PROGMEM = - "
 " D_TEMPLATE_PARAMETERS " " - "
"; -const char HTTP_FORM_TEMPLATE_FLAG[] PROGMEM = - "

" - "
 " D_TEMPLATE_FLAGS " 

" - - "

"; - -const char HTTP_FORM_MODULE[] PROGMEM = - "
 " D_MODULE_PARAMETERS " " - "" - "

" D_MODULE_TYPE " (%s)

" - "
"; - -const char HTTP_FORM_WIFI[] PROGMEM = - "
 " D_WIFI_PARAMETERS " " - "" - "

" D_AP1_SSID " (" STA_SSID1 ")

" - "

" D_AP1_PASSWORD "

" - "

" D_AP2_SSID " (" STA_SSID2 ")

" - "

" D_AP2_PASSWORD "

" - "

" D_HOSTNAME " (%s)

" - "

" D_CORS_DOMAIN "

"; - -const char HTTP_FORM_LOG1[] PROGMEM = - "
 " D_LOGGING_PARAMETERS " " - ""; -const char HTTP_FORM_LOG2[] PROGMEM = - "

" D_SYSLOG_HOST " (" SYS_LOG_HOST ")

" - "

" D_SYSLOG_PORT " (" STR(SYS_LOG_PORT) ")

" - "

" D_TELEMETRY_PERIOD " (" STR(TELE_PERIOD) ")

"; - -const char HTTP_FORM_OTHER[] PROGMEM = - "
 " D_OTHER_PARAMETERS " " - "" - "

" - "
 " D_TEMPLATE " " - "

" - "

" D_ACTIVATE "

" - "
" - "
" - "" D_WEB_ADMIN_PASSWORD "

" - "
" - "" D_MQTT_ENABLE "
" - "
"; - -const char HTTP_FORM_END[] PROGMEM = - "
" - "" - "
"; - -const char HTTP_FORM_RST[] PROGMEM = - "
" - "
 " D_RESTORE_CONFIGURATION " "; -const char HTTP_FORM_UPG[] PROGMEM = - "
" - "
 " D_UPGRADE_BY_WEBSERVER " " - "
" - "
" D_OTA_URL "

" - "
" - "


" - "
 " D_UPGRADE_BY_FILE_UPLOAD " "; -const char HTTP_FORM_RST_UPG[] PROGMEM = - "
" - "

" - "
" - "
" - "
" - ""; - -const char HTTP_FORM_CMND[] PROGMEM = - "


" - "
" - "
" - - ""; - -const char HTTP_TABLE100[] PROGMEM = - "
"; - -const char HTTP_COUNTER[] PROGMEM = - "
"; - -const char HTTP_END[] PROGMEM = - "" - "" - "" - ""; - -const char HTTP_DEVICE_CONTROL[] PROGMEM = ""; -const char HTTP_DEVICE_STATE[] PROGMEM = ""; - -enum ButtonTitle { - BUTTON_RESTART, BUTTON_RESET_CONFIGURATION, - BUTTON_MAIN, BUTTON_CONFIGURATION, BUTTON_INFORMATION, BUTTON_FIRMWARE_UPGRADE, BUTTON_CONSOLE, - BUTTON_MODULE, BUTTON_WIFI, BUTTON_LOGGING, BUTTON_OTHER, BUTTON_TEMPLATE, BUTTON_BACKUP, BUTTON_RESTORE }; -const char kButtonTitle[] PROGMEM = - D_RESTART "|" D_RESET_CONFIGURATION "|" - D_MAIN_MENU "|" D_CONFIGURATION "|" D_INFORMATION "|" D_FIRMWARE_UPGRADE "|" D_CONSOLE "|" - D_CONFIGURE_MODULE "|" D_CONFIGURE_WIFI"|" D_CONFIGURE_LOGGING "|" D_CONFIGURE_OTHER "|" D_CONFIGURE_TEMPLATE "|" D_BACKUP_CONFIGURATION "|" D_RESTORE_CONFIGURATION; -const char kButtonAction[] PROGMEM = - ".|rt|" - ".|cn|in|up|cs|" - "md|wi|lg|co|tp|dl|rs"; -const char kButtonConfirm[] PROGMEM = D_CONFIRM_RESTART "|" D_CONFIRM_RESET_CONFIGURATION; - -enum CTypes { CT_HTML, CT_PLAIN, CT_XML, CT_JSON, CT_STREAM }; -const char kContentTypes[] PROGMEM = "text/html|text/plain|text/xml|application/json|application/octet-stream"; - -const char kLoggingOptions[] PROGMEM = D_SERIAL_LOG_LEVEL "|" D_WEB_LOG_LEVEL "|" D_MQTT_LOG_LEVEL "|" D_SYS_LOG_LEVEL; -const char kLoggingLevels[] PROGMEM = D_NONE "|" D_ERROR "|" D_INFO "|" D_DEBUG "|" D_MORE_DEBUG; - -const char kEmulationOptions[] PROGMEM = D_NONE "|" D_BELKIN_WEMO "|" D_HUE_BRIDGE; - -const char kUploadErrors[] PROGMEM = - D_UPLOAD_ERR_1 "|" D_UPLOAD_ERR_2 "|" D_UPLOAD_ERR_3 "|" D_UPLOAD_ERR_4 "|" D_UPLOAD_ERR_5 "|" D_UPLOAD_ERR_6 "|" D_UPLOAD_ERR_7 "|" D_UPLOAD_ERR_8 "|" D_UPLOAD_ERR_9 -#ifdef USE_RF_FLASH - "|" D_UPLOAD_ERR_10 "|" D_UPLOAD_ERR_11 "|" D_UPLOAD_ERR_12 "|" D_UPLOAD_ERR_13 -#endif - "|" D_UPLOAD_ERR_14 - ; - -const uint16_t DNS_PORT = 53; -enum HttpOptions {HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER, HTTP_MANAGER_RESET_ONLY}; - -DNSServer *DnsServer; -ESP8266WebServer *WebServer; - -struct WEB { - String chunk_buffer = ""; - bool reset_web_log_flag = false; - uint8_t state = HTTP_OFF; - uint8_t upload_error = 0; - uint8_t upload_file_type; - uint8_t upload_progress_dot_count; - uint8_t config_block_count = 0; - uint8_t config_xor_on = 0; - uint8_t config_xor_on_set = CONFIG_FILE_XOR; -} Web; - - -static void WebGetArg(const char* arg, char* out, size_t max) -{ - String s = WebServer->arg(arg); - strlcpy(out, s.c_str(), max); - -} - -static bool WifiIsInManagerMode(){ - return (HTTP_MANAGER == Web.state || HTTP_MANAGER_RESET_ONLY == Web.state); -} - -void ShowWebSource(uint32_t source) -{ - if ((source > 0) && (source < SRC_MAX)) { - char stemp1[20]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SRC: %s from %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource), WebServer->client().remoteIP().toString().c_str()); - } -} - -void ExecuteWebCommand(char* svalue, uint32_t source) -{ - ShowWebSource(source); - last_source = source; - ExecuteCommand(svalue, SRC_IGNORE); -} - -void StartWebserver(int type, IPAddress ipweb) -{ - if (!Settings.web_refresh) { Settings.web_refresh = HTTP_REFRESH_TIME; } - if (!Web.state) { - if (!WebServer) { - WebServer = new ESP8266WebServer((HTTP_MANAGER == type || HTTP_MANAGER_RESET_ONLY == type) ? 80 : WEB_PORT); - WebServer->on("/", HandleRoot); - WebServer->onNotFound(HandleNotFound); - WebServer->on("/up", HandleUpgradeFirmware); - WebServer->on("/u1", HandleUpgradeFirmwareStart); - WebServer->on("/u2", HTTP_POST, HandleUploadDone, HandleUploadLoop); - WebServer->on("/u2", HTTP_OPTIONS, HandlePreflightRequest); - WebServer->on("/cs", HTTP_GET, HandleConsole); - WebServer->on("/cs", HTTP_OPTIONS, HandlePreflightRequest); - WebServer->on("/cm", HandleHttpCommand); -#ifndef FIRMWARE_MINIMAL - WebServer->on("/cn", HandleConfiguration); - WebServer->on("/md", HandleModuleConfiguration); - WebServer->on("/wi", HandleWifiConfiguration); - WebServer->on("/lg", HandleLoggingConfiguration); - WebServer->on("/tp", HandleTemplateConfiguration); - WebServer->on("/co", HandleOtherConfiguration); - WebServer->on("/dl", HandleBackupConfiguration); - WebServer->on("/rs", HandleRestoreConfiguration); - WebServer->on("/rt", HandleResetConfiguration); - WebServer->on("/in", HandleInformation); - XdrvCall(FUNC_WEB_ADD_HANDLER); - XsnsCall(FUNC_WEB_ADD_HANDLER); -#endif - } - Web.reset_web_log_flag = false; - - - - WebServer->collectHeaders(HEADER_KEYS, sizeof(HEADER_KEYS)/sizeof(char*)); - - WebServer->begin(); - } - if (Web.state != type) { -#if LWIP_IPV6 - String ipv6_addr = WifiGetIPv6(); - if(ipv6_addr!="") AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s and IPv6 global address %s "), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str(),ipv6_addr.c_str()); - else AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str()); -#else - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_ACTIVE_ON " %s%s " D_WITH_IP_ADDRESS " %s"), my_hostname, (Wifi.mdns_begun) ? ".local" : "", ipweb.toString().c_str()); -#endif - rules_flag.http_init = 1; - } - if (type) { Web.state = type; } -} - -void StopWebserver(void) -{ - if (Web.state) { - WebServer->close(); - Web.state = HTTP_OFF; - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_HTTP D_WEBSERVER_STOPPED)); - } -} - -void WifiManagerBegin(bool reset_only) -{ - - if (!global_state.wifi_down) { - - WifiSetMode(WIFI_AP_STA); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION)); - } else { - - WifiSetMode(WIFI_AP); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_WIFIMANAGER_SET_ACCESSPOINT)); - } - - StopWebserver(); - - DnsServer = new DNSServer(); - - int channel = WIFI_SOFT_AP_CHANNEL; - if ((channel < 1) || (channel > 13)) { channel = 1; } - -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - - WiFi.softAP(my_hostname, WIFI_AP_PASSPHRASE, channel, 0); -#else - - WiFi.softAP(my_hostname, WIFI_AP_PASSPHRASE, channel, 0, 1); -#endif - - delay(500); - - DnsServer->setErrorReplyCode(DNSReplyCode::NoError); - DnsServer->start(DNS_PORT, "*", WiFi.softAPIP()); - - StartWebserver((reset_only ? HTTP_MANAGER_RESET_ONLY : HTTP_MANAGER), WiFi.softAPIP()); -} - -void PollDnsWebserver(void) -{ - if (DnsServer) { DnsServer->processNextRequest(); } - if (WebServer) { WebServer->handleClient(); } -} - - - -bool WebAuthenticate(void) -{ - if (strlen(SettingsText(SET_WEBPWD)) && (HTTP_MANAGER_RESET_ONLY != Web.state)) { - return WebServer->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD)); - } else { - return true; - } -} - -bool HttpCheckPriviledgedAccess(bool autorequestauth = true) -{ - if (HTTP_USER == Web.state) { - HandleRoot(); - return false; - } - if (autorequestauth && !WebAuthenticate()) { - WebServer->requestAuthentication(); - return false; - } - return true; -} - -void HttpHeaderCors(void) -{ - if (strlen(SettingsText(SET_CORS))) { - WebServer->sendHeader(F("Access-Control-Allow-Origin"), SettingsText(SET_CORS)); - } -} - -void WSHeaderSend(void) -{ - WebServer->sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); - WebServer->sendHeader(F("Pragma"), F("no-cache")); - WebServer->sendHeader(F("Expires"), F("-1")); - HttpHeaderCors(); -} - - - - - -void WSSend(int code, int ctype, const String& content) -{ - char ct[25]; - WebServer->send(code, GetTextIndexed(ct, sizeof(ct), ctype, kContentTypes), content); -} - - - - - -void WSContentBegin(int code, int ctype) -{ - WebServer->client().flush(); - WSHeaderSend(); -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - WebServer->sendHeader(F("Accept-Ranges"),F("none")); - WebServer->sendHeader(F("Transfer-Encoding"),F("chunked")); -#endif - WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); - WSSend(code, ctype, ""); - Web.chunk_buffer = ""; -} - -void _WSContentSend(const String& content) -{ - size_t len = content.length(); - -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - const char * footer = "\r\n"; - char chunk_size[11]; - sprintf(chunk_size, "%x\r\n", len); - WebServer->sendContent(String() + chunk_size + content + footer); -#else - WebServer->sendContent(content); -#endif - -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("WSContentSend")); -#endif - DEBUG_CORE_LOG(PSTR("WEB: Chunk size %d"), len); -} - -void WSContentFlush(void) -{ - if (Web.chunk_buffer.length() > 0) { - _WSContentSend(Web.chunk_buffer); - Web.chunk_buffer = ""; - } -} - -void _WSContentSendBuffer(void) -{ - int len = strlen(mqtt_data); - - if (0 == len) { - return; - } - else if (len == sizeof(mqtt_data)) { - AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: Content too large")); - } - else if (len < CHUNKED_BUFFER_SIZE) { - Web.chunk_buffer += mqtt_data; - len = Web.chunk_buffer.length(); - } - - if (len >= CHUNKED_BUFFER_SIZE) { - WSContentFlush(); - } - if (strlen(mqtt_data) >= CHUNKED_BUFFER_SIZE) { - _WSContentSend(mqtt_data); - } -} - -void WSContentSend_P(const char* formatP, ...) -{ - - va_list arg; - va_start(arg, formatP); - int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); - va_end(arg); - -#ifdef DEBUG_TASMOTA_CORE - if (len > (sizeof(mqtt_data) -1)) { - mqtt_data[33] = '\0'; - DEBUG_CORE_LOG(PSTR("ERROR: WSContentSend_P size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); - } -#endif - - _WSContentSendBuffer(); -} - -void WSContentSend_PD(const char* formatP, ...) -{ - - va_list arg; - va_start(arg, formatP); - int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); - va_end(arg); - -#ifdef DEBUG_TASMOTA_CORE - if (len > (sizeof(mqtt_data) -1)) { - mqtt_data[33] = '\0'; - DEBUG_CORE_LOG(PSTR("ERROR: WSContentSend_PD size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); - } -#endif - - if (D_DECIMAL_SEPARATOR[0] != '.') { - for (uint32_t i = 0; i < len; i++) { - if ('.' == mqtt_data[i]) { - mqtt_data[i] = D_DECIMAL_SEPARATOR[0]; - } - } - } - - _WSContentSendBuffer(); -} - -void WSContentStart_P(const char* title, bool auth) -{ - if (auth && strlen(SettingsText(SET_WEBPWD)) && !WebServer->authenticate(WEB_USERNAME, SettingsText(SET_WEBPWD))) { - return WebServer->requestAuthentication(); - } - - WSContentBegin(200, CT_HTML); - - if (title != nullptr) { - char ctitle[strlen_P(title) +1]; - strcpy_P(ctitle, title); - WSContentSend_P(HTTP_HEADER, SettingsText(SET_FRIENDLYNAME1), ctitle); - } -} - -void WSContentStart_P(const char* title) -{ - WSContentStart_P(title, true); -} - -void WSContentSendStyle_P(const char* formatP, ...) -{ - if (WifiIsInManagerMode()) { - if (WifiConfigCounter()) { - WSContentSend_P(HTTP_SCRIPT_COUNTER); - } - } - WSContentSend_P(HTTP_HEAD_LAST_SCRIPT); - - WSContentSend_P(HTTP_HEAD_STYLE1, WebColor(COL_FORM), WebColor(COL_INPUT), WebColor(COL_INPUT_TEXT), WebColor(COL_INPUT), - WebColor(COL_INPUT_TEXT), WebColor(COL_CONSOLE), WebColor(COL_CONSOLE_TEXT), WebColor(COL_BACKGROUND)); - WSContentSend_P(HTTP_HEAD_STYLE2, WebColor(COL_BUTTON), WebColor(COL_BUTTON_TEXT), WebColor(COL_BUTTON_HOVER), - WebColor(COL_BUTTON_RESET), WebColor(COL_BUTTON_RESET_HOVER), WebColor(COL_BUTTON_SAVE), WebColor(COL_BUTTON_SAVE_HOVER), - WebColor(COL_BUTTON)); - if (formatP != nullptr) { - - va_list arg; - va_start(arg, formatP); - int len = vsnprintf_P(mqtt_data, sizeof(mqtt_data), formatP, arg); - va_end(arg); - -#ifdef DEBUG_TASMOTA_CORE - if (len > (sizeof(mqtt_data) -1)) { - mqtt_data[33] = '\0'; - DEBUG_CORE_LOG(PSTR("ERROR: WSContentSendStyle_P size %d > mqtt_data size %d. Start of data [%s...]"), len, sizeof(mqtt_data), mqtt_data); - } -#endif - - _WSContentSendBuffer(); - } - WSContentSend_P(HTTP_HEAD_STYLE3, WebColor(COL_TEXT), -#ifdef FIRMWARE_MINIMAL - WebColor(COL_TEXT_WARNING), -#endif - WebColor(COL_TITLE), - ModuleName().c_str(), SettingsText(SET_FRIENDLYNAME1)); - if (Settings.flag3.gui_hostname_ip) { - bool lip = (static_cast(WiFi.localIP()) != 0); - bool sip = (static_cast(WiFi.softAPIP()) != 0); - WSContentSend_P(PSTR("

%s%s (%s%s%s)

"), - my_hostname, - (Wifi.mdns_begun) ? ".local" : "", - (lip) ? WiFi.localIP().toString().c_str() : "", - (lip && sip) ? ", " : "", - (sip) ? WiFi.softAPIP().toString().c_str() : ""); - } - WSContentSend_P(PSTR("")); -} - -void WSContentSendStyle(void) -{ - WSContentSendStyle_P(nullptr); -} - -void WSContentButton(uint32_t title_index) -{ - char action[4]; - char title[100]; - - if (title_index <= BUTTON_RESET_CONFIGURATION) { - char confirm[100]; - WSContentSend_P(PSTR("

"), - GetTextIndexed(action, sizeof(action), title_index, kButtonAction), - GetTextIndexed(confirm, sizeof(confirm), title_index, kButtonConfirm), - (!title_index) ? "rst" : "non", - GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); - } else { - WSContentSend_P(PSTR("

"), - GetTextIndexed(action, sizeof(action), title_index, kButtonAction), - GetTextIndexed(title, sizeof(title), title_index, kButtonTitle)); - } -} - -void WSContentSpaceButton(uint32_t title_index) -{ - WSContentSend_P(PSTR("
")); - WSContentButton(title_index); -} - -void WSContentEnd(void) -{ - WSContentFlush(); - _WSContentSend(""); - WebServer->client().stop(); -} - -void WSContentStop(void) -{ - if (WifiIsInManagerMode()) { - if (WifiConfigCounter()) { - WSContentSend_P(HTTP_COUNTER); - } - } - WSContentSend_P(HTTP_END, my_version); - WSContentEnd(); -} - - - -void WebRestart(uint32_t type) -{ - - - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTART); - - bool reset_only = (HTTP_MANAGER_RESET_ONLY == Web.state); - - WSContentStart_P((type) ? S_SAVE_CONFIGURATION : S_RESTART, !reset_only); - WSContentSend_P(HTTP_SCRIPT_RELOAD); - WSContentSendStyle(); - if (type) { - WSContentSend_P(PSTR("
" D_CONFIGURATION_SAVED "
")); - if (2 == type) { - WSContentSend_P(PSTR("
" D_TRYING_TO_CONNECT "
")); - } - WSContentSend_P(PSTR("
")); - } - WSContentSend_P(HTTP_MSG_RSTRT); - if (HTTP_MANAGER == Web.state || reset_only) { - Web.state = HTTP_ADMIN; - } else { - WSContentSpaceButton(BUTTON_MAIN); - } - WSContentStop(); - - ShowWebSource(SRC_WEBGUI); - restart_flag = 2; -} - - - -void HandleWifiLogin(void) -{ - WSContentStart_P(S_CONFIGURE_WIFI, false); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_LOGIN); - - if (HTTP_MANAGER_RESET_ONLY == Web.state) { - WSContentSpaceButton(BUTTON_RESTART); -#ifndef FIRMWARE_MINIMAL - WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); -#endif - } - - WSContentStop(); -} - -void WebSliderColdWarm(void) -{ - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, - "a", - "#fff", "#ff0", - 1, - 153, 500, - LightGetColorTemp(), - 't', 0); -} - -void HandleRoot(void) -{ - if (CaptivePortal()) { return; } - - if (WebServer->hasArg("rst")) { - WebRestart(0); - return; - } - - if (WifiIsInManagerMode()) { -#ifndef FIRMWARE_MINIMAL - if (strlen(SettingsText(SET_WEBPWD)) && !(WebServer->hasArg("USER1")) && !(WebServer->hasArg("PASS1")) && HTTP_MANAGER_RESET_ONLY != Web.state) { - HandleWifiLogin(); - } else { - if (!strlen(SettingsText(SET_WEBPWD)) || (((WebServer->arg("USER1") == WEB_USERNAME ) && (WebServer->arg("PASS1") == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) { - HandleWifiConfiguration(); - } else { - - HandleWifiLogin(); - } - } -#endif - return; - } - - if (HandleRootStatusRefresh()) { - return; - } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_MAIN_MENU); - - char stemp[33]; - - WSContentStart_P(S_MAIN_MENU); -#ifdef USE_SCRIPT_WEB_DISPLAY - WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh, Settings.web_refresh); -#else - WSContentSend_P(HTTP_SCRIPT_ROOT, Settings.web_refresh); -#endif - WSContentSend_P(HTTP_SCRIPT_ROOT_PART2); - - WSContentSendStyle(); - - WSContentSend_P(PSTR("
")); - if (devices_present) { -#ifdef USE_LIGHT - if (light_type) { - uint8_t light_subtype = light_type &7; - if (!Settings.flag3.pwm_multi_channels) { - bool split_white = ((LST_RGBW <= light_subtype) && (devices_present > 1)); - - if ((LST_COLDWARM == light_subtype) || ((LST_RGBCW == light_subtype) && !split_white)) { - WebSliderColdWarm(); - } - - if (light_subtype > 2) { - uint16_t hue; - uint8_t sat; - LightGetHSB(&hue, &sat, nullptr); - - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, - "b", - "#800", "#f00 5%,#ff0 20%,#0f0 35%,#0ff 50%,#00f 65%,#f0f 80%,#f00 95%,#800", - 2, - 1, 359, - hue, - 'h', 0); - - uint8_t dcolor = changeUIntScale(Settings.light_dimmer, 0, 100, 0, 255); - char scolor[8]; - snprintf_P(scolor, sizeof(scolor), PSTR("#%02X%02X%02X"), dcolor, dcolor, dcolor); - uint8_t red, green, blue; - LightHsToRgb(hue, 255, &red, &green, &blue); - snprintf_P(stemp, sizeof(stemp), PSTR("#%02X%02X%02X"), red, green, blue); - - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, - "s", - scolor, stemp, - 3, - 0, 100, - changeUIntScale(sat, 0, 255, 0, 100), - 'n', 0); - } - - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, - "c", - "#000", "#fff", - 4, - Settings.flag3.slider_dimmer_stay_on, 100, - Settings.light_dimmer, - 'd', 0); - - if (split_white) { - if (LST_RGBCW == light_subtype) { - WebSliderColdWarm(); - } - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, - "f", - "#000", "#fff", - 5, - Settings.flag3.slider_dimmer_stay_on, 100, - LightGetDimmer(2), - 'w', 0); - } - } else { - uint32_t pwm_channels = light_subtype > LST_MAX ? LST_MAX : light_subtype; - stemp[0] = 'e'; stemp[1] = '0'; stemp[2] = '\0'; - for (uint32_t i = 0; i < pwm_channels; i++) { - stemp[1]++; - - WSContentSend_P(HTTP_MSG_SLIDER_GRADIENT, - stemp, - "#000", "#fff", - i+1, - 1, 100, - changeUIntScale(Settings.light_color[i], 0, 255, 0, 100), - 'e', i+1); - } - } - } -#endif -#ifdef USE_SHUTTER - if (Settings.flag3.shutter_mode) { - for (uint32_t i = 0; i < shutters_present; i++) { - WSContentSend_P(HTTP_MSG_SLIDER_SHUTTER, Settings.shutter_position[i], i+1); - } - } -#endif - WSContentSend_P(HTTP_TABLE100); - WSContentSend_P(PSTR("
")); -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { - WSContentSend_P(HTTP_DEVICE_CONTROL, 36, 1, - (strlen(SettingsText(SET_BUTTON1))) ? SettingsText(SET_BUTTON1) : D_BUTTON_TOGGLE, - ""); - for (uint32_t i = 0; i < MaxFanspeed(); i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i); - WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i +2, - (strlen(SettingsText(SET_BUTTON2 + i))) ? SettingsText(SET_BUTTON2 + i) : stemp, - ""); - } - } else { -#endif - for (uint32_t idx = 1; idx <= devices_present; idx++) { - bool set_button = ((idx <= MAX_BUTTON_TEXT) && strlen(SettingsText(SET_BUTTON1 + idx -1))); -#ifdef USE_SHUTTER - int32_t ShutterWebButton; - if (ShutterWebButton = IsShutterWebButton(idx)) { - WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, - (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : ((Settings.shutter_options[abs(ShutterWebButton)-1] & 2) ? "-" : ((ShutterWebButton>0) ? "â–²" : "â–¼")), - ""); - continue; - } -#endif - snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx); - WSContentSend_P(HTTP_DEVICE_CONTROL, 100 / devices_present, idx, - (set_button) ? SettingsText(SET_BUTTON1 + idx -1) : (devices_present < 5) ? D_BUTTON_TOGGLE : "", - (set_button) ? "" : (devices_present > 1) ? stemp : ""); - } -#ifdef USE_SONOFF_IFAN - } -#endif - WSContentSend_P(PSTR("
%s
")); - } -#ifdef USE_SONOFF_RF - if (SONOFF_BRIDGE == my_module_type) { - WSContentSend_P(HTTP_TABLE100); - WSContentSend_P(PSTR("")); - uint32_t idx = 0; - for (uint32_t i = 0; i < 4; i++) { - if (idx > 0) { WSContentSend_P(PSTR("")); } - for (uint32_t j = 0; j < 4; j++) { - idx++; - snprintf_P(stemp, sizeof(stemp), PSTR("%d"), idx); - WSContentSend_P(PSTR(""), idx, - (strlen(SettingsText(SET_BUTTON1 + idx -1))) ? SettingsText(SET_BUTTON1 + idx -1) : stemp); - } - } - WSContentSend_P(PSTR("")); - } -#endif - -#ifndef FIRMWARE_MINIMAL - XdrvCall(FUNC_WEB_ADD_MAIN_BUTTON); - XsnsCall(FUNC_WEB_ADD_MAIN_BUTTON); -#endif - - if (HTTP_ADMIN == Web.state) { -#ifdef FIRMWARE_MINIMAL - WSContentSpaceButton(BUTTON_FIRMWARE_UPGRADE); -#else - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentButton(BUTTON_INFORMATION); - WSContentButton(BUTTON_FIRMWARE_UPGRADE); -#endif - WSContentButton(BUTTON_CONSOLE); - WSContentButton(BUTTON_RESTART); - } - WSContentStop(); -} - -bool HandleRootStatusRefresh(void) -{ - if (!WebAuthenticate()) { - WebServer->requestAuthentication(); - return true; - } - - if (!WebServer->hasArg("m")) { - return false; - } - - #ifdef USE_SCRIPT_WEB_DISPLAY - Script_Check_HTML_Setvars(); - #endif - - char tmp[8]; - char svalue[32]; - char webindex[5]; - - WebGetArg("o", tmp, sizeof(tmp)); - if (strlen(tmp)) { - ShowWebSource(SRC_WEBGUI); - uint32_t device = atoi(tmp); -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { - if (device < 2) { - ExecuteCommandPower(1, POWER_TOGGLE, SRC_IGNORE); - } else { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), device -2); - ExecuteCommand(svalue, SRC_WEBGUI); - } - } else { -#endif -#ifdef USE_SHUTTER - int32_t ShutterWebButton; - if (ShutterWebButton = IsShutterWebButton(device)) { - snprintf_P(svalue, sizeof(svalue), PSTR("ShutterPosition%d %s"), abs(ShutterWebButton), (ShutterWebButton>0) ? PSTR(D_CMND_SHUTTER_TOGGLEUP) : PSTR(D_CMND_SHUTTER_TOGGLEDOWN)); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } else { -#endif - ExecuteCommandPower(device, POWER_TOGGLE, SRC_IGNORE); -#ifdef USE_SHUTTER - } -#endif -#ifdef USE_SONOFF_IFAN - } -#endif - } - WebGetArg("d0", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - WebGetArg("w0", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_WHITE " %s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); - for (uint32_t j = 1; j <= pwm_channels; j++) { - snprintf_P(webindex, sizeof(webindex), PSTR("e%d"), j); - WebGetArg(webindex, tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_CHANNEL "%d %s"), j, tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - } - WebGetArg("t0", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - WebGetArg("h0", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "1 %s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - WebGetArg("n0", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_HSBCOLOR "2 %s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } -#ifdef USE_SHUTTER - for (uint32_t j = 1; j <= shutters_present; j++) { - snprintf_P(webindex, sizeof(webindex), PSTR("u%d"), j); - WebGetArg(webindex, tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR("ShutterPosition%d %s"), j, tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - } -#endif -#ifdef USE_SONOFF_RF - WebGetArg("k", tmp, sizeof(tmp)); - if (strlen(tmp)) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_RFKEY "%s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } -#endif - WSContentBegin(200, CT_HTML); - WSContentSend_P(PSTR("{t}")); - XsnsCall(FUNC_WEB_SENSOR); -#ifdef USE_SCRIPT_WEB_DISPLAY - XdrvCall(FUNC_WEB_SENSOR); -#endif - - WSContentSend_P(PSTR("")); - - if (devices_present) { - WSContentSend_P(PSTR("{t}")); - uint32_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32; -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { - WSContentSend_P(HTTP_DEVICE_STATE, 36, (bitRead(power, 0)) ? "bold" : "normal", 54, GetStateText(bitRead(power, 0))); - uint32_t fanspeed = GetFanspeed(); - snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fanspeed); - WSContentSend_P(HTTP_DEVICE_STATE, 64, (fanspeed) ? "bold" : "normal", 54, (fanspeed) ? svalue : GetStateText(0)); - } else { -#endif - for (uint32_t idx = 1; idx <= devices_present; idx++) { - snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(power, idx -1)); - WSContentSend_P(HTTP_DEVICE_STATE, 100 / devices_present, (bitRead(power, idx -1)) ? "bold" : "normal", fsize, (devices_present < 5) ? GetStateText(bitRead(power, idx -1)) : svalue); - } -#ifdef USE_SONOFF_IFAN - } -#endif - WSContentSend_P(PSTR("")); - } - WSContentEnd(); - - return true; -} - -#ifdef USE_SHUTTER -int32_t IsShutterWebButton(uint32_t idx) { - - int32_t ShutterWebButton = 0; - if (Settings.flag3.shutter_mode) { - for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { - if (Settings.shutter_startrelay[i] && ((Settings.shutter_startrelay[i] == idx) || (Settings.shutter_startrelay[i] == (idx-1)))) { - ShutterWebButton = (Settings.shutter_startrelay[i] == idx) ? (i+1): (-1-i); - break; - } - } - } - return ShutterWebButton; -} -#endif - - - -#ifndef FIRMWARE_MINIMAL - -void HandleConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURATION); - - WSContentStart_P(S_CONFIGURATION); - WSContentSendStyle(); - - WSContentButton(BUTTON_MODULE); - WSContentButton(BUTTON_WIFI); - - XdrvCall(FUNC_WEB_ADD_BUTTON); - XsnsCall(FUNC_WEB_ADD_BUTTON); - - WSContentButton(BUTTON_LOGGING); - WSContentButton(BUTTON_OTHER); - WSContentButton(BUTTON_TEMPLATE); - - WSContentSpaceButton(BUTTON_RESET_CONFIGURATION); - WSContentButton(BUTTON_BACKUP); - WSContentButton(BUTTON_RESTORE); - - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); -} - - - -void HandleTemplateConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - if (WebServer->hasArg("save")) { - TemplateSaveSettings(); - WebRestart(1); - return; - } - - char stemp[30]; - - if (WebServer->hasArg("m")) { - WSContentBegin(200, CT_PLAIN); - for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) { - uint32_t midx = pgm_read_byte(kModuleNiceList + i); - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), midx +1); - } - WSContentEnd(); - return; - } - - WebGetArg("t", stemp, sizeof(stemp)); - if (strlen(stemp)) { - uint32_t module = atoi(stemp); - uint32_t module_save = Settings.module; - Settings.module = module; - myio cmodule; - ModuleGpios(&cmodule); - gpio_flag flag = ModuleFlag(); - Settings.module = module_save; - - WSContentBegin(200, CT_PLAIN); - WSContentSend_P(PSTR("%s}1"), AnyModuleName(module).c_str()); - for (uint32_t i = 0; i < sizeof(kGpioNiceList); i++) { - if (1 == i) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, 255, D_SENSOR_USER, 255); - } - uint32_t midx = pgm_read_byte(kGpioNiceList + i); - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); - } - WSContentSend_P(PSTR("}1")); - - for (uint32_t i = 0; i < ADC0_END; i++) { - if (1 == i) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, ADC0_USER, D_SENSOR_USER, ADC0_USER); - } - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, i, GetTextIndexed(stemp, sizeof(stemp), i, kAdc0Names), i); - } - WSContentSend_P(PSTR("}1")); - - for (uint32_t i = 0; i < sizeof(cmodule); i++) { - if ((i < 6) || ((i > 8) && (i != 11))) { - WSContentSend_P(PSTR("%s%d"), (i>0)?",":"", cmodule.io[i]); - } - } - WSContentSend_P(PSTR("}1%d}1%d"), flag, Settings.user_template_base); - WSContentEnd(); - return; - } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TEMPLATE); - - WSContentStart_P(S_CONFIGURE_TEMPLATE); - WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); - WSContentSend_P(HTTP_SCRIPT_TEMPLATE); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_TEMPLATE); - WSContentSend_P(HTTP_TABLE100); - WSContentSend_P(PSTR("" D_TEMPLATE_NAME "" - "" D_BASE_TYPE "" - "" - "
")); - WSContentSend_P(HTTP_TABLE100); - for (uint32_t i = 0; i < 17; i++) { - if ((i < 6) || ((i > 8) && (i != 11))) { - WSContentSend_P(PSTR("" D_GPIO "%d"), - ((9==i)||(10==i)) ? WebColor(COL_TEXT_WARNING) : WebColor(COL_TEXT), i, (0==i) ? " style='width:200px'" : "", i); - } - } - WSContentSend_P(PSTR("" D_ADC "0"), WebColor(COL_TEXT)); - WSContentSend_P(PSTR("")); - gpio_flag flag = ModuleFlag(); - if (flag.data > ADC0_USER) { - WSContentSend_P(HTTP_FORM_TEMPLATE_FLAG); - } - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void TemplateSaveSettings(void) -{ - char tmp[sizeof(Settings.user_template.name)]; - char webindex[5]; - char svalue[128]; - - WebGetArg("s1", tmp, sizeof(tmp)); - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " {\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), tmp); - - uint32_t j = 0; - for (uint32_t i = 0; i < sizeof(Settings.user_template.gp); i++) { - if (6 == i) { j = 9; } - if (8 == i) { j = 12; } - snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), j); - WebGetArg(webindex, tmp, sizeof(tmp)); - uint8_t gpio = atoi(tmp); - snprintf_P(svalue, sizeof(svalue), PSTR("%s%s%d"), svalue, (i>0)?",":"", gpio); - j++; - } - - WebGetArg("g17", tmp, sizeof(tmp)); - uint32_t flag = atoi(tmp); - for (uint32_t i = 0; i < GPIO_FLAG_USED; i++) { - snprintf_P(webindex, sizeof(webindex), PSTR("c%d"), i); - uint32_t state = WebServer->hasArg(webindex) << i +4; - flag += state; - } - WebGetArg("g99", tmp, sizeof(tmp)); - uint32_t base = atoi(tmp) +1; - - snprintf_P(svalue, sizeof(svalue), PSTR("%s],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), svalue, flag, base); - ExecuteWebCommand(svalue, SRC_WEBGUI); -} - - - -void HandleModuleConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - if (WebServer->hasArg("save")) { - ModuleSaveSettings(); - WebRestart(1); - return; - } - - char stemp[30]; - uint32_t midx; - myio cmodule; - ModuleGpios(&cmodule); - - if (WebServer->hasArg("m")) { - WSContentBegin(200, CT_PLAIN); - uint32_t vidx = 0; - for (uint32_t i = 0; i <= sizeof(kModuleNiceList); i++) { - if (0 == i) { - midx = USER_MODULE; - vidx = 0; - } else { - midx = pgm_read_byte(kModuleNiceList + i -1); - vidx = midx +1; - } - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, AnyModuleName(midx).c_str(), vidx); - } - WSContentEnd(); - return; - } - - if (WebServer->hasArg("g")) { - WSContentBegin(200, CT_PLAIN); - for (uint32_t j = 0; j < sizeof(kGpioNiceList); j++) { - midx = pgm_read_byte(kGpioNiceList + j); - if (!GetUsedInModule(midx, cmodule.io)) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, midx, GetTextIndexed(stemp, sizeof(stemp), midx, kSensorNames), midx); - } - } - WSContentEnd(); - return; - } - -#ifndef USE_ADC_VCC - if (WebServer->hasArg("a")) { - WSContentBegin(200, CT_PLAIN); - for (uint32_t j = 0; j < ADC0_END; j++) { - WSContentSend_P(HTTP_MODULE_TEMPLATE_REPLACE, j, GetTextIndexed(stemp, sizeof(stemp), j, kAdc0Names), j); - } - WSContentEnd(); - return; - } -#endif - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MODULE); - - WSContentStart_P(S_CONFIGURE_MODULE); - WSContentSend_P(HTTP_SCRIPT_MODULE_TEMPLATE); - WSContentSend_P(HTTP_SCRIPT_MODULE1, Settings.module); - for (uint32_t i = 0; i < sizeof(cmodule); i++) { - if (ValidGPIO(i, cmodule.io[i])) { - WSContentSend_P(PSTR("sk(%d,%d);"), my_module.io[i], i); - } - } - WSContentSend_P(HTTP_SCRIPT_MODULE2, Settings.my_adc0); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_MODULE, AnyModuleName(MODULE).c_str()); - for (uint32_t i = 0; i < sizeof(cmodule); i++) { - if (ValidGPIO(i, cmodule.io[i])) { - snprintf_P(stemp, 3, PINS_WEMOS +i*2); - char sesp8285[40]; - snprintf_P(sesp8285, sizeof(sesp8285), PSTR("ESP8285"), WebColor(COL_TEXT_WARNING)); - WSContentSend_P(PSTR("%s " D_GPIO "%d %s"), - (WEMOS==my_module_type)?stemp:"", i, (0==i)? D_SENSOR_BUTTON "1":(1==i)? D_SERIAL_OUT :(3==i)? D_SERIAL_IN :((9==i)||(10==i))? sesp8285 :(12==i)? D_SENSOR_RELAY "1":(13==i)? D_SENSOR_LED "1i":(14==i)? D_SENSOR :"", i); - } - } -#ifndef USE_ADC_VCC - if (ValidAdc()) { - WSContentSend_P(PSTR("%s " D_ADC "0"), (WEMOS==my_module_type)?"A0":""); - } -#endif - WSContentSend_P(PSTR("")); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void ModuleSaveSettings(void) -{ - char tmp[8]; - char webindex[5]; - - WebGetArg("g99", tmp, sizeof(tmp)); - uint32_t new_module = (!strlen(tmp)) ? MODULE : atoi(tmp); - Settings.last_module = Settings.module; - Settings.module = new_module; - SetModuleType(); - myio cmodule; - ModuleGpios(&cmodule); - String gpios = ""; - for (uint32_t i = 0; i < sizeof(cmodule); i++) { - if (Settings.last_module != new_module) { - Settings.my_gp.io[i] = GPIO_NONE; - } else { - if (ValidGPIO(i, cmodule.io[i])) { - snprintf_P(webindex, sizeof(webindex), PSTR("g%d"), i); - WebGetArg(webindex, tmp, sizeof(tmp)); - Settings.my_gp.io[i] = (!strlen(tmp)) ? 0 : atoi(tmp); - gpios += F(", " D_GPIO ); gpios += String(i); gpios += F(" "); gpios += String(Settings.my_gp.io[i]); - } - } - } -#ifndef USE_ADC_VCC - WebGetArg("g17", tmp, sizeof(tmp)); - Settings.my_adc0 = (!strlen(tmp)) ? 0 : atoi(tmp); - gpios += F(", " D_ADC "0 "); gpios += String(Settings.my_adc0); -#endif - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MODULE "%s " D_CMND_MODULE "%s"), ModuleName().c_str(), gpios.c_str()); -} - - - -const char kUnescapeCode[] = "&><\"\'"; -const char kEscapeCode[] PROGMEM = "&|>|<|"|'"; - -String HtmlEscape(const String unescaped) { - char escaped[10]; - size_t ulen = unescaped.length(); - String result = ""; - for (size_t i = 0; i < ulen; i++) { - char c = unescaped[i]; - char *p = strchr(kUnescapeCode, c); - if (p != nullptr) { - result += GetTextIndexed(escaped, sizeof(escaped), p - kUnescapeCode, kEscapeCode); - } else { - result += c; - } - } - return result; -} - - -const char kEncryptionType[] PROGMEM = "|||" D_WPA_PSK "||" D_WPA2_PSK "|" D_WEP "||" D_NONE "|" D_AUTO; - -void HandleWifiConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_WIFI); - - if (WebServer->hasArg("save") && HTTP_MANAGER_RESET_ONLY != Web.state) { - WifiSaveSettings(); - WebRestart(2); - return; - } - - WSContentStart_P(S_CONFIGURE_WIFI, !WifiIsInManagerMode()); - WSContentSend_P(HTTP_SCRIPT_WIFI); - WSContentSendStyle(); - - if (HTTP_MANAGER_RESET_ONLY != Web.state) { - if (WebServer->hasArg("scan")) { -#ifdef USE_EMULATION - UdpDisconnect(); -#endif - int n = WiFi.scanNetworks(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_SCAN_DONE)); - - if (0 == n) { - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_WIFI, S_NO_NETWORKS_FOUND); - WSContentSend_P(S_NO_NETWORKS_FOUND); - WSContentSend_P(PSTR(". " D_REFRESH_TO_SCAN_AGAIN ".")); - } else { - - int indices[n]; - for (uint32_t i = 0; i < n; i++) { - indices[i] = i; - } - - - for (uint32_t i = 0; i < n; i++) { - for (uint32_t j = i + 1; j < n; j++) { - if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) { - std::swap(indices[i], indices[j]); - } - } - } - - - String cssid; - for (uint32_t i = 0; i < n; i++) { - if (-1 == indices[i]) { continue; } - cssid = WiFi.SSID(indices[i]); - uint32_t cschn = WiFi.channel(indices[i]); - for (uint32_t j = i + 1; j < n; j++) { - if ((cssid == WiFi.SSID(indices[j])) && (cschn == WiFi.channel(indices[j]))) { - DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_DUPLICATE_ACCESSPOINT " %s"), WiFi.SSID(indices[j]).c_str()); - indices[j] = -1; - } - } - } - - - for (uint32_t i = 0; i < n; i++) { - if (-1 == indices[i]) { continue; } - DEBUG_CORE_LOG(PSTR(D_LOG_WIFI D_SSID " %s, " D_BSSID " %s, " D_CHANNEL " %d, " D_RSSI " %d"), - WiFi.SSID(indices[i]).c_str(), WiFi.BSSIDstr(indices[i]).c_str(), WiFi.channel(indices[i]), WiFi.RSSI(indices[i])); - int quality = WifiGetRssiAsQuality(WiFi.RSSI(indices[i])); - int auth = WiFi.encryptionType(indices[i]); - char encryption[20]; - WSContentSend_P(PSTR("
%s (%d) %s %d%% (%d dBm)
"), - HtmlEscape(WiFi.SSID(indices[i])).c_str(), - WiFi.channel(indices[i]), - GetTextIndexed(encryption, sizeof(encryption), auth +1, kEncryptionType), - quality, WiFi.RSSI(indices[i]) - ); - delay(0); - - } - WSContentSend_P(PSTR("
")); - } - } else { - WSContentSend_P(PSTR("
")); - } - - - WSContentSend_P(HTTP_FORM_WIFI, SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), WIFI_HOSTNAME, WIFI_HOSTNAME, SettingsText(SET_HOSTNAME), SettingsText(SET_CORS)); - WSContentSend_P(HTTP_FORM_END); - } - - if (WifiIsInManagerMode()) { -#ifndef FIRMWARE_MINIMAL - WSContentSpaceButton(BUTTON_RESTORE); - WSContentButton(BUTTON_RESET_CONFIGURATION); -#endif - WSContentSpaceButton(BUTTON_RESTART); - } else { - WSContentSpaceButton(BUTTON_CONFIGURATION); - } - WSContentStop(); -} - -void WifiSaveSettings(void) -{ - char tmp[TOPSZ]; - - WebGetArg("h", tmp, sizeof(tmp)); - SettingsUpdateText(SET_HOSTNAME, (!strlen(tmp)) ? WIFI_HOSTNAME : tmp); - if (strstr(SettingsText(SET_HOSTNAME), "%") != nullptr) { - SettingsUpdateText(SET_HOSTNAME, WIFI_HOSTNAME); - } - WebGetArg("c", tmp, sizeof(tmp)); - SettingsUpdateText(SET_CORS, (!strlen(tmp)) ? CORS_DOMAIN : tmp); - WebGetArg("s1", tmp, sizeof(tmp)); - SettingsUpdateText(SET_STASSID1, (!strlen(tmp)) ? STA_SSID1 : tmp); - WebGetArg("s2", tmp, sizeof(tmp)); - SettingsUpdateText(SET_STASSID2, (!strlen(tmp)) ? STA_SSID2 : tmp); - WebGetArg("p1", tmp, sizeof(tmp)); - SettingsUpdateText(SET_STAPWD1, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD1) : tmp); - WebGetArg("p2", tmp, sizeof(tmp)); - SettingsUpdateText(SET_STAPWD2, (!strlen(tmp)) ? "" : (strlen(tmp) < 5) ? SettingsText(SET_STAPWD2) : tmp); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_HOSTNAME " %s, " D_CMND_SSID "1 %s, " D_CMND_SSID "2 %s, " D_CMND_CORS " %s"), - SettingsText(SET_HOSTNAME), SettingsText(SET_STASSID1), SettingsText(SET_STASSID2), SettingsText(SET_CORS)); -} - - - -void HandleLoggingConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_LOGGING); - - if (WebServer->hasArg("save")) { - LoggingSaveSettings(); - HandleConfiguration(); - return; - } - - WSContentStart_P(S_CONFIGURE_LOGGING); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_LOG1); - char stemp1[45]; - char stemp2[32]; - uint8_t dlevel[4] = { LOG_LEVEL_INFO, LOG_LEVEL_INFO, LOG_LEVEL_NONE, LOG_LEVEL_NONE }; - for (uint32_t idx = 0; idx < 4; idx++) { - if ((2==idx) && !Settings.flag.mqtt_enabled) { continue; } - uint32_t llevel = (0==idx)?Settings.seriallog_level:(1==idx)?Settings.weblog_level:(2==idx)?Settings.mqttlog_level:Settings.syslog_level; - WSContentSend_P(PSTR("

%s (%s)

")); - } - WSContentSend_P(HTTP_FORM_LOG2, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void LoggingSaveSettings(void) -{ - char tmp[TOPSZ]; - - WebGetArg("l0", tmp, sizeof(tmp)); - SetSeriallog((!strlen(tmp)) ? SERIAL_LOG_LEVEL : atoi(tmp)); - WebGetArg("l1", tmp, sizeof(tmp)); - Settings.weblog_level = (!strlen(tmp)) ? WEB_LOG_LEVEL : atoi(tmp); - WebGetArg("l2", tmp, sizeof(tmp)); - Settings.mqttlog_level = (!strlen(tmp)) ? MQTT_LOG_LEVEL : atoi(tmp); - WebGetArg("l3", tmp, sizeof(tmp)); - SetSyslog((!strlen(tmp)) ? SYS_LOG_LEVEL : atoi(tmp)); - WebGetArg("lh", tmp, sizeof(tmp)); - SettingsUpdateText(SET_SYSLOG_HOST, (!strlen(tmp)) ? SYS_LOG_HOST : tmp); - WebGetArg("lp", tmp, sizeof(tmp)); - Settings.syslog_port = (!strlen(tmp)) ? SYS_LOG_PORT : atoi(tmp); - WebGetArg("lt", tmp, sizeof(tmp)); - Settings.tele_period = (!strlen(tmp)) ? TELE_PERIOD : atoi(tmp); - if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) { - Settings.tele_period = 10; - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_LOG D_CMND_SERIALLOG " %d, " D_CMND_WEBLOG " %d, " D_CMND_MQTTLOG " %d, " D_CMND_SYSLOG " %d, " D_CMND_LOGHOST " %s, " D_CMND_LOGPORT " %d, " D_CMND_TELEPERIOD " %d"), - Settings.seriallog_level, Settings.weblog_level, Settings.mqttlog_level, Settings.syslog_level, SettingsText(SET_SYSLOG_HOST), Settings.syslog_port, Settings.tele_period); -} - - - -void HandleOtherConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_OTHER); - - if (WebServer->hasArg("save")) { - OtherSaveSettings(); - WebRestart(1); - return; - } - - WSContentStart_P(S_CONFIGURE_OTHER); - WSContentSendStyle(); - - TemplateJson(); - char stemp[strlen(mqtt_data) +1]; - strlcpy(stemp, mqtt_data, sizeof(stemp)); - WSContentSend_P(HTTP_FORM_OTHER, stemp, (USER_MODULE == Settings.module) ? " checked disabled" : "", (Settings.flag.mqtt_enabled) ? " checked" : ""); - - uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present; -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { maxfn = 1; } -#endif - for (uint32_t i = 0; i < maxfn; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i +1); - WSContentSend_P(PSTR("" D_FRIENDLY_NAME " %d (" FRIENDLY_NAME "%s)

"), - i +1, - (i) ? stemp : "", - i, - (i) ? stemp : "", - SettingsText(SET_FRIENDLYNAME1 + i)); - } - -#ifdef USE_EMULATION - WSContentSend_P(PSTR("

 " D_EMULATION " 

")); - for (uint32_t i = 0; i < EMUL_MAX; i++) { -#ifndef USE_EMULATION_WEMO - if (i == EMUL_WEMO) { i++; } -#endif -#ifndef USE_EMULATION_HUE - if (i == EMUL_HUE) { i++; } -#endif - if (i < EMUL_MAX) { - WSContentSend_P(PSTR("%s %s
"), - i, i, - (i == Settings.flag2.emulation) ? " checked" : "", - GetTextIndexed(stemp, sizeof(stemp), i, kEmulationOptions), - (i == EMUL_NONE) ? "" : (i == EMUL_WEMO) ? D_SINGLE_DEVICE : D_MULTI_DEVICE); - } - } - WSContentSend_P(PSTR("

")); -#endif - - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void OtherSaveSettings(void) -{ - char tmp[TOPSZ]; - char webindex[5]; - char friendlyname[TOPSZ]; - char message[LOGSZ]; - - WebGetArg("wp", tmp, sizeof(tmp)); - SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp); - Settings.flag.mqtt_enabled = WebServer->hasArg("b1"); -#ifdef USE_EMULATION - WebGetArg("b2", tmp, sizeof(tmp)); - Settings.flag2.emulation = (!strlen(tmp)) ? 0 : atoi(tmp); -#endif - - snprintf_P(message, sizeof(message), PSTR(D_LOG_OTHER D_MQTT_ENABLE " %s, " D_CMND_EMULATION " %d, " D_CMND_FRIENDLYNAME), GetStateText(Settings.flag.mqtt_enabled), Settings.flag2.emulation); - for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) { - snprintf_P(webindex, sizeof(webindex), PSTR("a%d"), i); - WebGetArg(webindex, tmp, sizeof(tmp)); - snprintf_P(friendlyname, sizeof(friendlyname), PSTR(FRIENDLY_NAME"%d"), i +1); - SettingsUpdateText(SET_FRIENDLYNAME1 +i, (!strlen(tmp)) ? (i) ? friendlyname : FRIENDLY_NAME : tmp); - snprintf_P(message, sizeof(message), PSTR("%s%s %s"), message, (i) ? "," : "", SettingsText(SET_FRIENDLYNAME1 +i)); - } - AddLog_P(LOG_LEVEL_INFO, message); - - WebGetArg("t1", tmp, sizeof(tmp)); - if (strlen(tmp)) { - char svalue[128]; - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_TEMPLATE " %s"), tmp); - ExecuteWebCommand(svalue, SRC_WEBGUI); - - if (WebServer->hasArg("t2")) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_MODULE " 0")); - ExecuteWebCommand(svalue, SRC_WEBGUI); - } - - } -} - - - -void HandleBackupConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_BACKUP_CONFIGURATION)); - - if (!SettingsBufferAlloc()) { return; } - - WiFiClient myClient = WebServer->client(); - WebServer->setContentLength(sizeof(Settings)); - - char attachment[TOPSZ]; - - - - - char hostname[sizeof(my_hostname)]; - snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=Config_%s_%s.dmp"), NoAlNumToUnderscore(hostname, my_hostname), my_version); - - WebServer->sendHeader(F("Content-Disposition"), attachment); - - WSSend(200, CT_STREAM, ""); - - uint32_t cfg_crc32 = Settings.cfg_crc32; - Settings.cfg_crc32 = GetSettingsCrc32(); - - memcpy(settings_buffer, &Settings, sizeof(Settings)); - if (Web.config_xor_on_set) { - for (uint32_t i = 2; i < sizeof(Settings); i++) { - settings_buffer[i] ^= (Web.config_xor_on_set +i); - } - } - -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - size_t written = myClient.write((const char*)settings_buffer, sizeof(Settings)); - if (written < sizeof(Settings)) { - myClient.write((const char*)settings_buffer +written, sizeof(Settings) -written); - } -#else - myClient.write((const char*)settings_buffer, sizeof(Settings)); -#endif - - SettingsBufferFree(); - - Settings.cfg_crc32 = cfg_crc32; -} - - - -void HandleResetConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess(!WifiIsInManagerMode())) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESET_CONFIGURATION); - - WSContentStart_P(S_RESET_CONFIGURATION, !WifiIsInManagerMode()); - WSContentSendStyle(); - WSContentSend_P(PSTR("
" D_CONFIGURATION_RESET "
")); - WSContentSend_P(HTTP_MSG_RSTRT); - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); - - char command[CMDSZ]; - snprintf_P(command, sizeof(command), PSTR(D_CMND_RESET " 1")); - ExecuteWebCommand(command, SRC_WEBGUI); -} - -void HandleRestoreConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTORE_CONFIGURATION); - - WSContentStart_P(S_RESTORE_CONFIGURATION); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_RST); - WSContentSend_P(HTTP_FORM_RST_UPG, D_RESTORE); - if (WifiIsInManagerMode()) { - WSContentSpaceButton(BUTTON_MAIN); - } else { - WSContentSpaceButton(BUTTON_CONFIGURATION); - } - WSContentStop(); - - Web.upload_error = 0; - Web.upload_file_type = UPL_SETTINGS; -} - - - -void HandleInformation(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_INFORMATION); - - char stopic[TOPSZ]; - - int freeMem = ESP.getFreeHeap(); - - WSContentStart_P(S_INFORMATION); - - - - WSContentSend_P(HTTP_SCRIPT_INFO_BEGIN); - WSContentSend_P(PSTR("
")); - WSContentSend_P(PSTR(D_PROGRAM_VERSION "}2%s%s"), my_version, my_image); - WSContentSend_P(PSTR("}1" D_BUILD_DATE_AND_TIME "}2%s"), GetBuildDateAndTime().c_str()); - WSContentSend_P(PSTR("}1" D_CORE_AND_SDK_VERSION "}2" ARDUINO_ESP8266_RELEASE "/%s"), ESP.getSdkVersion()); - WSContentSend_P(PSTR("}1" D_UPTIME "}2%s"), GetUptime().c_str()); - WSContentSend_P(PSTR("}1" D_FLASH_WRITE_COUNT "}2%d at 0x%X"), Settings.save_flag, GetSettingsAddress()); - WSContentSend_P(PSTR("}1" D_BOOT_COUNT "}2%d"), Settings.bootcount); - WSContentSend_P(PSTR("}1" D_RESTART_REASON "}2%s"), GetResetReason().c_str()); - uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present; -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { maxfn = 1; } -#endif - for (uint32_t i = 0; i < maxfn; i++) { - WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, SettingsText(SET_FRIENDLYNAME1 +i)); - } - WSContentSend_P(PSTR("}1}2 ")); - WSContentSend_P(PSTR("}1" D_AP "%d " D_SSID " (" D_RSSI ")}2%s (%d%%, %d dBm)"), Settings.sta_active +1, SettingsText(SET_STASSID1 + Settings.sta_active), WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI()); - WSContentSend_P(PSTR("}1" D_HOSTNAME "}2%s%s"), my_hostname, (Wifi.mdns_begun) ? ".local" : ""); -#if LWIP_IPV6 - String ipv6_addr = WifiGetIPv6(); - if(ipv6_addr != ""){ - WSContentSend_P(PSTR("}1 IPv6 Address }2%s"), ipv6_addr.c_str()); - } -#endif - if (static_cast(WiFi.localIP()) != 0) { - WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.localIP().toString().c_str()); - WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), IPAddress(Settings.ip_address[1]).toString().c_str()); - WSContentSend_P(PSTR("}1" D_SUBNET_MASK "}2%s"), IPAddress(Settings.ip_address[2]).toString().c_str()); - WSContentSend_P(PSTR("}1" D_DNS_SERVER "}2%s"), IPAddress(Settings.ip_address[3]).toString().c_str()); - WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.macAddress().c_str()); - } - if (static_cast(WiFi.softAPIP()) != 0) { - WSContentSend_P(PSTR("}1" D_IP_ADDRESS "}2%s"), WiFi.softAPIP().toString().c_str()); - WSContentSend_P(PSTR("}1" D_GATEWAY "}2%s"), WiFi.softAPIP().toString().c_str()); - WSContentSend_P(PSTR("}1" D_MAC_ADDRESS "}2%s"), WiFi.softAPmacAddress().c_str()); - } - WSContentSend_P(PSTR("}1}2 ")); - if (Settings.flag.mqtt_enabled) { - WSContentSend_P(PSTR("}1" D_MQTT_HOST "}2%s"), SettingsText(SET_MQTT_HOST)); - WSContentSend_P(PSTR("}1" D_MQTT_PORT "}2%d"), Settings.mqtt_port); - WSContentSend_P(PSTR("}1" D_MQTT_USER "}2%s"), SettingsText(SET_MQTT_USER)); - WSContentSend_P(PSTR("}1" D_MQTT_CLIENT "}2%s"), mqtt_client); - WSContentSend_P(PSTR("}1" D_MQTT_TOPIC "}2%s"), SettingsText(SET_MQTT_TOPIC)); - - WSContentSend_P(PSTR("}1" D_MQTT_GROUP_TOPIC "}2%s"), GetGroupTopic_P(stopic, "")); - WSContentSend_P(PSTR("}1" D_MQTT_FULL_TOPIC "}2%s"), GetTopic_P(stopic, CMND, mqtt_topic, "")); - WSContentSend_P(PSTR("}1" D_MQTT " " D_FALLBACK_TOPIC "}2%s"), GetFallbackTopic_P(stopic, "")); - } else { - WSContentSend_P(PSTR("}1" D_MQTT "}2" D_DISABLED)); - } - WSContentSend_P(PSTR("}1}2 ")); - -#ifdef USE_EMULATION - WSContentSend_P(PSTR("}1" D_EMULATION "}2%s"), GetTextIndexed(stopic, sizeof(stopic), Settings.flag2.emulation, kEmulationOptions)); -#else - WSContentSend_P(PSTR("}1" D_EMULATION "}2" D_DISABLED)); -#endif - -#ifdef USE_DISCOVERY - WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2%s"), (Settings.flag3.mdns_enabled) ? D_ENABLED : D_DISABLED); - if (Settings.flag3.mdns_enabled) { -#ifdef WEBSERVER_ADVERTISE - WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_WEB_SERVER)); -#else - WSContentSend_P(PSTR("}1" D_MDNS_ADVERTISE "}2" D_DISABLED)); -#endif - } -#else - WSContentSend_P(PSTR("}1" D_MDNS_DISCOVERY "}2" D_DISABLED)); -#endif - - WSContentSend_P(PSTR("}1}2 ")); - WSContentSend_P(PSTR("}1" D_ESP_CHIP_ID "}2%d"), ESP.getChipId()); - WSContentSend_P(PSTR("}1" D_FLASH_CHIP_ID "}20x%06X"), ESP.getFlashChipId()); - WSContentSend_P(PSTR("}1" D_FLASH_CHIP_SIZE "}2%dkB"), ESP.getFlashChipRealSize() / 1024); - WSContentSend_P(PSTR("}1" D_PROGRAM_FLASH_SIZE "}2%dkB"), ESP.getFlashChipSize() / 1024); - WSContentSend_P(PSTR("}1" D_PROGRAM_SIZE "}2%dkB"), ESP.getSketchSize() / 1024); - WSContentSend_P(PSTR("}1" D_FREE_PROGRAM_SPACE "}2%dkB"), ESP.getFreeSketchSpace() / 1024); - WSContentSend_P(PSTR("}1" D_FREE_MEMORY "}2%dkB"), freeMem / 1024); - WSContentSend_P(PSTR("
")); - - WSContentSend_P(HTTP_SCRIPT_INFO_END); - WSContentSendStyle(); - - WSContentSend_P(PSTR("" - "
")); - - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); -} -#endif - - - -void HandleUpgradeFirmware(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_FIRMWARE_UPGRADE); - - WSContentStart_P(S_FIRMWARE_UPGRADE); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_UPG, SettingsText(SET_OTAURL)); - WSContentSend_P(HTTP_FORM_RST_UPG, D_UPGRADE); - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); - - Web.upload_error = 0; - Web.upload_file_type = UPL_TASMOTA; -} - -void HandleUpgradeFirmwareStart(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - char command[TOPSZ + 10]; - - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED)); - WifiConfigCounter(); - - char otaurl[TOPSZ]; - WebGetArg("o", otaurl, sizeof(otaurl)); - if (strlen(otaurl)) { - snprintf_P(command, sizeof(command), PSTR(D_CMND_OTAURL " %s"), otaurl); - ExecuteWebCommand(command, SRC_WEBGUI); - } - - WSContentStart_P(S_INFORMATION); - WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA); - WSContentSendStyle(); - WSContentSend_P(PSTR("
" D_UPGRADE_STARTED " ...
")); - WSContentSend_P(HTTP_MSG_RSTRT); - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); - - snprintf_P(command, sizeof(command), PSTR(D_CMND_UPGRADE " 1")); - ExecuteWebCommand(command, SRC_WEBGUI); -} - -void HandleUploadDone(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE)); - - char error[100]; - - WifiConfigCounter(); - restart_flag = 0; - MqttRetryCounter(0); - - WSContentStart_P(S_INFORMATION); - if (!Web.upload_error) { - WSContentSend_P(HTTP_SCRIPT_RELOAD_OTA); - } - WSContentSendStyle(); - WSContentSend_P(PSTR("
" D_UPLOAD " " D_FAILED "

"), WebColor(COL_TEXT_WARNING)); -#ifdef USE_RF_FLASH - if (Web.upload_error < 15) { -#else - if ((Web.upload_error < 10) || (14 == Web.upload_error)) { - if (14 == Web.upload_error) { Web.upload_error = 10; } -#endif - GetTextIndexed(error, sizeof(error), Web.upload_error -1, kUploadErrors); - } else { - snprintf_P(error, sizeof(error), PSTR(D_UPLOAD_ERROR_CODE " %d"), Web.upload_error); - } - WSContentSend_P(error); - DEBUG_CORE_LOG(PSTR("UPL: %s"), error); - stop_flash_rotate = Settings.flag.stop_flash_rotate; - } else { - WSContentSend_P(PSTR("%06x'>" D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS)); - WSContentSend_P(HTTP_MSG_RSTRT); - ShowWebSource(SRC_WEBGUI); -#ifdef USE_TASMOTA_SLAVE - if (TasmotaSlave_GetFlagFlashing()) { - restart_flag = 0; - } else { - restart_flag = 2; - } -#else - restart_flag = 2; -#endif - } - SettingsBufferFree(); - WSContentSend_P(PSTR("

")); - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); -#ifdef USE_TASMOTA_SLAVE - if (TasmotaSlave_GetFlagFlashing()) { - TasmotaSlave_Flash(); - } -#endif -} - -void HandleUploadLoop(void) -{ - - bool _serialoutput = (LOG_LEVEL_DEBUG <= seriallog_level); - - if (HTTP_USER == Web.state) { return; } - if (Web.upload_error) { - if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); } - return; - } - - HTTPUpload& upload = WebServer->upload(); - - if (UPLOAD_FILE_START == upload.status) { - restart_flag = 60; - if (0 == upload.filename.c_str()[0]) { - Web.upload_error = 1; - return; - } - SettingsSave(1); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_FILE " %s ..."), upload.filename.c_str()); - if (UPL_SETTINGS == Web.upload_file_type) { - if (!SettingsBufferAlloc()) { - Web.upload_error = 2; - return; - } - } else { - MqttRetryCounter(60); -#ifdef USE_EMULATION - UdpDisconnect(); -#endif -#ifdef USE_ARILUX_RF - AriluxRfDisable(); -#endif - if (Settings.flag.mqtt_enabled) { - MqttDisconnect(); - } - uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; - if (!Update.begin(maxSketchSpace)) { - - - - - - - Web.upload_error = 2; - return; - } - } - Web.upload_progress_dot_count = 0; - } else if (!Web.upload_error && (UPLOAD_FILE_WRITE == upload.status)) { - if (0 == upload.totalSize) { - if (UPL_SETTINGS == Web.upload_file_type) { - Web.config_block_count = 0; - } - else { -#ifdef USE_RF_FLASH - if ((SONOFF_BRIDGE == my_module_type) && (upload.buf[0] == ':')) { - Update.end(); - Web.upload_file_type = UPL_EFM8BB1; - - Web.upload_error = SnfBrUpdateInit(); - if (Web.upload_error != 0) { return; } - } else -#endif -#ifdef USE_TASMOTA_SLAVE - if ((WEMOS == my_module_type) && (upload.buf[0] == ':')) { - Update.end(); - Web.upload_file_type = UPL_TASMOTASLAVE; - Web.upload_error = TasmotaSlave_UpdateInit(); - if (Web.upload_error != 0) { return; } - } else -#endif - { - if ((upload.buf[0] != 0xE9) && (upload.buf[0] != 0x1F)) { - Web.upload_error = 3; - return; - } - if (0xE9 == upload.buf[0]) { - uint32_t bin_flash_size = ESP.magicFlashChipSize((upload.buf[3] & 0xf0) >> 4); - if (bin_flash_size > ESP.getFlashChipRealSize()) { - Web.upload_error = 4; - return; - } - - } - } - } - } - if (UPL_SETTINGS == Web.upload_file_type) { - if (!Web.upload_error) { - if (upload.currentSize > (sizeof(Settings) - (Web.config_block_count * HTTP_UPLOAD_BUFLEN))) { - Web.upload_error = 9; - return; - } - memcpy(settings_buffer + (Web.config_block_count * HTTP_UPLOAD_BUFLEN), upload.buf, upload.currentSize); - Web.config_block_count++; - } - } -#ifdef USE_RF_FLASH - else if (UPL_EFM8BB1 == Web.upload_file_type) { - if (efm8bb1_update != nullptr) { - ssize_t result = rf_glue_remnant_with_new_data_and_write(efm8bb1_update, upload.buf, upload.currentSize); - free(efm8bb1_update); - efm8bb1_update = nullptr; - if (result != 0) { - Web.upload_error = abs(result); - return; - } - } - ssize_t result = rf_search_and_write(upload.buf, upload.currentSize); - if (result < 0) { - Web.upload_error = abs(result); - return; - } else if (result > 0) { - if ((size_t)result > upload.currentSize) { - - Web.upload_error = 9; - return; - } - - size_t remnant_sz = upload.currentSize - result; - efm8bb1_update = (uint8_t *) malloc(remnant_sz + 1); - if (efm8bb1_update == nullptr) { - Web.upload_error = 2; - return; - } - memcpy(efm8bb1_update, upload.buf + result, remnant_sz); - - efm8bb1_update[remnant_sz] = '\0'; - } - } -#endif -#ifdef USE_TASMOTA_SLAVE - else if (UPL_TASMOTASLAVE == Web.upload_file_type) { - TasmotaSlave_WriteBuffer(upload.buf, upload.currentSize); - } -#endif - else { - if (!Web.upload_error && (Update.write(upload.buf, upload.currentSize) != upload.currentSize)) { - Web.upload_error = 5; - return; - } - if (_serialoutput) { - Serial.printf("."); - Web.upload_progress_dot_count++; - if (!(Web.upload_progress_dot_count % 80)) { Serial.println(); } - } - } - } else if(!Web.upload_error && (UPLOAD_FILE_END == upload.status)) { - if (_serialoutput && (Web.upload_progress_dot_count % 80)) { - Serial.println(); - } - if (UPL_SETTINGS == Web.upload_file_type) { - if (Web.config_xor_on_set) { - for (uint32_t i = 2; i < sizeof(Settings); i++) { - settings_buffer[i] ^= (Web.config_xor_on_set +i); - } - } - bool valid_settings = false; - unsigned long buffer_version = settings_buffer[11] << 24 | settings_buffer[10] << 16 | settings_buffer[9] << 8 | settings_buffer[8]; - if (buffer_version > 0x06000000) { - uint32_t buffer_size = settings_buffer[3] << 8 | settings_buffer[2]; - if (buffer_version > 0x0606000A) { - uint32_t buffer_crc32 = settings_buffer[4095] << 24 | settings_buffer[4094] << 16 | settings_buffer[4093] << 8 | settings_buffer[4092]; - valid_settings = (GetCfgCrc32(settings_buffer, buffer_size -4) == buffer_crc32); - } else { - uint16_t buffer_crc16 = settings_buffer[15] << 8 | settings_buffer[14]; - valid_settings = (GetCfgCrc16(settings_buffer, buffer_size) == buffer_crc16); - } - } else { - valid_settings = (settings_buffer[0] == CONFIG_FILE_SIGN); - } - if (valid_settings) { - SettingsDefaultSet2(); - memcpy((char*)&Settings +16, settings_buffer +16, sizeof(Settings) -16); - Settings.version = buffer_version; - SettingsBufferFree(); - } else { - Web.upload_error = 8; - return; - } - } -#ifdef USE_RF_FLASH - else if (UPL_EFM8BB1 == Web.upload_file_type) { - - Web.upload_file_type = UPL_TASMOTA; - } -#endif -#ifdef USE_TASMOTA_SLAVE - else if (UPL_TASMOTASLAVE == Web.upload_file_type) { - - TasmotaSlave_SetFlagFlashing(true); - Web.upload_file_type = UPL_TASMOTA; - } -#endif - else { - if (!Update.end(true)) { - if (_serialoutput) { Update.printError(Serial); } - Web.upload_error = 6; - return; - } - if (!VersionCompatible()) { - Web.upload_error = 14; - return; - } - } - if (!Web.upload_error) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_UPLOAD D_SUCCESSFUL " %u bytes. " D_RESTARTING), upload.totalSize); - } - } else if (UPLOAD_FILE_ABORTED == upload.status) { - restart_flag = 0; - MqttRetryCounter(0); - Web.upload_error = 7; - if (UPL_TASMOTA == Web.upload_file_type) { Update.end(); } - } - delay(0); -} - - - -void HandlePreflightRequest(void) -{ - HttpHeaderCors(); - WebServer->sendHeader(F("Access-Control-Allow-Methods"), F("GET, POST")); - WebServer->sendHeader(F("Access-Control-Allow-Headers"), F("authorization")); - WSSend(200, CT_HTML, ""); -} - - - -void HandleHttpCommand(void) -{ - if (!HttpCheckPriviledgedAccess(false)) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND)); - - bool valid = true; - if (strlen(SettingsText(SET_WEBPWD))) { - char tmp1[33]; - WebGetArg("user", tmp1, sizeof(tmp1)); - char tmp2[strlen(SettingsText(SET_WEBPWD)) +1]; - WebGetArg("password", tmp2, sizeof(tmp2)); - if (!(!strcmp(tmp1, WEB_USERNAME) && !strcmp(tmp2, SettingsText(SET_WEBPWD)))) { valid = false; } - } - - WSContentBegin(200, CT_JSON); - if (valid) { - uint32_t curridx = web_log_index; - String svalue = WebServer->arg("cmnd"); - if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { - ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCOMMAND); - if (web_log_index != curridx) { - uint32_t counter = curridx; - WSContentSend_P(PSTR("{")); - bool cflg = false; - do { - char* tmp; - size_t len; - GetLog(counter, &tmp, &len); - if (len) { - - char* JSON = (char*)memchr(tmp, '{', len); - if (JSON) { - size_t JSONlen = len - (JSON - tmp); - if (JSONlen > sizeof(mqtt_data)) { JSONlen = sizeof(mqtt_data); } - char stemp[JSONlen]; - strlcpy(stemp, JSON +1, JSONlen -2); - WSContentSend_P(PSTR("%s%s"), (cflg) ? "," : "", stemp); - cflg = true; - } - } - counter++; - counter &= 0xFF; - if (!counter) counter++; - } while (counter != web_log_index); - WSContentSend_P(PSTR("}")); - } else { - WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENABLE_WEBLOG_FOR_RESPONSE "\"}")); - } - } else { - WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_ENTER_COMMAND " cmnd=\"}")); - } - } else { - WSContentSend_P(PSTR("{\"" D_RSLT_WARNING "\":\"" D_NEED_USER_AND_PASSWORD "\"}")); - } - WSContentEnd(); -} - - - -void HandleConsole(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - if (WebServer->hasArg("c2")) { - HandleConsoleRefresh(); - return; - } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONSOLE); - - WSContentStart_P(S_CONSOLE); - WSContentSend_P(HTTP_SCRIPT_CONSOL, Settings.web_refresh); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_CMND); - WSContentSpaceButton(BUTTON_MAIN); - WSContentStop(); -} - -void HandleConsoleRefresh(void) -{ - bool cflg = true; - uint32_t counter = 0; - - String svalue = WebServer->arg("c1"); - if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str()); - ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE); - } - - char stmp[8]; - WebGetArg("c2", stmp, sizeof(stmp)); - if (strlen(stmp)) { counter = atoi(stmp); } - - WSContentBegin(200, CT_PLAIN); - WSContentSend_P(PSTR("%d}1%d}1"), web_log_index, Web.reset_web_log_flag); - if (!Web.reset_web_log_flag) { - counter = 0; - Web.reset_web_log_flag = true; - } - if (counter != web_log_index) { - if (!counter) { - counter = web_log_index; - cflg = false; - } - do { - char* tmp; - size_t len; - GetLog(counter, &tmp, &len); - if (len) { - if (len > sizeof(mqtt_data) -2) { len = sizeof(mqtt_data); } - char stemp[len +1]; - strlcpy(stemp, tmp, len); - WSContentSend_P(PSTR("%s%s"), (cflg) ? "\n" : "", stemp); - cflg = true; - } - counter++; - counter &= 0xFF; - if (!counter) { counter++; } - } while (counter != web_log_index); - } - WSContentSend_P(PSTR("}1")); - WSContentEnd(); -} - - - -void HandleNotFound(void) -{ - - - if (CaptivePortal()) { return; } - -#ifdef USE_EMULATION -#ifdef USE_EMULATION_HUE - String path = WebServer->uri(); - if ((EMUL_HUE == Settings.flag2.emulation) && (path.startsWith("/api"))) { - HandleHueApi(&path); - } else -#endif -#endif - { - WSContentBegin(404, CT_PLAIN); - WSContentSend_P(PSTR(D_FILE_NOT_FOUND "\n\nURI: %s\nMethod: %s\nArguments: %d\n"), WebServer->uri().c_str(), (WebServer->method() == HTTP_GET) ? "GET" : "POST", WebServer->args()); - for (uint32_t i = 0; i < WebServer->args(); i++) { - WSContentSend_P(PSTR(" %s: %s\n"), WebServer->argName(i).c_str(), WebServer->arg(i).c_str()); - } - WSContentEnd(); - } -} - - -bool CaptivePortal(void) -{ - - if ((WifiIsInManagerMode()) && !ValidIpAddress(WebServer->hostHeader().c_str())) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_REDIRECTED)); - - WebServer->sendHeader(F("Location"), String("http://") + WebServer->client().localIP().toString(), true); - WSSend(302, CT_PLAIN, ""); - WebServer->client().stop(); - return true; - } - return false; -} - - - -String UrlEncode(const String& text) -{ - const char hex[] = "0123456789ABCDEF"; - - String encoded = ""; - int len = text.length(); - int i = 0; - while (i < len) { - char decodedChar = text.charAt(i++); -# 2672 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_01_webserver.ino" - if ((' ' == decodedChar) || ('+' == decodedChar)) { - encoded += '%'; - encoded += hex[decodedChar >> 4]; - encoded += hex[decodedChar & 0xF]; - } else { - encoded += decodedChar; - } - - } - return encoded; -} - -int WebSend(char *buffer) -{ - - - - - - char *host; - char *user; - char *password; - char *command; - int status = 1; - - - host = strtok_r(buffer, "]", &command); - if (host && command) { - RemoveSpace(host); - host++; - host = strtok_r(host, ",", &user); - String url = F("http://"); - url += host; - - command = Trim(command); - if (command[0] != '/') { - url += F("/cm?"); - if (user) { - user = strtok_r(user, ":", &password); - if (user && password) { - char userpass[200]; - snprintf_P(userpass, sizeof(userpass), PSTR("user=%s&password=%s&"), user, password); - url += userpass; - } - } - url += F("cmnd="); - } - url += command; - - DEBUG_CORE_LOG(PSTR("WEB: Uri |%s|"), url.c_str()); - -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) - HTTPClient http; - if (http.begin(UrlEncode(url))) { -#else - WiFiClient http_client; - HTTPClient http; - if (http.begin(http_client, UrlEncode(url))) { -#endif - int http_code = http.GET(); - if (http_code > 0) { - if (http_code == HTTP_CODE_OK || http_code == HTTP_CODE_MOVED_PERMANENTLY) { -#ifdef USE_WEBSEND_RESPONSE - - const char* read = http.getString().c_str(); - uint32_t j = 0; - char text = '.'; - while (text != '\0') { - text = *read++; - if (text > 31) { - mqtt_data[j++] = text; - if (j == sizeof(mqtt_data) -2) { break; } - } - } - mqtt_data[j] = '\0'; - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WEBSEND)); -#ifdef USE_SCRIPT -extern uint8_t tasm_cmd_activ; - - tasm_cmd_activ=0; - XdrvRulesProcess(); -#endif -#endif - } - status = 0; - } else { - status = 2; - } - http.end(); - } else { - status = 3; - } - } - return status; -} - -bool JsonWebColor(const char* dataBuf) -{ - - - - - - char dataBufLc[strlen(dataBuf) +1]; - LowerCase(dataBufLc, dataBuf); - RemoveSpace(dataBufLc); - if (strlen(dataBufLc) < 9) { return false; } - - StaticJsonBuffer<450> jb; - JsonObject& obj = jb.parseObject(dataBufLc); - if (!obj.success()) { return false; } - - char parm_lc[10]; - if (obj[LowerCase(parm_lc, D_CMND_WEBCOLOR)].success()) { - for (uint32_t i = 0; i < COL_LAST; i++) { - const char* color = obj[parm_lc][i]; - if (color != nullptr) { - WebHexCode(i, color); - } - } - } - return true; -} - -const char kWebSendStatus[] PROGMEM = D_JSON_DONE "|" D_JSON_WRONG_PARAMETERS "|" D_JSON_CONNECT_FAILED "|" D_JSON_HOST_NOT_FOUND "|" D_JSON_MEMORY_ERROR; - -const char kWebCommands[] PROGMEM = "|" -#ifdef USE_EMULATION - D_CMND_EMULATION "|" -#endif -#ifdef USE_SENDMAIL - D_CMND_SENDMAIL "|" -#endif - D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBCOLOR "|" - D_CMND_WEBSENSOR "|" D_CMND_WEBBUTTON "|" D_CMND_CORS; - -void (* const WebCommand[])(void) PROGMEM = { -#ifdef USE_EMULATION - &CmndEmulation, -#endif -#ifdef USE_SENDMAIL - &CmndSendmail, -#endif - &CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, - &CmndWebSensor, &CmndWebButton, &CmndCors }; - - - - - -#ifdef USE_EMULATION -void CmndEmulation(void) -{ -#if defined(USE_EMULATION_WEMO) && defined(USE_EMULATION_HUE) - if ((XdrvMailbox.payload >= EMUL_NONE) && (XdrvMailbox.payload < EMUL_MAX)) { -#else -#ifndef USE_EMULATION_WEMO - if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_HUE == XdrvMailbox.payload)) { -#endif -#ifndef USE_EMULATION_HUE - if ((EMUL_NONE == XdrvMailbox.payload) || (EMUL_WEMO == XdrvMailbox.payload)) { -#endif -#endif - Settings.flag2.emulation = XdrvMailbox.payload; - restart_flag = 2; - } - ResponseCmndNumber(Settings.flag2.emulation); -} -#endif - -#ifdef USE_SENDMAIL -void CmndSendmail(void) -{ - if (XdrvMailbox.data_len > 0) { - uint8_t result = SendMail(XdrvMailbox.data); - char stemp1[20]; - ResponseCmndChar(GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); - } -} -#endif - - -void CmndWebServer(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { - Settings.webserver = XdrvMailbox.payload; - } - if (Settings.webserver) { - Response_P(PSTR("{\"" D_CMND_WEBSERVER "\":\"" D_JSON_ACTIVE_FOR " %s " D_JSON_ON_DEVICE " %s " D_JSON_WITH_IP_ADDRESS " %s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); - } else { - ResponseCmndStateText(0); - } -} - -void CmndWebPassword(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_WEBPWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data); - ResponseCmndChar(SettingsText(SET_WEBPWD)); - } else { - Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); - } -} - -void CmndWeblog(void) -{ - if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { - Settings.weblog_level = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.weblog_level); -} - -void CmndWebRefresh(void) -{ - if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload <= 10000)) { - Settings.web_refresh = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.web_refresh); -} - -void CmndWebSend(void) -{ - if (XdrvMailbox.data_len > 0) { - uint32_t result = WebSend(XdrvMailbox.data); - char stemp1[20]; - ResponseCmndChar(GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); - } -} - -void CmndWebColor(void) -{ - if (XdrvMailbox.data_len > 0) { - if (strstr(XdrvMailbox.data, "{") == nullptr) { - if ((XdrvMailbox.data_len > 3) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= COL_LAST)) { - WebHexCode(XdrvMailbox.index -1, XdrvMailbox.data); - } - else if (0 == XdrvMailbox.payload) { - SettingsDefaultWebColor(); - } - } - else { - JsonWebColor(XdrvMailbox.data); - } - } - Response_P(PSTR("{\"" D_CMND_WEBCOLOR "\":[")); - for (uint32_t i = 0; i < COL_LAST; i++) { - if (i) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"#%06x\""), WebColor(i)); - } - ResponseAppend_P(PSTR("]}")); -} - -void CmndWebSensor(void) -{ - if (XdrvMailbox.index < MAX_XSNS_DRIVERS) { - if (XdrvMailbox.payload >= 0) { - bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); - } - } - Response_P(PSTR("{\"" D_CMND_WEBSENSOR "\":")); - XsnsSensorState(); - ResponseJsonEnd(); -} - -void CmndWebButton(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_BUTTON_TEXT)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_BUTTON1, MAX_BUTTON_TEXT); - } else { - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_BUTTON1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); - } - ResponseCmndIdxChar(SettingsText(SET_BUTTON1 + XdrvMailbox.index -1)); - } - } -} - -void CmndCors(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_CORS, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? WEB_PASSWORD : XdrvMailbox.data); - } - ResponseCmndChar(SettingsText(SET_CORS)); -} - - - - - -bool Xdrv01(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_LOOP: - PollDnsWebserver(); -#ifdef USE_EMULATION - if (Settings.flag2.emulation) { PollUdp(); } -#endif - break; - case FUNC_COMMAND: - result = DecodeCommand(kWebCommands, WebCommand); - break; - } - return result; -} -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_02_mqtt.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_02_mqtt.ino" -#define XDRV_02 2 - - - -#ifdef USE_MQTT_TLS - #include "WiFiClientSecureLightBearSSL.h" - BearSSL::WiFiClientSecure_light *tlsClient; -#else - WiFiClient EspClient; -#endif - -const char kMqttCommands[] PROGMEM = "|" -#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) - D_CMND_MQTTFINGERPRINT "|" -#endif - D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - D_CMND_TLSKEY "|" -#endif - D_CMND_MQTTHOST "|" D_CMND_MQTTPORT "|" D_CMND_MQTTRETRY "|" D_CMND_STATETEXT "|" D_CMND_MQTTCLIENT "|" - D_CMND_FULLTOPIC "|" D_CMND_PREFIX "|" D_CMND_GROUPTOPIC "|" D_CMND_TOPIC "|" D_CMND_PUBLISH "|" D_CMND_MQTTLOG "|" - D_CMND_BUTTONTOPIC "|" D_CMND_SWITCHTOPIC "|" D_CMND_BUTTONRETAIN "|" D_CMND_SWITCHRETAIN "|" D_CMND_POWERRETAIN "|" D_CMND_SENSORRETAIN ; - -void (* const MqttCommand[])(void) PROGMEM = { -#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) - &CmndMqttFingerprint, -#endif - &CmndMqttUser, &CmndMqttPassword, -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - &CmndTlsKey, -#endif - &CmndMqttHost, &CmndMqttPort, &CmndMqttRetry, &CmndStateText, &CmndMqttClient, - &CmndFullTopic, &CmndPrefix, &CmndGroupTopic, &CmndTopic, &CmndPublish, &CmndMqttlog, - &CmndButtonTopic, &CmndSwitchTopic, &CmndButtonRetain, &CmndSwitchRetain, &CmndPowerRetain, &CmndSensorRetain }; - -struct MQTT { - uint16_t connect_count = 0; - uint16_t retry_counter = 1; - uint8_t initial_connection_state = 2; - bool connected = false; - bool allowed = false; -} Mqtt; - -#ifdef USE_MQTT_TLS - -#ifdef USE_MQTT_AWS_IOT -#include - -const br_ec_private_key *AWS_IoT_Private_Key = nullptr; -const br_x509_certificate *AWS_IoT_Client_Certificate = nullptr; - -class tls_entry_t { -public: - uint32_t name; - uint16_t start; - uint16_t len; -}; - -const static uint32_t TLS_NAME_SKEY = 0x2079656B; -const static uint32_t TLS_NAME_CRT = 0x20747263; - -class tls_dir_t { -public: - tls_entry_t entry[4]; -}; - -tls_dir_t tls_dir; - -#endif - - - - -bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value) { - for (uint32_t i = 0; i<20; i++) { - if (finger[i] != value) { - return false; - } - } - return true; -} -#endif - -void MakeValidMqtt(uint32_t option, char* str) -{ - - - uint32_t i = 0; - while (str[i] > 0) { - - if ((str[i] == '+') || (str[i] == '#') || (str[i] == ' ')) { - if (option) { - uint32_t j = i; - while (str[j] > 0) { - str[j] = str[j +1]; - j++; - } - i--; - } else { - str[i] = '_'; - } - } - i++; - } -} - -#ifdef USE_DISCOVERY -#ifdef MQTT_HOST_DISCOVERY -void MqttDiscoverServer(void) -{ - if (!Wifi.mdns_begun) { return; } - - int n = MDNS.queryService("mqtt", "tcp"); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_QUERY_DONE " %d"), n); - - if (n > 0) { - uint32_t i = 0; -#ifdef MDNS_HOSTNAME - for (i = n; i > 0; i--) { - if (!strcmp(MDNS.hostname(i).c_str(), MDNS_HOSTNAME)) { - break; - } - } -#endif - SettingsUpdateText(SET_MQTT_HOST, MDNS.IP(i).toString().c_str()); - Settings.mqtt_port = MDNS.port(i); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MDNS D_MQTT_SERVICE_FOUND " %s, " D_IP_ADDRESS " %s, " D_PORT " %d"), MDNS.hostname(i).c_str(), SettingsText(SET_MQTT_HOST), Settings.mqtt_port); - } -} -#endif -#endif -# 163 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_02_mqtt.ino" -#include - - -#if (MQTT_MAX_PACKET_SIZE -TOPSZ -7) < MIN_MESSZ - #error "MQTT_MAX_PACKET_SIZE is too small in libraries/PubSubClient/src/PubSubClient.h, increase it to at least 1200" -#endif - -#ifdef USE_MQTT_TLS -PubSubClient MqttClient; -#else -PubSubClient MqttClient(EspClient); -#endif - -void MqttInit(void) -{ -#ifdef USE_MQTT_TLS - tlsClient = new BearSSL::WiFiClientSecure_light(1024,1024); - -#ifdef USE_MQTT_AWS_IOT - loadTlsDir(); - tlsClient->setClientECCert(AWS_IoT_Client_Certificate, - AWS_IoT_Private_Key, - 0xFFFF , 0); -#endif - -#ifdef USE_MQTT_TLS_CA_CERT -#ifdef USE_MQTT_AWS_IOT - tlsClient->setTrustAnchor(&AmazonRootCA1_TA); -#else - tlsClient->setTrustAnchor(&LetsEncryptX3CrossSigned_TA); -#endif -#endif - - MqttClient.setClient(*tlsClient); -#endif -} - -bool MqttIsConnected(void) -{ - return MqttClient.connected(); -} - -void MqttDisconnect(void) -{ - MqttClient.disconnect(); -} - -void MqttSubscribeLib(const char *topic) -{ - MqttClient.subscribe(topic); - MqttClient.loop(); -} - -void MqttUnsubscribeLib(const char *topic) -{ - MqttClient.unsubscribe(topic); - MqttClient.loop(); -} - -bool MqttPublishLib(const char* topic, bool retained) -{ - - if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { - char *str = strstr(topic, SettingsText(SET_MQTTPREFIX1)); - if (str == topic) { - mqtt_cmnd_blocked_reset = 4; - mqtt_cmnd_blocked++; - } - } - - bool result = MqttClient.publish(topic, mqtt_data, retained); - yield(); - return result; -} - -void MqttDataHandler(char* mqtt_topic, uint8_t* mqtt_data, unsigned int data_len) -{ -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("MqttDataHandler")); -#endif - - - if (data_len >= MQTT_MAX_PACKET_SIZE) { return; } - - - if (!strcmp(SettingsText(SET_MQTTPREFIX1), SettingsText(SET_MQTTPREFIX2))) { - char *str = strstr(mqtt_topic, SettingsText(SET_MQTTPREFIX1)); - if ((str == mqtt_topic) && mqtt_cmnd_blocked) { - mqtt_cmnd_blocked--; - return; - } - } - - - char topic[TOPSZ]; - strlcpy(topic, mqtt_topic, sizeof(topic)); - mqtt_data[data_len] = 0; - char data[data_len +1]; - memcpy(data, mqtt_data, sizeof(data)); - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_MQTT D_RECEIVED_TOPIC " \"%s\", " D_DATA_SIZE " %d, " D_DATA " \"%s\""), topic, data_len, data); - - - - XdrvMailbox.index = strlen(topic); - XdrvMailbox.data_len = data_len; - XdrvMailbox.topic = topic; - XdrvMailbox.data = (char*)data; - if (XdrvCall(FUNC_MQTT_DATA)) { return; } - - ShowSource(SRC_MQTT); - - CommandHandler(topic, data, data_len); -} - - - -void MqttRetryCounter(uint8_t value) -{ - Mqtt.retry_counter = value; -} - -void MqttSubscribe(const char *topic) -{ - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_SUBSCRIBE_TO " %s"), topic); - MqttSubscribeLib(topic); -} - -void MqttUnsubscribe(const char *topic) -{ - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT D_UNSUBSCRIBE_FROM " %s"), topic); - MqttUnsubscribeLib(topic); -} - -void MqttPublishLogging(const char *mxtime) -{ - char saved_mqtt_data[strlen(mqtt_data) +1]; - memcpy(saved_mqtt_data, mqtt_data, sizeof(saved_mqtt_data)); - - - Response_P(PSTR("%s%s"), mxtime, log_data); - char stopic[TOPSZ]; - GetTopic_P(stopic, STAT, mqtt_topic, PSTR("LOGGING")); - MqttPublishLib(stopic, false); - - memcpy(mqtt_data, saved_mqtt_data, sizeof(saved_mqtt_data)); -} - -void MqttPublish(const char* topic, bool retained) -{ -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("MqttPublish")); -#endif - -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) || defined(MQTT_NO_RETAIN) - - - - retained = false; -#endif - - char sretained[CMDSZ]; - sretained[0] = '\0'; - char slog_type[20]; - snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_RESULT)); - - if (Settings.flag.mqtt_enabled) { - if (MqttPublishLib(topic, retained)) { - snprintf_P(slog_type, sizeof(slog_type), PSTR(D_LOG_MQTT)); - if (retained) { - snprintf_P(sretained, sizeof(sretained), PSTR(" (" D_RETAINED ")")); - } - } - } - - snprintf_P(log_data, sizeof(log_data), PSTR("%s%s = %s"), slog_type, (Settings.flag.mqtt_enabled) ? topic : strrchr(topic,'/')+1, mqtt_data); - if (strlen(log_data) >= (sizeof(log_data) - strlen(sretained) -1)) { - log_data[sizeof(log_data) - strlen(sretained) -5] = '\0'; - snprintf_P(log_data, sizeof(log_data), PSTR("%s ..."), log_data); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s%s"), log_data, sretained); - AddLog(LOG_LEVEL_INFO); - - if (Settings.ledstate &0x04) { - blinks++; - } -} - -void MqttPublish(const char* topic) -{ - MqttPublish(topic, false); -} - -void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic, bool retained) -{ - - - - - - - - char romram[64]; - char stopic[TOPSZ]; - - snprintf_P(romram, sizeof(romram), ((prefix > 3) && !Settings.flag.mqtt_response) ? S_RSLT_RESULT : subtopic); - for (uint32_t i = 0; i < strlen(romram); i++) { - romram[i] = toupper(romram[i]); - } - prefix &= 3; - GetTopic_P(stopic, prefix, mqtt_topic, romram); - MqttPublish(stopic, retained); - -#ifdef USE_MQTT_AWS_IOT - if ((prefix > 0) && (Settings.flag4.awsiot_shadow)) { - - char *topic = SettingsText(SET_MQTT_TOPIC); - char topic2[strlen(topic)+1]; - strcpy(topic2, topic); - - char *s = topic2; - while (*s) { - if ('/' == *s) { - *s = '_'; - } - s++; - } - - snprintf_P(romram, sizeof(romram), PSTR("$aws/things/%s/shadow/update"), topic2); - - - char *mqtt_save = (char*) malloc(strlen(mqtt_data)+1); - if (!mqtt_save) { return; } - strcpy(mqtt_save, mqtt_data); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"state\":{\"reported\":%s}}"), mqtt_save); - free(mqtt_save); - - bool result = MqttClient.publish(romram, mqtt_data, false); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Updated shadow: %s"), romram); - yield(); - } -#endif -} - -void MqttPublishPrefixTopic_P(uint32_t prefix, const char* subtopic) -{ - MqttPublishPrefixTopic_P(prefix, subtopic, false); -} - -void MqttPublishTeleSensor(void) -{ - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); - XdrvRulesProcess(); -} - -void MqttPublishPowerState(uint32_t device) -{ - char stopic[TOPSZ]; - char scommand[33]; - - if ((device < 1) || (device > devices_present)) { device = 1; } - -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan() && (device > 1)) { - if (GetFanspeed() < MaxFanspeed()) { -#ifdef USE_DOMOTICZ - DomoticzUpdateFanState(); -#endif - snprintf_P(scommand, sizeof(scommand), PSTR(D_CMND_FANSPEED)); - GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); - Response_P(S_JSON_COMMAND_NVALUE, scommand, GetFanspeed()); - MqttPublish(stopic); - } - } else { -#endif - GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable); - GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT); - Response_P(S_JSON_COMMAND_SVALUE, scommand, GetStateText(bitRead(power, device -1))); - MqttPublish(stopic); - - GetTopic_P(stopic, STAT, mqtt_topic, scommand); - Response_P(GetStateText(bitRead(power, device -1))); - MqttPublish(stopic, Settings.flag.mqtt_power_retain); -#ifdef USE_SONOFF_IFAN - } -#endif -} - -void MqttPublishAllPowerState(void) -{ - for (uint32_t i = 1; i <= devices_present; i++) { - MqttPublishPowerState(i); -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan()) { break; } -#endif - } -} - -void MqttPublishPowerBlinkState(uint32_t device) -{ - char scommand[33]; - - if ((device < 1) || (device > devices_present)) { - device = 1; - } - Response_P(PSTR("{\"%s\":\"" D_JSON_BLINK " %s\"}"), - GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable), GetStateText(bitRead(blink_mask, device -1))); - - MqttPublishPrefixTopic_P(RESULT_OR_STAT, S_RSLT_POWER); -} - - - -uint16_t MqttConnectCount(void) -{ - return Mqtt.connect_count; -} - -void MqttDisconnected(int state) -{ - Mqtt.connected = false; - Mqtt.retry_counter = Settings.mqtt_retry; - - MqttClient.disconnect(); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), SettingsText(SET_MQTT_HOST), Settings.mqtt_port, state, Mqtt.retry_counter); - rules_flag.mqtt_disconnected = 1; -} - -void MqttConnected(void) -{ - char stopic[TOPSZ]; - - if (Mqtt.allowed) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_CONNECTED)); - Mqtt.connected = true; - Mqtt.retry_counter = 0; - Mqtt.connect_count++; - - GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); - Response_P(PSTR(D_ONLINE)); - MqttPublish(stopic, true); - - - mqtt_data[0] = '\0'; - MqttPublishPrefixTopic_P(CMND, S_RSLT_POWER); - - GetTopic_P(stopic, CMND, mqtt_topic, PSTR("#")); - MqttSubscribe(stopic); - if (strstr_P(SettingsText(SET_MQTT_FULLTOPIC), MQTT_TOKEN_TOPIC) != nullptr) { - GetGroupTopic_P(stopic, PSTR("#")); - MqttSubscribe(stopic); - GetFallbackTopic_P(stopic, PSTR("#")); - MqttSubscribe(stopic); - } - - XdrvCall(FUNC_MQTT_SUBSCRIBE); - } - - if (Mqtt.initial_connection_state) { - char stopic2[TOPSZ]; - Response_P(PSTR("{\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_FALLBACKTOPIC "\":\"%s\",\"" D_CMND_GROUPTOPIC "\":\"%s\"}"), - ModuleName().c_str(), my_version, my_image, GetFallbackTopic_P(stopic, ""), GetGroupTopic_P(stopic2, "")); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "1")); -#ifdef USE_WEBSERVER - if (Settings.webserver) { -#if LWIP_IPV6 - Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\",\"IPv6Address\":\"%s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str(),WifiGetIPv6().c_str()); -#else - Response_P(PSTR("{\"" D_JSON_WEBSERVER_MODE "\":\"%s\",\"" D_CMND_HOSTNAME "\":\"%s\",\"" D_CMND_IPADDRESS "\":\"%s\"}"), - (2 == Settings.webserver) ? D_ADMIN : D_USER, my_hostname, WiFi.localIP().toString().c_str()); -#endif - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "2")); - } -#endif - Response_P(PSTR("{\"" D_JSON_RESTARTREASON "\":")); - if (CrashFlag()) { - CrashDump(); - } else { - ResponseAppend_P(PSTR("\"%s\""), GetResetReason().c_str()); - } - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_INFO "3")); - MqttPublishAllPowerState(); - if (Settings.tele_period) { - tele_period = Settings.tele_period -5; - } - rules_flag.system_boot = 1; - XdrvCall(FUNC_MQTT_INIT); - } - Mqtt.initial_connection_state = 0; - - global_state.mqtt_down = 0; - if (Settings.flag.mqtt_enabled) { - rules_flag.mqtt_connected = 1; - } -} - -void MqttReconnect(void) -{ - char stopic[TOPSZ]; - - Mqtt.allowed = Settings.flag.mqtt_enabled; - if (Mqtt.allowed) { -#ifdef USE_DISCOVERY -#ifdef MQTT_HOST_DISCOVERY - MqttDiscoverServer(); -#endif -#endif - if (!strlen(SettingsText(SET_MQTT_HOST)) || !Settings.mqtt_port) { - Mqtt.allowed = false; - } -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - - if (!AWS_IoT_Private_Key || !AWS_IoT_Client_Certificate) { - Mqtt.allowed = false; - } -#endif - } - if (!Mqtt.allowed) { - MqttConnected(); - return; - } - -#ifdef USE_EMULATION - UdpDisconnect(); -#endif - - AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR(D_ATTEMPTING_CONNECTION)); - - Mqtt.connected = false; - Mqtt.retry_counter = Settings.mqtt_retry; - global_state.mqtt_down = 1; - - char *mqtt_user = nullptr; - char *mqtt_pwd = nullptr; - if (strlen(SettingsText(SET_MQTT_USER))) { - mqtt_user = SettingsText(SET_MQTT_USER); - } - if (strlen(SettingsText(SET_MQTT_PWD))) { - mqtt_pwd = SettingsText(SET_MQTT_PWD); - } - - GetTopic_P(stopic, TELE, mqtt_topic, S_LWT); - Response_P(S_OFFLINE); - - if (MqttClient.connected()) { MqttClient.disconnect(); } -#ifdef USE_MQTT_TLS - tlsClient->stop(); -#else - EspClient = WiFiClient(); - MqttClient.setClient(EspClient); -#endif - - if (2 == Mqtt.initial_connection_state) { - Mqtt.initial_connection_state = 1; - } - - MqttClient.setCallback(MqttDataHandler); -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - - tlsClient->setClientECCert(AWS_IoT_Client_Certificate, - AWS_IoT_Private_Key, - 0xFFFF , 0); -#endif - MqttClient.setServer(SettingsText(SET_MQTT_HOST), Settings.mqtt_port); - - uint32_t mqtt_connect_time = millis(); -#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) - bool allow_all_fingerprints = false; - bool learn_fingerprint1 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0x00); - bool learn_fingerprint2 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0x00); - allow_all_fingerprints |= is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0xff); - allow_all_fingerprints |= is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0xff); - allow_all_fingerprints |= learn_fingerprint1; - allow_all_fingerprints |= learn_fingerprint2; - tlsClient->setPubKeyFingerprint(Settings.mqtt_fingerprint[0], Settings.mqtt_fingerprint[1], allow_all_fingerprints); -#endif -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "AWS IoT endpoint: %s"), SettingsText(SET_MQTT_HOST)); - if (MqttClient.connect(mqtt_client, nullptr, nullptr, stopic, 1, false, mqtt_data, MQTT_CLEAN_SESSION)) { -#else - if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd, stopic, 1, true, mqtt_data, MQTT_CLEAN_SESSION)) { -#endif -#ifdef USE_MQTT_TLS - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connected in %d ms, max ThunkStack used %d"), - millis() - mqtt_connect_time, tlsClient->getMaxThunkStackUse()); - if (!tlsClient->getMFLNStatus()) { - AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR("MFLN not supported by TLS server")); - } -#ifndef USE_MQTT_TLS_CA_CERT - - char buf_fingerprint[64]; - ToHex_P((unsigned char *)tlsClient->getRecvPubKeyFingerprint(), 20, buf_fingerprint, sizeof(buf_fingerprint), ' '); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Server fingerprint: %s"), buf_fingerprint); - - if (learn_fingerprint1 || learn_fingerprint2) { - - bool fingerprint_matched = false; - const uint8_t *recv_fingerprint = tlsClient->getRecvPubKeyFingerprint(); - if (0 == memcmp(recv_fingerprint, Settings.mqtt_fingerprint[0], 20)) { - fingerprint_matched = true; - } - if (0 == memcmp(recv_fingerprint, Settings.mqtt_fingerprint[1], 20)) { - fingerprint_matched = true; - } - if (!fingerprint_matched) { - - if (learn_fingerprint1) { - memcpy(Settings.mqtt_fingerprint[0], recv_fingerprint, 20); - } - if (learn_fingerprint2) { - memcpy(Settings.mqtt_fingerprint[1], recv_fingerprint, 20); - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "Fingerprint learned: %s"), buf_fingerprint); - - SettingsSaveAll(); - } - } -#endif -#endif - MqttConnected(); - } else { -#ifdef USE_MQTT_TLS - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "TLS connection error: %d"), tlsClient->getLastError()); -#endif - MqttDisconnected(MqttClient.state()); - } -} - -void MqttCheck(void) -{ - if (Settings.flag.mqtt_enabled) { - if (!MqttIsConnected()) { - global_state.mqtt_down = 1; - if (!Mqtt.retry_counter) { - MqttReconnect(); - } else { - Mqtt.retry_counter--; - } - } else { - global_state.mqtt_down = 0; - } - } else { - global_state.mqtt_down = 0; - if (Mqtt.initial_connection_state) { - MqttReconnect(); - } - } -} - - - - - -#if defined(USE_MQTT_TLS) && !defined(USE_MQTT_TLS_CA_CERT) -void CmndMqttFingerprint(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { - char fingerprint[60]; - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(fingerprint))) { - strlcpy(fingerprint, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? (1 == XdrvMailbox.index) ? MQTT_FINGERPRINT1 : MQTT_FINGERPRINT2 : XdrvMailbox.data, sizeof(fingerprint)); - char *p = fingerprint; - for (uint32_t i = 0; i < 20; i++) { - Settings.mqtt_fingerprint[XdrvMailbox.index -1][i] = strtol(p, &p, 16); - } - restart_flag = 2; - } - ResponseCmndIdxChar(ToHex_P((unsigned char *)Settings.mqtt_fingerprint[XdrvMailbox.index -1], 20, fingerprint, sizeof(fingerprint), ' ')); - } -} -#endif - -void CmndMqttUser(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_MQTT_USER, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_USER : XdrvMailbox.data); - restart_flag = 2; - } - ResponseCmndChar(SettingsText(SET_MQTT_USER)); -} - -void CmndMqttPassword(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_MQTT_PWD, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_PASS : XdrvMailbox.data); - ResponseCmndChar(SettingsText(SET_MQTT_PWD)); - restart_flag = 2; - } else { - Response_P(S_JSON_COMMAND_ASTERISK, XdrvMailbox.command); - } -} - -void CmndMqttlog(void) -{ - if ((XdrvMailbox.payload >= LOG_LEVEL_NONE) && (XdrvMailbox.payload <= LOG_LEVEL_DEBUG_MORE)) { - Settings.mqttlog_level = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.mqttlog_level); -} - -void CmndMqttHost(void) -{ - if (XdrvMailbox.data_len > 0) { - SettingsUpdateText(SET_MQTT_HOST, (SC_CLEAR == Shortcut()) ? "" : (SC_DEFAULT == Shortcut()) ? MQTT_HOST : XdrvMailbox.data); - restart_flag = 2; - } - ResponseCmndChar(SettingsText(SET_MQTT_HOST)); -} - -void CmndMqttPort(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 65536)) { - Settings.mqtt_port = (1 == XdrvMailbox.payload) ? MQTT_PORT : XdrvMailbox.payload; - restart_flag = 2; - } - ResponseCmndNumber(Settings.mqtt_port); -} - -void CmndMqttRetry(void) -{ - if ((XdrvMailbox.payload >= MQTT_RETRY_SECS) && (XdrvMailbox.payload < 32001)) { - Settings.mqtt_retry = XdrvMailbox.payload; - Mqtt.retry_counter = Settings.mqtt_retry; - } - ResponseCmndNumber(Settings.mqtt_retry); -} - -void CmndStateText(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_STATE_TEXT)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_STATE_TXT1, MAX_STATE_TEXT); - } else { - if (XdrvMailbox.data_len > 0) { - for (uint32_t i = 0; i <= XdrvMailbox.data_len; i++) { - if (XdrvMailbox.data[i] == ' ') XdrvMailbox.data[i] = '_'; - } - SettingsUpdateText(SET_STATE_TXT1 + XdrvMailbox.index -1, XdrvMailbox.data); - } - ResponseCmndIdxChar(GetStateText(XdrvMailbox.index -1)); - } - } -} - -void CmndMqttClient(void) -{ - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { - SettingsUpdateText(SET_MQTT_CLIENT, (SC_DEFAULT == Shortcut()) ? MQTT_CLIENT_ID : XdrvMailbox.data); - restart_flag = 2; - } - ResponseCmndChar(SettingsText(SET_MQTT_CLIENT)); -} - -void CmndFullTopic(void) -{ - if (XdrvMailbox.data_len > 0) { - MakeValidMqtt(1, XdrvMailbox.data); - if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } - char stemp1[TOPSZ]; - strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_FULLTOPIC : XdrvMailbox.data, sizeof(stemp1)); - if (strcmp(stemp1, SettingsText(SET_MQTT_FULLTOPIC))) { - Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); - MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); - SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp1); - restart_flag = 2; - } - } - ResponseCmndChar(SettingsText(SET_MQTT_FULLTOPIC)); -} - -void CmndPrefix(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_MQTT_PREFIXES)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_MQTTPREFIX1, MAX_MQTT_PREFIXES); - } else { - if (XdrvMailbox.data_len > 0) { - MakeValidMqtt(0, XdrvMailbox.data); - SettingsUpdateText(SET_MQTTPREFIX1 + XdrvMailbox.index -1, - (SC_DEFAULT == Shortcut()) ? (1==XdrvMailbox.index) ? SUB_PREFIX : (2==XdrvMailbox.index) ? PUB_PREFIX : PUB_PREFIX2 : XdrvMailbox.data); - restart_flag = 2; - } - ResponseCmndIdxChar(SettingsText(SET_MQTTPREFIX1 + XdrvMailbox.index -1)); - } - } -} - -void CmndPublish(void) -{ - if (XdrvMailbox.data_len > 0) { - char *payload_part; - char *mqtt_part = strtok_r(XdrvMailbox.data, " ", &payload_part); - if (mqtt_part) { - char stemp1[TOPSZ]; - strlcpy(stemp1, mqtt_part, sizeof(stemp1)); - if ((payload_part != nullptr) && strlen(payload_part)) { - strlcpy(mqtt_data, payload_part, sizeof(mqtt_data)); - } else { - mqtt_data[0] = '\0'; - } - MqttPublish(stemp1, (XdrvMailbox.index == 2)); - - mqtt_data[0] = '\0'; - } - } -} - -void CmndGroupTopic(void) -{ - if (XdrvMailbox.data_len > 0) { - MakeValidMqtt(0, XdrvMailbox.data); - if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } - SettingsUpdateText(SET_MQTT_GRP_TOPIC, (SC_DEFAULT == Shortcut()) ? MQTT_GRPTOPIC : XdrvMailbox.data); - restart_flag = 2; - } - ResponseCmndChar(SettingsText(SET_MQTT_GRP_TOPIC)); -} - -void CmndTopic(void) -{ - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { - MakeValidMqtt(0, XdrvMailbox.data); - if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } - char stemp1[TOPSZ]; - strlcpy(stemp1, (SC_DEFAULT == Shortcut()) ? MQTT_TOPIC : XdrvMailbox.data, sizeof(stemp1)); - if (strcmp(stemp1, SettingsText(SET_MQTT_TOPIC))) { - Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); - MqttPublishPrefixTopic_P(TELE, PSTR(D_LWT), true); - SettingsUpdateText(SET_MQTT_TOPIC, stemp1); - restart_flag = 2; - } - } - ResponseCmndChar(SettingsText(SET_MQTT_TOPIC)); -} - -void CmndButtonTopic(void) -{ - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { - MakeValidMqtt(0, XdrvMailbox.data); - if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } - switch (Shortcut()) { - case SC_CLEAR: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, ""); break; - case SC_DEFAULT: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, mqtt_topic); break; - case SC_USER: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, MQTT_BUTTON_TOPIC); break; - default: SettingsUpdateText(SET_MQTT_BUTTON_TOPIC, XdrvMailbox.data); - } - } - ResponseCmndChar(SettingsText(SET_MQTT_BUTTON_TOPIC)); -} - -void CmndSwitchTopic(void) -{ - if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) { - MakeValidMqtt(0, XdrvMailbox.data); - if (!strcmp(XdrvMailbox.data, mqtt_client)) { SetShortcutDefault(); } - switch (Shortcut()) { - case SC_CLEAR: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, ""); break; - case SC_DEFAULT: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, mqtt_topic); break; - case SC_USER: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, MQTT_SWITCH_TOPIC); break; - default: SettingsUpdateText(SET_MQTT_SWITCH_TOPIC, XdrvMailbox.data); - } - } - ResponseCmndChar(SettingsText(SET_MQTT_SWITCH_TOPIC)); -} - -void CmndButtonRetain(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - if (!XdrvMailbox.payload) { - for (uint32_t i = 1; i <= MAX_KEYS; i++) { - SendKey(KEY_BUTTON, i, CLEAR_RETAIN); - } - } - Settings.flag.mqtt_button_retain = XdrvMailbox.payload; - } - ResponseCmndStateText(Settings.flag.mqtt_button_retain); -} - -void CmndSwitchRetain(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - if (!XdrvMailbox.payload) { - for (uint32_t i = 1; i <= MAX_SWITCHES; i++) { - SendKey(KEY_SWITCH, i, CLEAR_RETAIN); - } - } - Settings.flag.mqtt_switch_retain = XdrvMailbox.payload; - } - ResponseCmndStateText(Settings.flag.mqtt_switch_retain); -} - -void CmndPowerRetain(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - if (!XdrvMailbox.payload) { - char stemp1[TOPSZ]; - char scommand[CMDSZ]; - for (uint32_t i = 1; i <= devices_present; i++) { - GetTopic_P(stemp1, STAT, mqtt_topic, GetPowerDevice(scommand, i, sizeof(scommand), Settings.flag.device_index_enable)); - mqtt_data[0] = '\0'; - MqttPublish(stemp1, Settings.flag.mqtt_power_retain); - } - } - Settings.flag.mqtt_power_retain = XdrvMailbox.payload; - } - ResponseCmndStateText(Settings.flag.mqtt_power_retain); -} - -void CmndSensorRetain(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - if (!XdrvMailbox.payload) { - mqtt_data[0] = '\0'; - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_ENERGY), Settings.flag.mqtt_sensor_retain); - } - Settings.flag.mqtt_sensor_retain = XdrvMailbox.payload; - } - ResponseCmndStateText(Settings.flag.mqtt_sensor_retain); -} - - - - -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - - - -const static uint16_t tls_spi_start_sector = 0xFF; -const static uint8_t* tls_spi_start = (uint8_t*) 0x402FF000; -const static size_t tls_spi_len = 0x1000; -const static size_t tls_block_offset = 0x0400; -const static size_t tls_block_len = 0x0400; -const static size_t tls_obj_store_offset = tls_block_offset + sizeof(tls_dir_t); - - -inline void TlsEraseBuffer(uint8_t *buffer) { - memset(buffer + tls_block_offset, 0xFF, tls_block_len); -} - - - -static br_ec_private_key EC = { - 23, - nullptr, 0 -}; - -static br_x509_certificate CHAIN[] = { - { nullptr, 0 } -}; - - - -void loadTlsDir(void) { - memcpy_P(&tls_dir, tls_spi_start + tls_block_offset, sizeof(tls_dir)); - - - if ((TLS_NAME_SKEY == tls_dir.entry[0].name) && (tls_dir.entry[0].len > 0)) { - EC.x = (unsigned char *)(tls_spi_start + tls_obj_store_offset + tls_dir.entry[0].start); - EC.xlen = tls_dir.entry[0].len; - AWS_IoT_Private_Key = &EC; - } else { - AWS_IoT_Private_Key = nullptr; - } - if ((TLS_NAME_CRT == tls_dir.entry[1].name) && (tls_dir.entry[1].len > 0)) { - CHAIN[0].data = (unsigned char *) (tls_spi_start + tls_obj_store_offset + tls_dir.entry[1].start); - CHAIN[0].data_len = tls_dir.entry[1].len; - AWS_IoT_Client_Certificate = CHAIN; - } else { - AWS_IoT_Client_Certificate = nullptr; - } - -} - -const char ALLOCATE_ERROR[] PROGMEM = "TLSKey " D_JSON_ERROR ": cannot allocate buffer."; - -void CmndTlsKey(void) { -#ifdef DEBUG_DUMP_TLS - if (0 == XdrvMailbox.index){ - CmndTlsDump(); - } -#endif - if ((XdrvMailbox.index >= 1) && (XdrvMailbox.index <= 2)) { - tls_dir_t *tls_dir_write; - - if (XdrvMailbox.data_len > 0) { - - uint8_t *spi_buffer = (uint8_t*) malloc(tls_spi_len); - if (!spi_buffer) { - AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR); - return; - } - memcpy_P(spi_buffer, tls_spi_start, tls_spi_len); - - - RemoveAllSpaces(XdrvMailbox.data); - - - uint32_t bin_len = decode_base64_length((unsigned char*)XdrvMailbox.data); - uint8_t *bin_buf = nullptr; - if (bin_len > 0) { - bin_buf = (uint8_t*) malloc(bin_len + 4); - if (!bin_buf) { - AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR); - free(spi_buffer); - return; - } - } - - - if (bin_len > 0) { - decode_base64((unsigned char*)XdrvMailbox.data, bin_buf); - } - - - tls_dir_write = (tls_dir_t*) (spi_buffer + tls_block_offset); - - if (1 == XdrvMailbox.index) { - - - TlsEraseBuffer(spi_buffer); - if (bin_len > 0) { - if (bin_len != 32) { - - AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate must be 32 bytes: %d."), bin_len); - free(spi_buffer); - free(bin_buf); - return; - } - tls_entry_t *entry = &tls_dir_write->entry[0]; - entry->name = TLS_NAME_SKEY; - entry->start = 0; - entry->len = bin_len; - memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len); - } else { - - } - } else if (2 == XdrvMailbox.index) { - - if (TLS_NAME_SKEY != tls_dir.entry[0].name) { - - AddLog_P(LOG_LEVEL_INFO, PSTR("TLSKey: cannot store Cert if no Key previously stored.")); - free(spi_buffer); - free(bin_buf); - return; - } - if (bin_len <= 256) { - - AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate length too short: %d."), bin_len); - free(spi_buffer); - free(bin_buf); - return; - } - tls_entry_t *entry = &tls_dir_write->entry[1]; - entry->name = TLS_NAME_CRT; - entry->start = (tls_dir_write->entry[0].start + tls_dir_write->entry[0].len + 3) & ~0x03; - entry->len = bin_len; - memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len); - } - - if (ESP.flashEraseSector(tls_spi_start_sector)) { - ESP.flashWrite(tls_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - } - free(spi_buffer); - free(bin_buf); - } - - loadTlsDir(); - Response_P(PSTR("{\"%s1\":%d,\"%s2\":%d}"), - XdrvMailbox.command, AWS_IoT_Private_Key ? tls_dir.entry[0].len : -1, - XdrvMailbox.command, AWS_IoT_Client_Certificate ? tls_dir.entry[1].len : -1); - } -} - -#ifdef DEBUG_DUMP_TLS - -uint32_t bswap32(uint32_t x) { - return ((x << 24) & 0xff000000 ) | - ((x << 8) & 0x00ff0000 ) | - ((x >> 8) & 0x0000ff00 ) | - ((x >> 24) & 0x000000ff ); -} -void CmndTlsDump(void) { - uint32_t start = (uint32_t)tls_spi_start + tls_block_offset; - uint32_t end = start + tls_block_len -1; - for (uint32_t pos = start; pos < end; pos += 0x10) { - uint32_t* values = (uint32_t*)(pos); -#ifdef ARDUINO_ESP8266_RELEASE_2_3_0 - Serial.printf("%08x: %08x %08x %08x %08x\n", pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3])); -#else - Serial.printf_P(PSTR("%08x: %08x %08x %08x %08x\n"), pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3])); -#endif - } -} -#endif -#endif - - - - - -#ifdef USE_WEBSERVER - -#define WEB_HANDLE_MQTT "mq" - -const char S_CONFIGURE_MQTT[] PROGMEM = D_CONFIGURE_MQTT; - -const char HTTP_BTN_MENU_MQTT[] PROGMEM = - "

"; - -const char HTTP_FORM_MQTT1[] PROGMEM = - "
 " D_MQTT_PARAMETERS " " - "
" - "

" D_HOST " (" MQTT_HOST ")

" - "

" D_PORT " (" STR(MQTT_PORT) ")

" - "

" D_CLIENT " (%s)

"; -const char HTTP_FORM_MQTT2[] PROGMEM = - "

" D_USER " (" MQTT_USER ")

" - "

" D_PASSWORD "

" - "

" D_TOPIC " = %%topic%% (%s)

" - "

" D_FULL_TOPIC " (%s)

"; - -void HandleMqttConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MQTT); - - if (WebServer->hasArg("save")) { - MqttSaveSettings(); - WebRestart(1); - return; - } - - char str[TOPSZ]; - - WSContentStart_P(S_CONFIGURE_MQTT); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_MQTT1, - SettingsText(SET_MQTT_HOST), - Settings.mqtt_port, - Format(str, MQTT_CLIENT_ID, sizeof(str)), MQTT_CLIENT_ID, SettingsText(SET_MQTT_CLIENT)); - WSContentSend_P(HTTP_FORM_MQTT2, - (!strlen(SettingsText(SET_MQTT_USER))) ? "0" : SettingsText(SET_MQTT_USER), - Format(str, MQTT_TOPIC, sizeof(str)), MQTT_TOPIC, SettingsText(SET_MQTT_TOPIC), - MQTT_FULLTOPIC, MQTT_FULLTOPIC, SettingsText(SET_MQTT_FULLTOPIC)); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void MqttSaveSettings(void) -{ - char tmp[TOPSZ]; - char stemp[TOPSZ]; - char stemp2[TOPSZ]; - - WebGetArg("mt", tmp, sizeof(tmp)); - strlcpy(stemp, (!strlen(tmp)) ? MQTT_TOPIC : tmp, sizeof(stemp)); - MakeValidMqtt(0, stemp); - WebGetArg("mf", tmp, sizeof(tmp)); - strlcpy(stemp2, (!strlen(tmp)) ? MQTT_FULLTOPIC : tmp, sizeof(stemp2)); - MakeValidMqtt(1, stemp2); - if ((strcmp(stemp, SettingsText(SET_MQTT_TOPIC))) || (strcmp(stemp2, SettingsText(SET_MQTT_FULLTOPIC)))) { - Response_P((Settings.flag.mqtt_offline) ? S_OFFLINE : ""); - MqttPublishPrefixTopic_P(TELE, S_LWT, true); - } - SettingsUpdateText(SET_MQTT_TOPIC, stemp); - SettingsUpdateText(SET_MQTT_FULLTOPIC, stemp2); - WebGetArg("mh", tmp, sizeof(tmp)); - SettingsUpdateText(SET_MQTT_HOST, (!strlen(tmp)) ? MQTT_HOST : (!strcmp(tmp,"0")) ? "" : tmp); - WebGetArg("ml", tmp, sizeof(tmp)); - Settings.mqtt_port = (!strlen(tmp)) ? MQTT_PORT : atoi(tmp); - WebGetArg("mc", tmp, sizeof(tmp)); - SettingsUpdateText(SET_MQTT_CLIENT, (!strlen(tmp)) ? MQTT_CLIENT_ID : tmp); -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), - SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); -#else - WebGetArg("mu", tmp, sizeof(tmp)); - SettingsUpdateText(SET_MQTT_USER, (!strlen(tmp)) ? MQTT_USER : (!strcmp(tmp,"0")) ? "" : tmp); - WebGetArg("mp", tmp, sizeof(tmp)); - SettingsUpdateText(SET_MQTT_PWD, (!strlen(tmp)) ? "" : (!strcmp(tmp, D_ASTERISK_PWD)) ? SettingsText(SET_MQTT_PWD) : tmp); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CMND_MQTTHOST " %s, " D_CMND_MQTTPORT " %d, " D_CMND_MQTTCLIENT " %s, " D_CMND_MQTTUSER " %s, " D_CMND_TOPIC " %s, " D_CMND_FULLTOPIC " %s"), - SettingsText(SET_MQTT_HOST), Settings.mqtt_port, SettingsText(SET_MQTT_CLIENT), SettingsText(SET_MQTT_USER), SettingsText(SET_MQTT_TOPIC), SettingsText(SET_MQTT_FULLTOPIC)); -#endif -} -#endif - - - - - -bool Xdrv02(uint8_t function) -{ - bool result = false; - - if (Settings.flag.mqtt_enabled) { - switch (function) { - case FUNC_PRE_INIT: - MqttInit(); - break; - case FUNC_EVERY_50_MSECOND: - MqttClient.loop(); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_ADD_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_MQTT); - break; - case FUNC_WEB_ADD_HANDLER: - WebServer->on("/" WEB_HANDLE_MQTT, HandleMqttConfiguration); - break; -#endif - case FUNC_COMMAND: - result = DecodeCommand(kMqttCommands, MqttCommand); - break; - } - } - return result; -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_03_energy.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_03_energy.ino" -#ifdef USE_ENERGY_SENSOR - - - - -#define XDRV_03 3 -#define XSNS_03 3 - - - - -#define ENERGY_NONE 0 -#define ENERGY_WATCHDOG 4 - -#include - -#define D_CMND_POWERCAL "PowerCal" -#define D_CMND_VOLTAGECAL "VoltageCal" -#define D_CMND_CURRENTCAL "CurrentCal" -#define D_CMND_TARIFF "Tariff" -#define D_CMND_MODULEADDRESS "ModuleAddress" - -enum EnergyCommands { - CMND_POWERCAL, CMND_VOLTAGECAL, CMND_CURRENTCAL, - CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET, CMND_MODULEADDRESS }; - -const char kEnergyCommands[] PROGMEM = "|" - D_CMND_POWERCAL "|" D_CMND_VOLTAGECAL "|" D_CMND_CURRENTCAL "|" - D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|" D_CMND_MODULEADDRESS "|" -#ifdef USE_ENERGY_MARGIN_DETECTION - D_CMND_POWERDELTA "|" D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|" -#ifdef USE_ENERGY_POWER_LIMIT - D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|" - D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|" - D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW "|" -#endif -#endif - D_CMND_ENERGYRESET "|" D_CMND_TARIFF ; - -void (* const EnergyCommand[])(void) PROGMEM = { - &CmndPowerCal, &CmndVoltageCal, &CmndCurrentCal, - &CmndPowerSet, &CmndVoltageSet, &CmndCurrentSet, &CmndFrequencySet, &CmndModuleAddress, -#ifdef USE_ENERGY_MARGIN_DETECTION - &CmndPowerDelta, &CmndPowerLow, &CmndPowerHigh, &CmndVoltageLow, &CmndVoltageHigh, &CmndCurrentLow, &CmndCurrentHigh, -#ifdef USE_ENERGY_POWER_LIMIT - &CmndMaxEnergy, &CmndMaxEnergyStart, - &CmndMaxPower, &CmndMaxPowerHold, &CmndMaxPowerWindow, - &CmndSafePower, &CmndSafePowerHold, &CmndSafePowerWindow, -#endif -#endif - &CmndEnergyReset, &CmndTariff }; - -const char kEnergyPhases[] PROGMEM = "|%s / %s|%s / %s / %s||[%s,%s]|[%s,%s,%s]"; - -struct ENERGY { - float voltage[3] = { 0, 0, 0 }; - float current[3] = { 0, 0, 0 }; - float active_power[3] = { 0, 0, 0 }; - float apparent_power[3] = { NAN, NAN, NAN }; - float reactive_power[3] = { NAN, NAN, NAN }; - float power_factor[3] = { NAN, NAN, NAN }; - float frequency[3] = { NAN, NAN, NAN }; - - float start_energy = 0; - float daily = 0; - float total = 0; - float export_active = NAN; - - unsigned long kWhtoday_delta = 0; - unsigned long kWhtoday_offset = 0; - unsigned long kWhtoday; - unsigned long period = 0; - - uint8_t fifth_second = 0; - uint8_t command_code = 0; - uint8_t data_valid[3] = { 0, 0, 0 }; - - uint8_t phase_count = 1; - bool voltage_common = false; - - bool voltage_available = true; - bool current_available = true; - - bool type_dc = false; - bool power_on = true; - -#ifdef USE_ENERGY_MARGIN_DETECTION - uint16_t power_history[3] = { 0 }; - uint8_t power_steady_counter = 8; - bool power_delta = false; - bool min_power_flag = false; - bool max_power_flag = false; - bool min_voltage_flag = false; - bool max_voltage_flag = false; - bool min_current_flag = false; - bool max_current_flag = false; - -#ifdef USE_ENERGY_POWER_LIMIT - uint16_t mplh_counter = 0; - uint16_t mplw_counter = 0; - uint8_t mplr_counter = 0; - uint8_t max_energy_state = 0; -#endif -#endif -} Energy; - -Ticker ticker_energy; - - - -bool EnergyTariff1Active() -{ - uint8_t dst = 0; - if (IsDst() && (Settings.tariff[0][1] != Settings.tariff[1][1])) { - dst = 1; - } - if (Settings.tariff[0][dst] != Settings.tariff[1][dst]) { - if (Settings.flag3.energy_weekend && ((RtcTime.day_of_week == 1) || - (RtcTime.day_of_week == 7))) { - return true; - } - uint32_t minutes = MinutesPastMidnight(); - if (Settings.tariff[0][dst] > Settings.tariff[1][dst]) { - - return ((minutes >= Settings.tariff[0][dst]) || (minutes < Settings.tariff[1][dst])); - } else { - - return ((minutes >= Settings.tariff[0][dst]) && (minutes < Settings.tariff[1][dst])); - } - } else { - return false; - } -} - -void EnergyUpdateToday(void) -{ - if (Energy.kWhtoday_delta > 1000) { - unsigned long delta = Energy.kWhtoday_delta / 1000; - Energy.kWhtoday_delta -= (delta * 1000); - Energy.kWhtoday += delta; - } - - RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset + Energy.kWhtoday; - Energy.daily = (float)(RtcSettings.energy_kWhtoday) / 100000; - Energy.total = (float)(RtcSettings.energy_kWhtotal + RtcSettings.energy_kWhtoday) / 100000; - - if (RtcTime.valid){ - - uint32_t energy_diff = (uint32_t)(Energy.total * 100000) - RtcSettings.energy_usage.last_usage_kWhtotal; - RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 100000); - - uint32_t return_diff = 0; - if (!isnan(Energy.export_active)) { - return_diff = (uint32_t)(Energy.export_active * 100000) - RtcSettings.energy_usage.last_return_kWhtotal; - RtcSettings.energy_usage.last_return_kWhtotal = (uint32_t)(Energy.export_active * 100000); - } - - if (EnergyTariff1Active()) { - RtcSettings.energy_usage.usage1_kWhtotal += energy_diff; - RtcSettings.energy_usage.return1_kWhtotal += return_diff; - } else { - RtcSettings.energy_usage.usage2_kWhtotal += energy_diff; - RtcSettings.energy_usage.return2_kWhtotal += return_diff; - } - } -} - -void EnergyUpdateTotal(float value, bool kwh) -{ - - - - - uint32_t multiplier = (kwh) ? 100000 : 100; - - if (0 == Energy.start_energy || (value < Energy.start_energy)) { - Energy.start_energy = value; - } - else if (value != Energy.start_energy) { - Energy.kWhtoday = (unsigned long)((value - Energy.start_energy) * multiplier); - } - - if ((Energy.total < (value - 0.01)) && - Settings.flag3.hardware_energy_total) { - RtcSettings.energy_kWhtotal = (unsigned long)((value * multiplier) - Energy.kWhtoday_offset - Energy.kWhtoday); - Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; - Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; - Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); - - } - EnergyUpdateToday(); -} - - - -void Energy200ms(void) -{ - Energy.power_on = (power != 0) | Settings.flag.no_power_on_check; - - Energy.fifth_second++; - if (5 == Energy.fifth_second) { - Energy.fifth_second = 0; - - XnrgCall(FUNC_ENERGY_EVERY_SECOND); - - if (RtcTime.valid) { - if (LocalTime() == Midnight()) { - Settings.energy_kWhyesterday = RtcSettings.energy_kWhtoday; - - RtcSettings.energy_kWhtotal += RtcSettings.energy_kWhtoday; - Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; - Energy.kWhtoday = 0; - Energy.kWhtoday_offset = 0; - RtcSettings.energy_kWhtoday = 0; - Energy.start_energy = 0; - - Energy.kWhtoday_delta = 0; - Energy.period = Energy.kWhtoday; - EnergyUpdateToday(); -#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) - Energy.max_energy_state = 3; -#endif - } -#if defined(USE_ENERGY_MARGIN_DETECTION) && defined(USE_ENERGY_POWER_LIMIT) - if ((RtcTime.hour == Settings.energy_max_energy_start) && (3 == Energy.max_energy_state )) { - Energy.max_energy_state = 0; - } -#endif - - } - } - - XnrgCall(FUNC_EVERY_200_MSECOND); -} - -void EnergySaveState(void) -{ - Settings.energy_kWhdoy = (RtcTime.valid) ? RtcTime.day_of_year : 0; - - Settings.energy_kWhtoday = RtcSettings.energy_kWhtoday; - Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; - - Settings.energy_usage = RtcSettings.energy_usage; -} - -#ifdef USE_ENERGY_MARGIN_DETECTION -bool EnergyMargin(bool type, uint16_t margin, uint16_t value, bool &flag, bool &save_flag) -{ - bool change; - - if (!margin) return false; - change = save_flag; - if (type) { - flag = (value > margin); - } else { - flag = (value < margin); - } - save_flag = flag; - return (change != save_flag); -} - -void EnergyMarginCheck(void) -{ - if (Energy.power_steady_counter) { - Energy.power_steady_counter--; - return; - } - - uint16_t energy_power_u = (uint16_t)(Energy.active_power[0]); - - if (Settings.energy_power_delta) { - uint16_t delta = abs(Energy.power_history[0] - energy_power_u); - if (delta > 0) { - if (Settings.energy_power_delta < 101) { - uint16_t min_power = (Energy.power_history[0] > energy_power_u) ? energy_power_u : Energy.power_history[0]; - if (0 == min_power) { min_power++; } - if (((delta * 100) / min_power) > Settings.energy_power_delta) { - Energy.power_delta = true; - } - } else { - if (delta > (Settings.energy_power_delta -100)) { - Energy.power_delta = true; - } - } - if (Energy.power_delta) { - Energy.power_history[1] = Energy.active_power[0]; - Energy.power_history[2] = Energy.active_power[0]; - } - } - } - Energy.power_history[0] = Energy.power_history[1]; - Energy.power_history[1] = Energy.power_history[2]; - Energy.power_history[2] = energy_power_u; - - if (Energy.power_on && (Settings.energy_min_power || Settings.energy_max_power || Settings.energy_min_voltage || Settings.energy_max_voltage || Settings.energy_min_current || Settings.energy_max_current)) { - uint16_t energy_voltage_u = (uint16_t)(Energy.voltage[0]); - uint16_t energy_current_u = (uint16_t)(Energy.current[0] * 1000); - - DEBUG_DRIVER_LOG(PSTR("NRG: W %d, U %d, I %d"), energy_power_u, energy_voltage_u, energy_current_u); - - Response_P(PSTR("{")); - bool flag; - bool jsonflg = false; - if (EnergyMargin(false, Settings.energy_min_power, energy_power_u, flag, Energy.min_power_flag)) { - ResponseAppend_P(PSTR("%s\"" D_CMND_POWERLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); - jsonflg = true; - } - if (EnergyMargin(true, Settings.energy_max_power, energy_power_u, flag, Energy.max_power_flag)) { - ResponseAppend_P(PSTR("%s\"" D_CMND_POWERHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); - jsonflg = true; - } - if (EnergyMargin(false, Settings.energy_min_voltage, energy_voltage_u, flag, Energy.min_voltage_flag)) { - ResponseAppend_P(PSTR("%s\"" D_CMND_VOLTAGELOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); - jsonflg = true; - } - if (EnergyMargin(true, Settings.energy_max_voltage, energy_voltage_u, flag, Energy.max_voltage_flag)) { - ResponseAppend_P(PSTR("%s\"" D_CMND_VOLTAGEHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); - jsonflg = true; - } - if (EnergyMargin(false, Settings.energy_min_current, energy_current_u, flag, Energy.min_current_flag)) { - ResponseAppend_P(PSTR("%s\"" D_CMND_CURRENTLOW "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); - jsonflg = true; - } - if (EnergyMargin(true, Settings.energy_max_current, energy_current_u, flag, Energy.max_current_flag)) { - ResponseAppend_P(PSTR("%s\"" D_CMND_CURRENTHIGH "\":\"%s\""), (jsonflg)?",":"", GetStateText(flag)); - jsonflg = true; - } - if (jsonflg) { - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_MARGINS), MQTT_TELE_RETAIN); - EnergyMqttShow(); - } - } - -#ifdef USE_ENERGY_POWER_LIMIT - - if (Settings.energy_max_power_limit) { - if (Energy.active_power[0] > Settings.energy_max_power_limit) { - if (!Energy.mplh_counter) { - Energy.mplh_counter = Settings.energy_max_power_limit_hold; - } else { - Energy.mplh_counter--; - if (!Energy.mplh_counter) { - ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHED "\":%d}"), energy_power_u); - MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); - EnergyMqttShow(); - SetAllPower(POWER_ALL_OFF, SRC_MAXPOWER); - if (!Energy.mplr_counter) { - Energy.mplr_counter = Settings.param[P_MAX_POWER_RETRY] +1; - } - Energy.mplw_counter = Settings.energy_max_power_limit_window; - } - } - } - else if (power && (energy_power_u <= Settings.energy_max_power_limit)) { - Energy.mplh_counter = 0; - Energy.mplr_counter = 0; - Energy.mplw_counter = 0; - } - if (!power) { - if (Energy.mplw_counter) { - Energy.mplw_counter--; - } else { - if (Energy.mplr_counter) { - Energy.mplr_counter--; - if (Energy.mplr_counter) { - ResponseTime_P(PSTR(",\"" D_JSON_POWERMONITOR "\":\"%s\"}"), GetStateText(1)); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_POWERMONITOR)); - RestorePower(true, SRC_MAXPOWER); - } else { - ResponseTime_P(PSTR(",\"" D_JSON_MAXPOWERREACHEDRETRY "\":\"%s\"}"), GetStateText(0)); - MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); - EnergyMqttShow(); - SetAllPower(POWER_ALL_OFF, SRC_MAXPOWER); - } - } - } - } - } - - - if (Settings.energy_max_energy) { - uint16_t energy_daily_u = (uint16_t)(Energy.daily * 1000); - if (!Energy.max_energy_state && (RtcTime.hour == Settings.energy_max_energy_start)) { - Energy.max_energy_state = 1; - ResponseTime_P(PSTR(",\"" D_JSON_ENERGYMONITOR "\":\"%s\"}"), GetStateText(1)); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_JSON_ENERGYMONITOR)); - RestorePower(true, SRC_MAXENERGY); - } - else if ((1 == Energy.max_energy_state ) && (energy_daily_u >= Settings.energy_max_energy)) { - Energy.max_energy_state = 2; - char stemp[FLOATSZ]; - dtostrfd(Energy.daily, 3, stemp); - ResponseTime_P(PSTR(",\"" D_JSON_MAXENERGYREACHED "\":%s}"), stemp); - MqttPublishPrefixTopic_P(STAT, S_RSLT_WARNING); - EnergyMqttShow(); - SetAllPower(POWER_ALL_OFF, SRC_MAXENERGY); - } - } -#endif - - if (Energy.power_delta) { EnergyMqttShow(); } -} - -void EnergyMqttShow(void) -{ - - int tele_period_save = tele_period; - tele_period = 2; - mqtt_data[0] = '\0'; - ResponseAppendTime(); - EnergyShow(true); - tele_period = tele_period_save; - ResponseJsonEnd(); - MqttPublishTeleSensor(); - Energy.power_delta = false; -} -#endif - -void EnergyEverySecond(void) -{ - - if (global_update) { - if (power && (global_temperature != 9999) && (global_temperature > Settings.param[P_OVER_TEMP])) { - SetAllPower(POWER_ALL_OFF, SRC_OVERTEMP); - } - } - - - uint32_t data_valid = Energy.phase_count; - for (uint32_t i = 0; i < Energy.phase_count; i++) { - if (Energy.data_valid[i] <= ENERGY_WATCHDOG) { - Energy.data_valid[i]++; - if (Energy.data_valid[i] > ENERGY_WATCHDOG) { - - Energy.voltage[i] = 0; - Energy.current[i] = 0; - Energy.active_power[i] = 0; - if (!isnan(Energy.apparent_power[i])) { Energy.apparent_power[i] = 0; } - if (!isnan(Energy.reactive_power[i])) { Energy.reactive_power[i] = 0; } - if (!isnan(Energy.frequency[i])) { Energy.frequency[i] = 0; } - if (!isnan(Energy.power_factor[i])) { Energy.power_factor[i] = 0; } - - data_valid--; - } - } - } - if (!data_valid) { - if (!isnan(Energy.export_active)) { Energy.export_active = 0; } - Energy.start_energy = 0; - - XnrgCall(FUNC_ENERGY_RESET); - } - -#ifdef USE_ENERGY_MARGIN_DETECTION - EnergyMarginCheck(); -#endif -} - - - - - -void EnergyCommandCalResponse(uint32_t nvalue) -{ - snprintf_P(XdrvMailbox.command, CMDSZ, PSTR("%sCal"), XdrvMailbox.command); - ResponseCmndNumber(nvalue); -} - -void CmndEnergyReset(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 3)) { - char *p; - unsigned long lnum = strtoul(XdrvMailbox.data, &p, 10); - if (p != XdrvMailbox.data) { - switch (XdrvMailbox.index) { - case 1: - - Energy.kWhtoday_offset = lnum *100; - Energy.kWhtoday = 0; - Energy.kWhtoday_delta = 0; - Energy.start_energy = 0; - Energy.period = Energy.kWhtoday_offset; - Settings.energy_kWhtoday = Energy.kWhtoday_offset; - RtcSettings.energy_kWhtoday = Energy.kWhtoday_offset; - Energy.daily = (float)Energy.kWhtoday_offset / 100000; - if (!RtcSettings.energy_kWhtotal && !Energy.kWhtoday_offset) { - Settings.energy_kWhtotal_time = LocalTime(); - } - break; - case 2: - - Settings.energy_kWhyesterday = lnum *100; - break; - case 3: - - RtcSettings.energy_kWhtotal = lnum *100; - Settings.energy_kWhtotal = RtcSettings.energy_kWhtotal; - - Settings.energy_kWhtotal_time = (!Energy.kWhtoday_offset) ? LocalTime() : Midnight(); - RtcSettings.energy_usage.last_usage_kWhtotal = (uint32_t)(Energy.total * 1000); - break; - } - } - } - else if ((XdrvMailbox.index > 3) && (XdrvMailbox.index <= 5)) { - uint32_t values[2] = { 0 }; - uint32_t position = ParseParameters(2, values); - values[0] *= 100; - values[1] *= 100; - - switch (XdrvMailbox.index) - { - case 4: - - if (position > 0) { - RtcSettings.energy_usage.usage1_kWhtotal = values[0]; - } - if (position > 1) { - RtcSettings.energy_usage.usage2_kWhtotal = values[1]; - } - Settings.energy_usage.usage1_kWhtotal = RtcSettings.energy_usage.usage1_kWhtotal; - Settings.energy_usage.usage2_kWhtotal = RtcSettings.energy_usage.usage2_kWhtotal; - break; - case 5: - - if (position > 0) { - RtcSettings.energy_usage.return1_kWhtotal = values[0]; - } - if (position > 1) { - RtcSettings.energy_usage.return2_kWhtotal = values[1]; - } - Settings.energy_usage.return1_kWhtotal = RtcSettings.energy_usage.return1_kWhtotal; - Settings.energy_usage.return2_kWhtotal = RtcSettings.energy_usage.return2_kWhtotal; - break; - } - } - - Energy.total = (float)(RtcSettings.energy_kWhtotal + Energy.kWhtoday_offset + Energy.kWhtoday) / 100000; - - char energy_total_chr[FLOATSZ]; - dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr); - char energy_daily_chr[FLOATSZ]; - dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); - char energy_yesterday_chr[FLOATSZ]; - dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); - - char energy_usage1_chr[FLOATSZ]; - dtostrfd((float)Settings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage1_chr); - char energy_usage2_chr[FLOATSZ]; - dtostrfd((float)Settings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_usage2_chr); - char energy_return1_chr[FLOATSZ]; - dtostrfd((float)Settings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return1_chr); - char energy_return2_chr[FLOATSZ]; - dtostrfd((float)Settings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_return2_chr); - - Response_P(PSTR("{\"%s\":{\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s,\"" D_JSON_USAGE "\":[%s,%s],\"" D_JSON_EXPORT "\":[%s,%s]}}"), - XdrvMailbox.command, energy_total_chr, energy_yesterday_chr, energy_daily_chr, energy_usage1_chr, energy_usage2_chr, energy_return1_chr, energy_return2_chr); -} - -void CmndTariff(void) -{ - - - - - - - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { - uint32_t tariff = XdrvMailbox.index -1; - uint32_t time_type = 0; - char *p; - char *str = strtok_r(XdrvMailbox.data, ", ", &p); - while ((str != nullptr) && (time_type < 2)) { - char *q; - uint32_t value = strtol(str, &q, 10); - Settings.tariff[tariff][time_type] = value; - if (value < 24) { - Settings.tariff[tariff][time_type] *= 60; - char *minute = strtok_r(nullptr, ":", &q); - if (minute) { - value = strtol(minute, nullptr, 10); - if (value > 59) { - value = 59; - } - Settings.tariff[tariff][time_type] += value; - } - } - if (Settings.tariff[tariff][time_type] > 1439) { - Settings.tariff[tariff][time_type] = 1439; - } - str = strtok_r(nullptr, ", ", &p); - time_type++; - } - } - else if (XdrvMailbox.index == 9) { - Settings.flag3.energy_weekend = XdrvMailbox.payload & 1; - } - Response_P(PSTR("{\"%s\":{\"Off-Peak\":{\"STD\":\"%s\",\"DST\":\"%s\"},\"Standard\":{\"STD\":\"%s\",\"DST\":\"%s\"},\"Weekend\":\"%s\"}}"), - XdrvMailbox.command, - GetMinuteTime(Settings.tariff[0][0]).c_str(),GetMinuteTime(Settings.tariff[0][1]).c_str(), - GetMinuteTime(Settings.tariff[1][0]).c_str(),GetMinuteTime(Settings.tariff[1][1]).c_str(), - GetStateText(Settings.flag3.energy_weekend)); -} - -void CmndPowerCal(void) -{ - Energy.command_code = CMND_POWERCAL; - if (XnrgCall(FUNC_COMMAND)) { - if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { - Settings.energy_power_calibration = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_power_calibration); - } -} - -void CmndVoltageCal(void) -{ - Energy.command_code = CMND_VOLTAGECAL; - if (XnrgCall(FUNC_COMMAND)) { - if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { - Settings.energy_voltage_calibration = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_voltage_calibration); - } -} - -void CmndCurrentCal(void) -{ - Energy.command_code = CMND_CURRENTCAL; - if (XnrgCall(FUNC_COMMAND)) { - if ((XdrvMailbox.payload > 999) && (XdrvMailbox.payload < 32001)) { - Settings.energy_current_calibration = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_current_calibration); - } -} - -void CmndPowerSet(void) -{ - Energy.command_code = CMND_POWERSET; - if (XnrgCall(FUNC_COMMAND)) { - EnergyCommandCalResponse(Settings.energy_power_calibration); - } -} - -void CmndVoltageSet(void) -{ - Energy.command_code = CMND_VOLTAGESET; - if (XnrgCall(FUNC_COMMAND)) { - EnergyCommandCalResponse(Settings.energy_voltage_calibration); - } -} - -void CmndCurrentSet(void) -{ - Energy.command_code = CMND_CURRENTSET; - if (XnrgCall(FUNC_COMMAND)) { - EnergyCommandCalResponse(Settings.energy_current_calibration); - } -} - -void CmndFrequencySet(void) -{ - Energy.command_code = CMND_FREQUENCYSET; - if (XnrgCall(FUNC_COMMAND)) { - EnergyCommandCalResponse(Settings.energy_frequency_calibration); - } -} - -void CmndModuleAddress(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4) && (1 == Energy.phase_count)) { - Energy.command_code = CMND_MODULEADDRESS; - if (XnrgCall(FUNC_COMMAND)) { - ResponseCmndDone(); - } - } -} - -#ifdef USE_ENERGY_MARGIN_DETECTION -void CmndPowerDelta(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32000)) { - Settings.energy_power_delta = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_power_delta); -} - -void CmndPowerLow(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_min_power = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_min_power); -} - -void CmndPowerHigh(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power); -} - -void CmndVoltageLow(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { - Settings.energy_min_voltage = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_min_voltage); -} - -void CmndVoltageHigh(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 501)) { - Settings.energy_max_voltage = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_voltage); -} - -void CmndCurrentLow(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { - Settings.energy_min_current = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_min_current); -} - -void CmndCurrentHigh(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 16001)) { - Settings.energy_max_current = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_current); -} - -#ifdef USE_ENERGY_POWER_LIMIT -void CmndMaxPower(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_limit = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power_limit); -} - -void CmndMaxPowerHold(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_limit_hold = (1 == XdrvMailbox.payload) ? MAX_POWER_HOLD : XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power_limit_hold); -} - -void CmndMaxPowerWindow(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_limit_window = (1 == XdrvMailbox.payload) ? MAX_POWER_WINDOW : XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power_limit_window); -} - -void CmndSafePower(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_safe_limit = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power_safe_limit); -} - -void CmndSafePowerHold(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_power_safe_limit_hold = (1 == XdrvMailbox.payload) ? SAFE_POWER_HOLD : XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power_safe_limit_hold); -} - -void CmndSafePowerWindow(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 1440)) { - Settings.energy_max_power_safe_limit_window = (1 == XdrvMailbox.payload) ? SAFE_POWER_WINDOW : XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_power_safe_limit_window); -} - -void CmndMaxEnergy(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.energy_max_energy = XdrvMailbox.payload; - Energy.max_energy_state = 3; - } - ResponseCmndNumber(Settings.energy_max_energy); -} - -void CmndMaxEnergyStart(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 24)) { - Settings.energy_max_energy_start = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.energy_max_energy_start); -} -#endif -#endif - -void EnergySnsInit(void) -{ - XnrgCall(FUNC_INIT); - - if (energy_flg) { - if (RtcSettingsValid()) { - Energy.kWhtoday_offset = RtcSettings.energy_kWhtoday; - } - else if (RtcTime.day_of_year == Settings.energy_kWhdoy) { - Energy.kWhtoday_offset = Settings.energy_kWhtoday; - } - else { - Energy.kWhtoday_offset = 0; - } - Energy.kWhtoday = 0; - Energy.kWhtoday_delta = 0; - Energy.period = Energy.kWhtoday_offset; - EnergyUpdateToday(); - ticker_energy.attach_ms(200, Energy200ms); - } -} - -#ifdef USE_WEBSERVER -const char HTTP_ENERGY_SNS1[] PROGMEM = - "{s}" D_POWERUSAGE_APPARENT "{m}%s " D_UNIT_VA "{e}" - "{s}" D_POWERUSAGE_REACTIVE "{m}%s " D_UNIT_VAR "{e}" - "{s}" D_POWER_FACTOR "{m}%s{e}"; - -const char HTTP_ENERGY_SNS2[] PROGMEM = - "{s}" D_ENERGY_TODAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" - "{s}" D_ENERGY_YESTERDAY "{m}%s " D_UNIT_KILOWATTHOUR "{e}" - "{s}" D_ENERGY_TOTAL "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; - -const char HTTP_ENERGY_SNS3[] PROGMEM = - "{s}" D_EXPORT_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}"; -#endif - -char* EnergyFormatIndex(char* result, char* input, bool json, uint32_t index, bool single = false) -{ - char layout[16]; - GetTextIndexed(layout, sizeof(layout), (index -1) + (3 * json), kEnergyPhases); - switch (index) { - case 2: - snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ); - break; - case 3: - snprintf_P(result, FLOATSZ *3, layout, input, input + FLOATSZ, input + FLOATSZ + FLOATSZ); - break; - default: - snprintf_P(result, FLOATSZ *3, input); - } - return result; -} - -char* EnergyFormat(char* result, char* input, bool json, bool single = false) -{ - uint8_t index = (single) ? 1 : Energy.phase_count; - return EnergyFormatIndex(result, input, json, index, single); -} - -void EnergyShow(bool json) -{ - for (uint32_t i = 0; i < Energy.phase_count; i++) { - if (Energy.voltage_common) { - Energy.voltage[i] = Energy.voltage[0]; - } - } - - float power_factor_knx = Energy.power_factor[0]; - - char apparent_power_chr[Energy.phase_count][FLOATSZ]; - char reactive_power_chr[Energy.phase_count][FLOATSZ]; - char power_factor_chr[Energy.phase_count][FLOATSZ]; - char frequency_chr[Energy.phase_count][FLOATSZ]; - if (!Energy.type_dc) { - if (Energy.current_available && Energy.voltage_available) { - for (uint32_t i = 0; i < Energy.phase_count; i++) { - float apparent_power = Energy.apparent_power[i]; - if (isnan(apparent_power)) { - apparent_power = Energy.voltage[i] * Energy.current[i]; - } - if (apparent_power < Energy.active_power[i]) { - Energy.active_power[i] = apparent_power; - } - - float power_factor = Energy.power_factor[i]; - if (isnan(power_factor)) { - power_factor = (Energy.active_power[i] && apparent_power) ? Energy.active_power[i] / apparent_power : 0; - if (power_factor > 1) { - power_factor = 1; - } - } - if (0 == i) { power_factor_knx = power_factor; } - - float reactive_power = Energy.reactive_power[i]; - if (isnan(reactive_power)) { - reactive_power = 0; - uint32_t difference = ((uint32_t)(apparent_power * 100) - (uint32_t)(Energy.active_power[i] * 100)) / 10; - if ((Energy.current[i] > 0.005) && ((difference > 15) || (difference > (uint32_t)(apparent_power * 100 / 1000)))) { - - - reactive_power = (float)(RoundSqrtInt((uint32_t)(apparent_power * apparent_power * 100) - (uint32_t)(Energy.active_power[i] * Energy.active_power[i] * 100))) / 10; - } - } - - dtostrfd(apparent_power, Settings.flag2.wattage_resolution, apparent_power_chr[i]); - dtostrfd(reactive_power, Settings.flag2.wattage_resolution, reactive_power_chr[i]); - dtostrfd(power_factor, 2, power_factor_chr[i]); - } - } - for (uint32_t i = 0; i < Energy.phase_count; i++) { - float frequency = Energy.frequency[i]; - if (isnan(Energy.frequency[i])) { - frequency = 0; - } - dtostrfd(frequency, Settings.flag2.frequency_resolution, frequency_chr[i]); - } - } - - char voltage_chr[Energy.phase_count][FLOATSZ]; - char current_chr[Energy.phase_count][FLOATSZ]; - char active_power_chr[Energy.phase_count][FLOATSZ]; - for (uint32_t i = 0; i < Energy.phase_count; i++) { - dtostrfd(Energy.voltage[i], Settings.flag2.voltage_resolution, voltage_chr[i]); - dtostrfd(Energy.current[i], Settings.flag2.current_resolution, current_chr[i]); - dtostrfd(Energy.active_power[i], Settings.flag2.wattage_resolution, active_power_chr[i]); - } - - char energy_daily_chr[FLOATSZ]; - dtostrfd(Energy.daily, Settings.flag2.energy_resolution, energy_daily_chr); - char energy_yesterday_chr[FLOATSZ]; - dtostrfd((float)Settings.energy_kWhyesterday / 100000, Settings.flag2.energy_resolution, energy_yesterday_chr); - - char energy_total_chr[3][FLOATSZ]; - dtostrfd(Energy.total, Settings.flag2.energy_resolution, energy_total_chr[0]); - char export_active_chr[3][FLOATSZ]; - dtostrfd(Energy.export_active, Settings.flag2.energy_resolution, export_active_chr[0]); - uint8_t energy_total_fields = 1; - - if (Settings.tariff[0][0] != Settings.tariff[1][0]) { - dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_total_chr[1]); - dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100000, Settings.flag2.energy_resolution, energy_total_chr[2]); - dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100000, Settings.flag2.energy_resolution, export_active_chr[1]); - dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100000, Settings.flag2.energy_resolution, export_active_chr[2]); - energy_total_fields = 3; - } - - char value_chr[FLOATSZ *3]; - char value2_chr[FLOATSZ *3]; - char value3_chr[FLOATSZ *3]; - - if (json) { - bool show_energy_period = (0 == tele_period); - - ResponseAppend_P(PSTR(",\"" D_RSLT_ENERGY "\":{\"" D_JSON_TOTAL_START_TIME "\":\"%s\",\"" D_JSON_TOTAL "\":%s,\"" D_JSON_YESTERDAY "\":%s,\"" D_JSON_TODAY "\":%s"), - GetDateAndTime(DT_ENERGY).c_str(), - EnergyFormatIndex(value_chr, energy_total_chr[0], json, energy_total_fields), - energy_yesterday_chr, - energy_daily_chr); - - if (!isnan(Energy.export_active)) { - ResponseAppend_P(PSTR(",\"" D_JSON_EXPORT_ACTIVE "\":%s"), - EnergyFormatIndex(value_chr, export_active_chr[0], json, energy_total_fields)); - } - - if (show_energy_period) { - float energy = 0; - if (Energy.period) { - energy = (float)(RtcSettings.energy_kWhtoday - Energy.period) / 100; - } - Energy.period = RtcSettings.energy_kWhtoday; - char energy_period_chr[FLOATSZ]; - dtostrfd(energy, Settings.flag2.wattage_resolution, energy_period_chr); - ResponseAppend_P(PSTR(",\"" D_JSON_PERIOD "\":%s"), energy_period_chr); - } - ResponseAppend_P(PSTR(",\"" D_JSON_POWERUSAGE "\":%s"), - EnergyFormat(value_chr, active_power_chr[0], json)); - if (!Energy.type_dc) { - if (Energy.current_available && Energy.voltage_available) { - ResponseAppend_P(PSTR(",\"" D_JSON_APPARENT_POWERUSAGE "\":%s,\"" D_JSON_REACTIVE_POWERUSAGE "\":%s,\"" D_JSON_POWERFACTOR "\":%s"), - EnergyFormat(value_chr, apparent_power_chr[0], json), - EnergyFormat(value2_chr, reactive_power_chr[0], json), - EnergyFormat(value3_chr, power_factor_chr[0], json)); - } - if (!isnan(Energy.frequency[0])) { - ResponseAppend_P(PSTR(",\"" D_JSON_FREQUENCY "\":%s"), - EnergyFormat(value_chr, frequency_chr[0], json, Energy.voltage_common)); - } - } - if (Energy.voltage_available) { - ResponseAppend_P(PSTR(",\"" D_JSON_VOLTAGE "\":%s"), - EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); - } - if (Energy.current_available) { - ResponseAppend_P(PSTR(",\"" D_JSON_CURRENT "\":%s"), - EnergyFormat(value_chr, current_chr[0], json)); - } - XnrgCall(FUNC_JSON_APPEND); - ResponseJsonEnd(); - -#ifdef USE_DOMOTICZ - if (show_energy_period) { - dtostrfd(Energy.total * 1000, 1, energy_total_chr[0]); - DomoticzSensorPowerEnergy((int)Energy.active_power[0], energy_total_chr[0]); - - dtostrfd((float)RtcSettings.energy_usage.usage1_kWhtotal / 100, 1, energy_total_chr[1]); - dtostrfd((float)RtcSettings.energy_usage.usage2_kWhtotal / 100, 1, energy_total_chr[2]); - dtostrfd((float)RtcSettings.energy_usage.return1_kWhtotal / 100, 1, export_active_chr[1]); - dtostrfd((float)RtcSettings.energy_usage.return2_kWhtotal / 100, 1, export_active_chr[2]); - DomoticzSensorP1SmartMeter(energy_total_chr[1], energy_total_chr[2], export_active_chr[1], export_active_chr[2], (int)Energy.active_power[0]); - - if (Energy.voltage_available) { - DomoticzSensor(DZ_VOLTAGE, voltage_chr[0]); - } - if (Energy.current_available) { - DomoticzSensor(DZ_CURRENT, current_chr[0]); - } - } -#endif -#ifdef USE_KNX - if (show_energy_period) { - if (Energy.voltage_available) { - KnxSensor(KNX_ENERGY_VOLTAGE, Energy.voltage[0]); - } - if (Energy.current_available) { - KnxSensor(KNX_ENERGY_CURRENT, Energy.current[0]); - } - KnxSensor(KNX_ENERGY_POWER, Energy.active_power[0]); - if (!Energy.type_dc) { - KnxSensor(KNX_ENERGY_POWERFACTOR, power_factor_knx); - } - KnxSensor(KNX_ENERGY_DAILY, Energy.daily); - KnxSensor(KNX_ENERGY_TOTAL, Energy.total); - KnxSensor(KNX_ENERGY_START, Energy.start_energy); - } -#endif -#ifdef USE_WEBSERVER - } else { - if (Energy.voltage_available) { - WSContentSend_PD(HTTP_SNS_VOLTAGE, EnergyFormat(value_chr, voltage_chr[0], json, Energy.voltage_common)); - } - if (Energy.current_available) { - WSContentSend_PD(HTTP_SNS_CURRENT, EnergyFormat(value_chr, current_chr[0], json)); - } - WSContentSend_PD(HTTP_SNS_POWER, EnergyFormat(value_chr, active_power_chr[0], json)); - if (!Energy.type_dc) { - if (Energy.current_available && Energy.voltage_available) { - WSContentSend_PD(HTTP_ENERGY_SNS1, EnergyFormat(value_chr, apparent_power_chr[0], json), - EnergyFormat(value2_chr, reactive_power_chr[0], json), - EnergyFormat(value3_chr, power_factor_chr[0], json)); - } - if (!isnan(Energy.frequency[0])) { - WSContentSend_PD(PSTR("{s}" D_FREQUENCY "{m}%s " D_UNIT_HERTZ "{e}"), - EnergyFormat(value_chr, frequency_chr[0], json, Energy.voltage_common)); - } - } - WSContentSend_PD(HTTP_ENERGY_SNS2, energy_daily_chr, energy_yesterday_chr, energy_total_chr[0]); - if (!isnan(Energy.export_active)) { - WSContentSend_PD(HTTP_ENERGY_SNS3, export_active_chr[0]); - } - - XnrgCall(FUNC_WEB_SENSOR); -#endif - } -} - - - - - -bool Xdrv03(uint8_t function) -{ - bool result = false; - - if (FUNC_PRE_INIT == function) { - energy_flg = ENERGY_NONE; - XnrgCall(FUNC_PRE_INIT); - } - else if (energy_flg) { - switch (function) { - case FUNC_LOOP: - XnrgCall(FUNC_LOOP); - break; - case FUNC_EVERY_250_MSECOND: - XnrgCall(FUNC_EVERY_250_MSECOND); - break; - case FUNC_SERIAL: - result = XnrgCall(FUNC_SERIAL); - break; -#ifdef USE_ENERGY_MARGIN_DETECTION - case FUNC_SET_POWER: - Energy.power_steady_counter = 2; - break; -#endif - case FUNC_COMMAND: - result = DecodeCommand(kEnergyCommands, EnergyCommand); - break; - } - } - return result; -} - -bool Xsns03(uint8_t function) -{ - bool result = false; - - if (energy_flg) { - switch (function) { - case FUNC_EVERY_SECOND: - EnergyEverySecond(); - break; - case FUNC_JSON_APPEND: - EnergyShow(true); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - EnergyShow(false); - break; -#endif - case FUNC_SAVE_BEFORE_RESTART: - EnergySaveState(); - break; - case FUNC_INIT: - EnergySnsInit(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" -#ifdef USE_LIGHT -# 124 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" -#define XDRV_04 4 - - -enum LightSchemes { LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX }; - -const uint8_t LIGHT_COLOR_SIZE = 25; - -const char kLightCommands[] PROGMEM = "|" - D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_DIMMER_RANGE "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" - D_CMND_RGBWWTABLE "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" - D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR "|UNDOCA" ; - -void (* const LightCommand[])(void) PROGMEM = { - &CmndColor, &CmndColorTemperature, &CmndDimmer, &CmndDimmerRange, &CmndLedTable, &CmndFade, - &CmndRgbwwTable, &CmndScheme, &CmndSpeed, &CmndWakeup, &CmndWakeupDuration, - &CmndWhite, &CmndChannel, &CmndHsbColor, &CmndUndocA }; - - -enum LightColorModes { - LCM_RGB = 1, LCM_CT = 2, LCM_BOTH = 3 }; - -struct LRgbColor { - uint8_t R, G, B; -}; -const uint8_t MAX_FIXED_COLOR = 12; -const LRgbColor kFixedColor[MAX_FIXED_COLOR] PROGMEM = - { 255,0,0, 0,255,0, 0,0,255, 228,32,0, 0,228,32, 0,32,228, 188,64,0, 0,160,96, 160,32,240, 255,255,0, 255,0,170, 255,255,255 }; - -struct LWColor { - uint8_t W; -}; -const uint8_t MAX_FIXED_WHITE = 4; -const LWColor kFixedWhite[MAX_FIXED_WHITE] PROGMEM = { 0, 255, 128, 32 }; - -struct LCwColor { - uint8_t C, W; -}; -const uint8_t MAX_FIXED_COLD_WARM = 4; -const LCwColor kFixedColdWarm[MAX_FIXED_COLD_WARM] PROGMEM = { 0,0, 255,0, 0,255, 128,128 }; - - -const uint16_t CT_MIN = 153; -const uint16_t CT_MAX = 500; - -const uint16_t CT_MIN_ALEXA = 200; -const uint16_t CT_MAX_ALEXA = 380; - - - - - - -typedef struct gamma_table_t { - uint16_t to_src; - uint16_t to_gamma; -} gamma_table_t; - -const gamma_table_t gamma_table[] = { - { 1, 1 }, - { 4, 1 }, - { 209, 13 }, - { 312, 41 }, - { 457, 106 }, - { 626, 261 }, - { 762, 450 }, - { 895, 703 }, - { 1023, 1023 }, - { 0xFFFF, 0xFFFF } -}; - - -const gamma_table_t gamma_table_fast[] = { - { 384, 192 }, - { 768, 576 }, - { 1023, 1023 }, - { 0xFFFF, 0xFFFF } -}; -# 248 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" -struct LIGHT { - uint32_t strip_timer_counter = 0; - power_t power = 0; - - uint16_t wakeup_counter = 0; - - uint8_t entry_color[LST_MAX]; - uint8_t current_color[LST_MAX]; - uint8_t new_color[LST_MAX]; - uint8_t last_color[LST_MAX]; - uint8_t color_remap[LST_MAX]; - - uint8_t wheel = 0; - uint8_t random = 0; - uint8_t subtype = 0; - uint8_t device = 0; - uint8_t old_power = 1; - uint8_t wakeup_active = 0; - uint8_t wakeup_dimmer = 0; - uint8_t fixed_color_index = 1; - uint8_t pwm_offset = 0; - uint8_t max_scheme = LS_MAX -1; - - bool update = true; - bool pwm_multi_channels = false; - - bool fade_initialized = false; - bool fade_running = false; - uint16_t fade_start_10[LST_MAX] = {0,0,0,0,0}; - uint16_t fade_cur_10[LST_MAX]; - uint16_t fade_end_10[LST_MAX]; - uint16_t fade_duration = 0; - uint32_t fade_start = 0; -} Light; - -power_t LightPower(void) -{ - return Light.power; -} - - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 -power_t LightPowerIRAM(void) ICACHE_RAM_ATTR; -#endif - -power_t LightPowerIRAM(void) -{ - return Light.power; -} - -uint8_t LightDevice(void) -{ - return Light.device; -} - -static uint32_t min3(uint32_t a, uint32_t b, uint32_t c) { - return (a < b && a < c) ? a : (b < c) ? b : c; -} -# 344 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" -class LightStateClass { - private: - uint16_t _hue = 0; - uint8_t _sat = 255; - uint8_t _briRGB = 255; - - uint8_t _r = 255; - uint8_t _g = 255; - uint8_t _b = 255; - - uint8_t _subtype = 0; - uint16_t _ct = CT_MIN; - uint8_t _wc = 255; - uint8_t _ww = 0; - uint8_t _briCT = 255; - - uint8_t _color_mode = LCM_RGB; - - - - - - uint16_t _ct_min_range = CT_MIN; - uint16_t _ct_max_range = CT_MAX; - - public: - LightStateClass() { - - } - - void setSubType(uint8_t sub_type) { - _subtype = sub_type; - } -# 386 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" - uint8_t setColorMode(uint8_t cm) { - uint8_t prev_cm = _color_mode; - if (cm < LCM_RGB) { cm = LCM_RGB; } - if (cm > LCM_BOTH) { cm = LCM_BOTH; } - uint8_t maxbri = (_briRGB >= _briCT) ? _briRGB : _briCT; - - switch (_subtype) { - case LST_COLDWARM: - _color_mode = LCM_CT; - break; - - case LST_NONE: - case LST_SINGLE: - case LST_RGB: - default: - _color_mode = LCM_RGB; - break; - - case LST_RGBW: - case LST_RGBCW: - _color_mode = cm; - break; - } - if (LCM_RGB == _color_mode) { - _briCT = 0; - if (0 == _briRGB) { _briRGB = maxbri; } - } - if (LCM_CT == _color_mode) { - _briRGB = 0; - if (0 == _briCT) { _briCT = maxbri; } - } -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setColorMode prev_cm (%d) req_cm (%d) new_cm (%d)", prev_cm, cm, _color_mode); -#endif - return prev_cm; - } - - inline uint8_t getColorMode() { - return _color_mode; - } - - void addRGBMode() { - setColorMode(_color_mode | LCM_RGB); - } - void addCTMode() { - setColorMode(_color_mode | LCM_CT); - } - - - void getRGB(uint8_t *r, uint8_t *g, uint8_t *b) { - if (r) { *r = _r; } - if (g) { *g = _g; } - if (b) { *b = _b; } - } - - - - void getCW(uint8_t *rc, uint8_t *rw) { - if (rc) { *rc = _wc; } - if (rw) { *rw = _ww; } - } - - - void getActualRGBCW(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *c, uint8_t *w) { - bool rgb_channels_on = _color_mode & LCM_RGB; - bool ct_channels_on = _color_mode & LCM_CT; - - if (r) { *r = rgb_channels_on ? changeUIntScale(_r, 0, 255, 0, _briRGB) : 0; } - if (g) { *g = rgb_channels_on ? changeUIntScale(_g, 0, 255, 0, _briRGB) : 0; } - if (b) { *b = rgb_channels_on ? changeUIntScale(_b, 0, 255, 0, _briRGB) : 0; } - - if (c) { *c = ct_channels_on ? changeUIntScale(_wc, 0, 255, 0, _briCT) : 0; } - if (w) { *w = ct_channels_on ? changeUIntScale(_ww, 0, 255, 0, _briCT) : 0; } - } - - uint8_t getChannels(uint8_t *channels) { - getActualRGBCW(&channels[0], &channels[1], &channels[2], &channels[3], &channels[4]); - } - - void getChannelsRaw(uint8_t *channels) { - channels[0] = _r; - channels[1] = _g; - channels[2] = _b; - channels[3] = _wc; - channels[4] = _ww; - } - - void getHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) { - if (hue) { *hue = _hue; } - if (sat) { *sat = _sat; } - if (bri) { *bri = _briRGB; } - } - - - uint8_t getBri(void) { - - return (_briRGB >= _briCT) ? _briRGB : _briCT; - } - - - inline uint8_t getBriCT() { - return _briCT; - } - - static inline uint8_t DimmerToBri(uint8_t dimmer) { - return changeUIntScale(dimmer, 0, 100, 0, 255); - } - static uint8_t BriToDimmer(uint8_t bri) { - uint8_t dimmer = changeUIntScale(bri, 0, 255, 0, 100); - - if ((dimmer == 0) && (bri > 0)) { dimmer = 1; } - return dimmer; - } - - uint8_t getDimmer(uint32_t mode = 0) { - uint8_t bri; - switch (mode) { - case 1: - bri = getBriRGB(); - break; - case 2: - bri = getBriCT(); - break; - default: - bri = getBri(); - break; - } - return BriToDimmer(bri); - } - - inline uint16_t getCT() const { - return _ct; - } - - - uint16_t getCT10bits() const { - return changeUIntScale(_ct, _ct_min_range, _ct_max_range, 0, 1023); - } - - inline void setCTRange(uint16_t ct_min_range, uint16_t ct_max_range) { - _ct_min_range = ct_min_range; - _ct_max_range = ct_max_range; - } - - inline void getCTRange(uint16_t *ct_min_range, uint16_t *ct_max_range) const { - if (ct_min_range) { *ct_min_range = _ct_min_range; } - if (ct_max_range) { *ct_max_range = _ct_max_range; } - } - - - void getXY(float *x, float *y) { - RgbToXy(_r, _g, _b, x, y); - } - - - - void setBri(uint8_t bri) { - setBriRGB(_color_mode & LCM_RGB ? bri : 0); - setBriCT(_color_mode & LCM_CT ? bri : 0); -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setBri RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); -#endif - } - - - uint8_t setBriRGB(uint8_t bri_rgb) { - uint8_t prev_bri = _briRGB; - _briRGB = bri_rgb; - if (bri_rgb > 0) { addRGBMode(); } - return prev_bri; - } - - - uint8_t setBriCT(uint8_t bri_ct) { - uint8_t prev_bri = _briCT; - _briCT = bri_ct; - if (bri_ct > 0) { addCTMode(); } - return prev_bri; - } - - inline uint8_t getBriRGB() { - return _briRGB; - } - - void setDimmer(uint8_t dimmer) { - setBri(DimmerToBri(dimmer)); - } - - void setCT(uint16_t ct) { - if (0 == ct) { - - setColorMode(LCM_RGB); - } else { - ct = (ct < CT_MIN ? CT_MIN : (ct > CT_MAX ? CT_MAX : ct)); - _ww = changeUIntScale(ct, _ct_min_range, _ct_max_range, 0, 255); - _wc = 255 - _ww; - _ct = ct; - addCTMode(); - } -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCT RGB raw (%d %d %d) HS (%d %d) briRGB (%d) briCT (%d) CT (%d)", _r, _g, _b, _hue, _sat, _briRGB, _briCT, _ct); -#endif - } -# 604 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" - void setCW(uint8_t c, uint8_t w, bool free_range = false) { - uint16_t max = (w > c) ? w : c; - uint16_t sum = c + w; - - if (0 == max) { - _briCT = 0; - setColorMode(LCM_RGB); - } else { - if (!free_range) { - - _ww = changeUIntScale(w, 0, sum, 0, 255); - _wc = 255 - _ww; - } else { - _ww = changeUIntScale(w, 0, max, 0, 255); - _wc = changeUIntScale(c, 0, max, 0, 255); - } - _ct = changeUIntScale(w, 0, sum, _ct_min_range, _ct_max_range); - addCTMode(); - if (_color_mode & LCM_CT) { _briCT = free_range ? max : (sum > 255 ? 255 : sum); } - } -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setCW CW (%d %d) CT (%d) briCT (%d)", c, w, _ct, _briCT); -#endif - } - - - uint8_t setRGB(uint8_t r, uint8_t g, uint8_t b, bool keep_bri = false) { - uint16_t hue; - uint8_t sat; -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB input (%d %d %d)", r, g, b); -#endif - - uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; - - if (0 == max) { - r = g = b = 255; - setColorMode(LCM_CT); - } else { - if (255 > max) { - - r = changeUIntScale(r, 0, max, 0, 255); - g = changeUIntScale(g, 0, max, 0, 255); - b = changeUIntScale(b, 0, max, 0, 255); - } - addRGBMode(); - } - if (!keep_bri) { - _briRGB = (_color_mode & LCM_RGB) ? max : 0; - } - - RgbToHsb(r, g, b, &hue, &sat, nullptr); - _r = r; - _g = g; - _b = b; - _hue = hue; - _sat = sat; -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setRGB RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); -#endif - return max; - } - - void setHS(uint16_t hue, uint8_t sat) { - uint8_t r, g, b; - HsToRgb(hue, sat, &r, &g, &b); - _r = r; - _g = g; - _b = b; - _hue = hue; - _sat = sat; - addRGBMode(); -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS HS (%d %d) rgb (%d %d %d)", hue, sat, r, g, b); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setHS RGB raw (%d %d %d) HS (%d %d) bri (%d)", _r, _g, _b, _hue, _sat, _briRGB); -#endif - } - - - - void setChannelsRaw(uint8_t *channels) { - _r = channels[0]; - _g = channels[1]; - _b = channels[2]; - _wc = channels[3]; - _ww = channels[4]; -} - - - - - void setChannels(uint8_t *channels) { - setRGB(channels[0], channels[1], channels[2]); - setCW(channels[3], channels[4], true); -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels (%d %d %d %d %d)", - channels[0], channels[1], channels[2], channels[3], channels[4]); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightStateClass::setChannels CT (%d) briRGB (%d) briCT (%d)", _ct, _briRGB, _briCT); -#endif - } - - - static void RgbToHsb(uint8_t r, uint8_t g, uint8_t b, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri); - static void HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b); - static void RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y); - static void XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb); - -}; -# 720 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_04_light.ino" -void LightStateClass::RgbToHsb(uint8_t ir, uint8_t ig, uint8_t ib, uint16_t *r_hue, uint8_t *r_sat, uint8_t *r_bri) { - uint32_t r = ir; - uint32_t g = ig; - uint32_t b = ib; - uint32_t max = (r > g && r > b) ? r : (g > b) ? g : b; - uint32_t min = (r < g && r < b) ? r : (g < b) ? g : b; - uint32_t d = max - min; - - uint16_t hue = 0; - uint8_t sat = 0; - uint8_t bri = max; - - if (d != 0) { - sat = changeUIntScale(d, 0, max, 0, 255); - if (r == max) { - hue = (g > b) ? changeUIntScale(g-b,0,d,0,60) : 360 - changeUIntScale(b-g,0,d,0,60); - } else if (g == max) { - hue = (b > r) ? 120 + changeUIntScale(b-r,0,d,0,60) : 120 - changeUIntScale(r-b,0,d,0,60); - } else { - hue = (r > g) ? 240 + changeUIntScale(r-g,0,d,0,60) : 240 - changeUIntScale(g-r,0,d,0,60); - } - hue = hue % 360; - } - - if (r_hue) *r_hue = hue; - if (r_sat) *r_sat = sat; - if (r_bri) *r_bri = bri; - -} - -void LightStateClass::HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { - uint32_t r = 255; - uint32_t g = 255; - uint32_t b = 255; - - hue = hue % 360; - - if (sat > 0) { - uint32_t i = hue / 60; - uint32_t f = hue % 60; - uint32_t q = 255 - changeUIntScale(f, 0, 60, 0, sat); - uint32_t p = 255 - sat; - uint32_t t = 255 - changeUIntScale(60 - f, 0, 60, 0, sat); - - switch (i) { - case 0: - - g = t; - b = p; - break; - case 1: - r = q; - - b = p; - break; - case 2: - r = p; - - b = t; - break; - case 3: - r = p; - g = q; - - break; - case 4: - r = t; - g = p; - - break; - default: - - g = p; - b = q; - break; - } - } - if (r_r) *r_r = r; - if (r_g) *r_g = g; - if (r_b) *r_b = b; -} - -#define POW FastPrecisePowf - -void LightStateClass::RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y) { - float x = 0.31271f; - float y = 0.32902f; - - if (i_r + i_b + i_g > 0) { - float r = (float)i_r / 255.0f; - float g = (float)i_g / 255.0f; - float b = (float)i_b / 255.0f; - - - r = (r > 0.04045f) ? POW((r + 0.055f) / (1.0f + 0.055f), 2.4f) : (r / 12.92f); - g = (g > 0.04045f) ? POW((g + 0.055f) / (1.0f + 0.055f), 2.4f) : (g / 12.92f); - b = (b > 0.04045f) ? POW((b + 0.055f) / (1.0f + 0.055f), 2.4f) : (b / 12.92f); - - - - float X = r * 0.649926f + g * 0.103455f + b * 0.197109f; - float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; - float Z = r * 0.000000f + g * 0.053077f + b * 1.035763f; - - x = X / (X + Y + Z); - y = Y / (X + Y + Z); - - } - if (r_x) *r_x = x; - if (r_y) *r_y = y; -} - -void LightStateClass::XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb) -{ - x = (x > 0.99f ? 0.99f : (x < 0.01f ? 0.01f : x)); - y = (y > 0.99f ? 0.99f : (y < 0.01f ? 0.01f : y)); - float z = 1.0f - x - y; - - float X = x / y; - float Z = z / y; - - - - float r = X * 3.2406f - 1.5372f - Z * 0.4986f; - float g = -X * 0.9689f + 1.8758f + Z * 0.0415f; - float b = X * 0.0557f - 0.2040f + Z * 1.0570f; - float max = (r > g && r > b) ? r : (g > b) ? g : b; - r = r / max; - g = g / max; - b = b / max; - r = (r <= 0.0031308f) ? 12.92f * r : 1.055f * POW(r, (1.0f / 2.4f)) - 0.055f; - g = (g <= 0.0031308f) ? 12.92f * g : 1.055f * POW(g, (1.0f / 2.4f)) - 0.055f; - b = (b <= 0.0031308f) ? 12.92f * b : 1.055f * POW(b, (1.0f / 2.4f)) - 0.055f; - - - - - - int32_t ir = r * 255.0f + 0.5f; - int32_t ig = g * 255.0f + 0.5f; - int32_t ib = b * 255.0f + 0.5f; - if (rr) { *rr = (ir > 255 ? 255: (ir < 0 ? 0 : ir)); } - if (rg) { *rg = (ig > 255 ? 255: (ig < 0 ? 0 : ig)); } - if (rb) { *rb = (ib > 255 ? 255: (ib < 0 ? 0 : ib)); } -} - -class LightControllerClass { -private: - LightStateClass *_state; - - - bool _ct_rgb_linked = true; - bool _pwm_multi_channels = false; - -public: - LightControllerClass(LightStateClass& state) { - _state = &state; - } - - void setSubType(uint8_t sub_type) { - _state->setSubType(sub_type); - } - - inline bool setCTRGBLinked(bool ct_rgb_linked) { - bool prev = _ct_rgb_linked; - if (_pwm_multi_channels) { - _ct_rgb_linked = false; - } else { - _ct_rgb_linked = ct_rgb_linked; - } - return prev; - } - - void setAlexaCTRange(bool alexa_ct_range) { - - if (alexa_ct_range) { - _state->setCTRange(CT_MIN_ALEXA, CT_MAX_ALEXA); - } else { - _state->setCTRange(CT_MIN, CT_MAX); - } - } - - inline bool isCTRGBLinked() { - return _ct_rgb_linked; - } - - inline bool setPWMMultiChannel(bool pwm_multi_channels) { - bool prev = _pwm_multi_channels; - _pwm_multi_channels = pwm_multi_channels; - if (pwm_multi_channels) setCTRGBLinked(false); - return prev; - } - - inline bool isPWMMultiChannel(void) { - return _pwm_multi_channels; - } - -#ifdef DEBUG_LIGHT - void debugLogs() { - uint8_t r,g,b,c,w; - _state->getActualRGBCW(&r,&g,&b,&c,&w); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs rgb (%d %d %d) cw (%d %d)", - r, g, b, c, w); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::debugLogs lightCurrent (%d %d %d %d %d)", - Light.current_color[0], Light.current_color[1], Light.current_color[2], - Light.current_color[3], Light.current_color[4]); - } -#endif - - void loadSettings() { -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings Settings.light_color (%d %d %d %d %d - %d)", - Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], - Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings light_type/sub (%d %d)", - light_type, Light.subtype); -#endif - if (_pwm_multi_channels) { - _state->setChannelsRaw(Settings.light_color); - } else { - - _state->setCW(Settings.light_color[3], Settings.light_color[4], true); - _state->setRGB(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]); - - - - uint8_t bri = _state->DimmerToBri(Settings.light_dimmer); - if (Settings.light_color[0] + Settings.light_color[1] + Settings.light_color[2] > 0) { - - - if ( (DEFAULT_LIGHT_COMPONENT == Settings.light_color[0]) && - (DEFAULT_LIGHT_COMPONENT == Settings.light_color[1]) && - (DEFAULT_LIGHT_COMPONENT == Settings.light_color[2]) && - (DEFAULT_LIGHT_COMPONENT == Settings.light_color[3]) && - (DEFAULT_LIGHT_COMPONENT == Settings.light_color[4]) && - (DEFAULT_LIGHT_DIMMER == Settings.light_dimmer) ) { - _state->setColorMode(LCM_RGB); - } - _state->setBriRGB(bri); - } else { - _state->setBriCT(bri); - } - } - } - - void changeCTB(uint16_t new_ct, uint8_t briCT) { - - - - - - - if ((LST_COLDWARM != Light.subtype) && (LST_RGBW > Light.subtype)) { - return; - } - _state->setCT(new_ct); - _state->setBriCT(briCT); - if (_ct_rgb_linked) { _state->setColorMode(LCM_CT); } - saveSettings(); - calcLevels(); - - } - - void changeDimmer(uint8_t dimmer, uint32_t mode = 0) { - uint8_t bri = changeUIntScale(dimmer, 0, 100, 0, 255); - switch (mode) { - case 1: - changeBriRGB(bri); - if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } - break; - case 2: - changeBriCT(bri); - if (_ct_rgb_linked) { _state->setColorMode(LCM_CT); } - break; - default: - changeBri(bri); - break; - } - } - - void changeBri(uint8_t bri) { - _state->setBri(bri); - saveSettings(); - calcLevels(); - } - - void changeBriRGB(uint8_t bri) { - _state->setBriRGB(bri); - saveSettings(); - calcLevels(); - } - - void changeBriCT(uint8_t bri) { - _state->setBriCT(bri); - saveSettings(); - calcLevels(); - } - - void changeRGB(uint8_t r, uint8_t g, uint8_t b, bool keep_bri = false) { - _state->setRGB(r, g, b, keep_bri); - if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } - saveSettings(); - calcLevels(); - } - - - - void calcLevels(uint8_t *current_color = nullptr) { - uint8_t r,g,b,c,w,briRGB,briCT; - if (current_color == nullptr) { current_color = Light.current_color; } - - if (_pwm_multi_channels) { - _state->getChannelsRaw(current_color); - return; - } - - _state->getActualRGBCW(&r,&g,&b,&c,&w); - briRGB = _state->getBriRGB(); - briCT = _state->getBriCT(); - - current_color[0] = current_color[1] = current_color[2] = 0; - current_color[3] = current_color[4] = 0; - switch (Light.subtype) { - case LST_NONE: - current_color[0] = 255; - break; - case LST_SINGLE: - current_color[0] = briRGB; - break; - case LST_COLDWARM: - current_color[0] = c; - current_color[1] = w; - break; - case LST_RGBW: - case LST_RGBCW: - if (LST_RGBCW == Light.subtype) { - current_color[3] = c; - current_color[4] = w; - } else { - current_color[3] = briCT; - } - - case LST_RGB: - current_color[0] = r; - current_color[1] = g; - current_color[2] = b; - break; - } - } - - void changeHSB(uint16_t hue, uint8_t sat, uint8_t briRGB) { - _state->setHS(hue, sat); - _state->setBriRGB(briRGB); - if (_ct_rgb_linked) { _state->setColorMode(LCM_RGB); } - saveSettings(); - calcLevels(); - } - - - void saveSettings() { - if (Light.pwm_multi_channels) { - - _state->getChannelsRaw(Settings.light_color); - Settings.light_dimmer = 100; - } else { - uint8_t cm = _state->getColorMode(); - - memset(&Settings.light_color[0], 0, sizeof(Settings.light_color)); - if (LCM_RGB & cm) { - _state->getRGB(&Settings.light_color[0], &Settings.light_color[1], &Settings.light_color[2]); - Settings.light_dimmer = _state->BriToDimmer(_state->getBriRGB()); - - if (LCM_BOTH == cm) { - - _state->getActualRGBCW(nullptr, nullptr, nullptr, &Settings.light_color[3], &Settings.light_color[4]); - } - } else if (LCM_CT == cm) { - _state->getCW(&Settings.light_color[3], &Settings.light_color[4]); - Settings.light_dimmer = _state->BriToDimmer(_state->getBriCT()); - } - } -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::saveSettings Settings.light_color (%d %d %d %d %d - %d)", - Settings.light_color[0], Settings.light_color[1], Settings.light_color[2], - Settings.light_color[3], Settings.light_color[4], Settings.light_dimmer); -#endif - } - - - - - void changeChannels(uint8_t *channels) { - if (Light.pwm_multi_channels) { - _state->setChannelsRaw(channels); - } else if (LST_COLDWARM == Light.subtype) { - - uint8_t remapped_channels[5] = {0,0,0,channels[0],channels[1]}; - _state->setChannels(remapped_channels); - } else { - _state->setChannels(channels); - } - - saveSettings(); - calcLevels(); - } -}; - - - -LightStateClass light_state = LightStateClass(); -LightControllerClass light_controller = LightControllerClass(light_state); - - - - - -uint16_t change8to10(uint8_t v) { - return changeUIntScale(v, 0, 255, 0, 1023); -} - -uint8_t change10to8(uint16_t v) { - return (0 == v) ? 0 : changeUIntScale(v, 4, 1023, 1, 255); -} - - - - - -uint16_t ledGamma_internal(uint16_t v, const struct gamma_table_t *gt_ptr) { - uint16_t from_src = 0; - uint16_t from_gamma = 0; - - for (const gamma_table_t *gt = gt_ptr; ; gt++) { - uint16_t to_src = gt->to_src; - uint16_t to_gamma = gt->to_gamma; - if (v <= to_src) { - return changeUIntScale(v, from_src, to_src, from_gamma, to_gamma); - } - from_src = to_src; - from_gamma = to_gamma; - } -} - -uint16_t ledGammaReverse_internal(uint16_t vg, const struct gamma_table_t *gt_ptr) { - uint16_t from_src = 0; - uint16_t from_gamma = 0; - - for (const gamma_table_t *gt = gt_ptr; ; gt++) { - uint16_t to_src = gt->to_src; - uint16_t to_gamma = gt->to_gamma; - if (vg <= to_gamma) { - return changeUIntScale(vg, from_gamma, to_gamma, from_src, to_src); - } - from_src = to_src; - from_gamma = to_gamma; - } -} - - -uint16_t ledGamma10_10(uint16_t v) { - return ledGamma_internal(v, gamma_table); -} - -uint16_t ledGamma10(uint8_t v) { - return ledGamma10_10(change8to10(v)); -} - - -uint8_t ledGamma(uint8_t v) { - return change10to8(ledGamma10(v)); -} - - - -void LightPwmOffset(uint32_t offset) -{ - Light.pwm_offset = offset; -} - -bool LightModuleInit(void) -{ - light_type = LT_BASIC; - - if (Settings.flag.pwm_control) { - for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 +i] < 99) { light_type++; } - } - } - - light_flg = 0; - if (XlgtCall(FUNC_MODULE_INIT)) { - - } - else if (SONOFF_BN == my_module_type) { - light_type = LT_PWM1; - } - else if (SONOFF_LED == my_module_type) { - if (!my_module.io[4]) { - pinMode(4, OUTPUT); - digitalWrite(4, LOW); - } - if (!my_module.io[5]) { - pinMode(5, OUTPUT); - digitalWrite(5, LOW); - } - if (!my_module.io[14]) { - pinMode(14, OUTPUT); - digitalWrite(14, LOW); - } - light_type = LT_PWM2; - } - - if (light_type > LT_BASIC) { - devices_present++; - } - - - if (Settings.flag3.pwm_multi_channels) { - uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); - if (0 == pwm_channels) { pwm_channels = 1; } - devices_present += pwm_channels - 1; - } else if ((Settings.param[P_RGB_REMAP] & 128) && (LST_RGBW <= (light_type & 7))) { - - devices_present++; - } - - return (light_type > LT_BASIC); -} - -void LightInit(void) -{ - Light.device = devices_present; - Light.subtype = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); - Light.pwm_multi_channels = Settings.flag3.pwm_multi_channels; - - if (LST_RGBW <= Light.subtype) { - - - bool ct_rgb_linked = !(Settings.param[P_RGB_REMAP] & 128); - light_controller.setCTRGBLinked(ct_rgb_linked); - } - - if ((LST_SINGLE <= Light.subtype) && Light.pwm_multi_channels) { - - light_controller.setPWMMultiChannel(true); - Light.device = devices_present - Light.subtype + 1; - } else if (!light_controller.isCTRGBLinked()) { - - Light.device--; - } -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightInit Light.pwm_multi_channels=%d Light.subtype=%d Light.device=%d devices_present=%d", - Light.pwm_multi_channels, Light.subtype, Light.device, devices_present); -#endif - - light_controller.setSubType(Light.subtype); - light_controller.loadSettings(); - light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); - - if (LST_SINGLE == Light.subtype) { - Settings.light_color[0] = 255; - } - if (light_type < LT_PWM6) { - for (uint32_t i = 0; i < light_type; i++) { - Settings.pwm_value[i] = 0; - if (pin[GPIO_PWM1 +i] < 99) { - pinMode(pin[GPIO_PWM1 +i], OUTPUT); - } - } - if (pin[GPIO_ARIRFRCV] < 99) { - if (pin[GPIO_ARIRFSEL] < 99) { - pinMode(pin[GPIO_ARIRFSEL], OUTPUT); - digitalWrite(pin[GPIO_ARIRFSEL], 1); - } - } - } - - uint32_t max_scheme = Light.max_scheme; - if (Light.subtype < LST_RGB) { - max_scheme = LS_POWER; - } - if ((LS_WAKEUP == Settings.light_scheme) || (Settings.light_scheme > max_scheme)) { - Settings.light_scheme = LS_POWER; - } - Light.power = 0; - Light.update = true; - Light.wakeup_active = 0; - - LightUpdateColorMapping(); -} - -void LightUpdateColorMapping(void) -{ - uint8_t param = Settings.param[P_RGB_REMAP] & 127; - if (param > 119){ param = 0; } - - uint8_t tmp[] = {0,1,2,3,4}; - Light.color_remap[0] = tmp[param / 24]; - for (uint32_t i = param / 24; i<4; ++i){ - tmp[i] = tmp[i+1]; - } - param = param % 24; - Light.color_remap[1] = tmp[(param / 6)]; - for (uint32_t i = param / 6; i<3; ++i){ - tmp[i] = tmp[i+1]; - } - param = param % 6; - Light.color_remap[2] = tmp[(param / 2)]; - for (uint32_t i = param / 2; i<2; ++i){ - tmp[i] = tmp[i+1]; - } - param = param % 2; - Light.color_remap[3] = tmp[param]; - Light.color_remap[4] = tmp[1-param]; - - Light.update = true; - -} - -uint8_t LightGetDimmer(uint8_t dimmer) { - return light_state.getDimmer(dimmer); -} - -void LightSetDimmer(uint8_t dimmer) { - light_controller.changeDimmer(dimmer); -} - -void LightGetHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) { - light_state.getHSB(hue, sat, bri); -} - -void LightHsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *r_g, uint8_t *r_b) { - light_state.HsToRgb(hue, sat, r_r, r_g, r_b); -} - - -uint8_t LightGetBri(uint8_t device) { - uint8_t bri = 254; - if (Light.pwm_multi_channels) { - if ((device >= Light.device) && (device < Light.device + LST_MAX) && (device <= devices_present)) { - bri = Light.current_color[device - Light.device]; - } - } else if (light_controller.isCTRGBLinked()) { - if (device == Light.device) { - bri = light_state.getBri(); - } - } else { - if (device == Light.device) { - bri = light_state.getBriRGB(); - } else if (device == Light.device + 1) { - bri = light_state.getBriCT(); - } - } - return bri; -} - - -void LightSetBri(uint8_t device, uint8_t bri) { - if (Light.pwm_multi_channels) { - if ((device >= Light.device) && (device < Light.device + LST_MAX) && (device <= devices_present)) { - Light.current_color[device - Light.device] = bri; - light_controller.changeChannels(Light.current_color); - } - } else if (light_controller.isCTRGBLinked()) { - if (device == Light.device) { - light_controller.changeBri(bri); - } - } else { - if (device == Light.device) { - light_controller.changeBriRGB(bri); - } else if (device == Light.device + 1) { - light_controller.changeBriCT(bri); - } - } -} - -void LightSetColorTemp(uint16_t ct) -{ - - - - - - - if ((LST_COLDWARM != Light.subtype) && (LST_RGBCW != Light.subtype)) { - return; - } - light_controller.changeCTB(ct, light_state.getBriCT()); -} - -uint16_t LightGetColorTemp(void) -{ - - if ((LST_COLDWARM != Light.subtype) && (LST_RGBCW != Light.subtype)) { - return 0; - } - return (light_state.getColorMode() & LCM_CT) ? light_state.getCT() : 0; -} - -void LightSetSignal(uint16_t lo, uint16_t hi, uint16_t value) -{ - - - - if (Settings.flag.light_signal) { - uint16_t signal = changeUIntScale(value, lo, hi, 0, 255); - - light_controller.changeRGB(signal, 255 - signal, 0, true); - Settings.light_scheme = 0; - if (0 == light_state.getBri()) { - light_controller.changeBri(50); - } - } -} - - -char* LightGetColor(char* scolor, boolean force_hex = false) -{ - light_controller.calcLevels(); - scolor[0] = '\0'; - for (uint32_t i = 0; i < Light.subtype; i++) { - if (!force_hex && Settings.flag.decimal_text) { - snprintf_P(scolor, LIGHT_COLOR_SIZE, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Light.current_color[i]); - } else { - snprintf_P(scolor, LIGHT_COLOR_SIZE, PSTR("%s%02X"), scolor, Light.current_color[i]); - } - } - return scolor; -} - -void LightPowerOn(void) -{ - if (light_state.getBri() && !(Light.power)) { - ExecuteCommandPower(Light.device, POWER_ON, SRC_LIGHT); - } -} - -void LightState(uint8_t append) -{ - char scolor[LIGHT_COLOR_SIZE]; - char scommand[33]; - bool unlinked = !light_controller.isCTRGBLinked() && (Light.subtype >= LST_RGBW); - - if (append) { - ResponseAppend_P(PSTR(",")); - } else { - Response_P(PSTR("{")); - } - if (!Light.pwm_multi_channels) { - if (unlinked) { - - ResponseAppend_P(PSTR("\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "%d\":%d" - ",\"" D_RSLT_POWER "%d\":\"%s\",\"" D_CMND_DIMMER "%d\":%d"), - Light.device, GetStateText(Light.power & 1), Light.device, light_state.getDimmer(1), - Light.device + 1, GetStateText(Light.power & 2 ? 1 : 0), Light.device + 1, light_state.getDimmer(2)); - } else { - GetPowerDevice(scommand, Light.device, sizeof(scommand), Settings.flag.device_index_enable); - ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_DIMMER "\":%d"), scommand, GetStateText(Light.power & 1), - light_state.getDimmer()); - } - - - if (Light.subtype > LST_SINGLE) { - ResponseAppend_P(PSTR(",\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); - if (LST_RGB <= Light.subtype) { - uint16_t hue; - uint8_t sat, bri; - light_state.getHSB(&hue, &sat, &bri); - sat = changeUIntScale(sat, 0, 255, 0, 100); - bri = changeUIntScale(bri, 0, 255, 0, 100); - - ResponseAppend_P(PSTR(",\"" D_CMND_HSBCOLOR "\":\"%d,%d,%d\""), hue,sat,bri); - } - - if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) { - ResponseAppend_P(PSTR(",\"" D_CMND_WHITE "\":%d"), light_state.getDimmer(2)); - } - - if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { - ResponseAppend_P(PSTR(",\"" D_CMND_COLORTEMPERATURE "\":%d"), light_state.getCT()); - } - - ResponseAppend_P(PSTR(",\"" D_CMND_CHANNEL "\":[" )); - for (uint32_t i = 0; i < Light.subtype; i++) { - uint8_t channel_raw = Light.current_color[i]; - uint8_t channel = changeUIntScale(channel_raw,0,255,0,100); - - if ((0 == channel) && (channel_raw > 0)) { channel = 1; } - ResponseAppend_P(PSTR("%s%d" ), (i > 0 ? "," : ""), channel); - } - ResponseAppend_P(PSTR("]")); - } - - if (append) { - if (Light.subtype >= LST_RGB) { - ResponseAppend_P(PSTR(",\"" D_CMND_SCHEME "\":%d"), Settings.light_scheme); - } - if (Light.max_scheme > LS_MAX) { - ResponseAppend_P(PSTR(",\"" D_CMND_WIDTH "\":%d"), Settings.light_width); - } - ResponseAppend_P(PSTR(",\"" D_CMND_FADE "\":\"%s\",\"" D_CMND_SPEED "\":%d,\"" D_CMND_LEDTABLE "\":\"%s\""), - GetStateText(Settings.light_fade), Settings.light_speed, GetStateText(Settings.light_correction)); - } - } else { - for (uint32_t i = 0; i < Light.subtype; i++) { - GetPowerDevice(scommand, Light.device + i, sizeof(scommand), 1); - uint32_t light_power_masked = Light.power & (1 << i); - light_power_masked = light_power_masked ? 1 : 0; - ResponseAppend_P(PSTR("\"%s\":\"%s\",\"" D_CMND_CHANNEL "%d\":%d,"), scommand, GetStateText(light_power_masked), Light.device + i, - changeUIntScale(Light.current_color[i], 0, 255, 0, 100)); - } - ResponseAppend_P(PSTR("\"" D_CMND_COLOR "\":\"%s\""), LightGetColor(scolor)); - } - - if (!append) { - ResponseJsonEnd(); - } -} - -void LightPreparePower(power_t channels = 0xFFFFFFFF) { -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower power=%d Light.power=%d", power, Light.power); -#endif - - if (Light.pwm_multi_channels) { - for (uint32_t i = 0; i < Light.subtype; i++) { - if (bitRead(channels, i)) { - - if ((Light.current_color[i]) && (!bitRead(Light.power, i))) { - if (!Settings.flag.not_power_linked) { - ExecuteCommandPower(Light.device + i, POWER_ON_NO_STATE, SRC_LIGHT); - } - } else { - - if ((0 == Light.current_color[i]) && bitRead(Light.power, i)) { - ExecuteCommandPower(Light.device + i, POWER_OFF_NO_STATE, SRC_LIGHT); - } - } - #ifdef USE_DOMOTICZ - DomoticzUpdatePowerState(Light.device + i); - #endif - } - } - } else { - if (light_controller.isCTRGBLinked()) { - if (light_state.getBri() && !(Light.power)) { - if (!Settings.flag.not_power_linked) { - ExecuteCommandPower(Light.device, POWER_ON_NO_STATE, SRC_LIGHT); - } - } else if (!light_state.getBri() && Light.power) { - ExecuteCommandPower(Light.device, POWER_OFF_NO_STATE, SRC_LIGHT); - } - } else { - - if (channels & 1) { - if (light_state.getBriRGB() && !(Light.power & 1)) { - if (!Settings.flag.not_power_linked) { - ExecuteCommandPower(Light.device, POWER_ON_NO_STATE, SRC_LIGHT); - } - } else if (!light_state.getBriRGB() && (Light.power & 1)) { - ExecuteCommandPower(Light.device, POWER_OFF_NO_STATE, SRC_LIGHT); - } - } - - if (channels & 2) { - if (light_state.getBriCT() && !(Light.power & 2)) { - if (!Settings.flag.not_power_linked) { - ExecuteCommandPower(Light.device + 1, POWER_ON_NO_STATE, SRC_LIGHT); - } - } else if (!light_state.getBriCT() && (Light.power & 2)) { - ExecuteCommandPower(Light.device + 1, POWER_OFF_NO_STATE, SRC_LIGHT); - } - } - } -#ifdef USE_DOMOTICZ - DomoticzUpdatePowerState(Light.device); -#endif - } - - if (Settings.flag3.hass_tele_on_power) { - MqttPublishTeleState(); - } - -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG, "LightPreparePower End power=%d Light.power=%d", power, Light.power); -#endif - Light.power = power >> (Light.device - 1); - LightState(0); -} - -void LightCycleColor(int8_t direction) -{ - if (Light.strip_timer_counter % (Settings.light_speed * 2)) { - return; - } - - if (0 == direction) { - if (Light.random == Light.wheel) { - Light.random = random(255); - - uint8_t my_dir = (Light.random < Light.wheel -128) ? 1 : - (Light.random < Light.wheel ) ? 0 : - (Light.random > Light.wheel +128) ? 0 : 1; - Light.random = (Light.random & 0xFE) | my_dir; - - - } - - direction = (Light.random &0x01) ? 1 : -1; - } - Light.wheel += direction; - uint16_t hue = changeUIntScale(Light.wheel, 0, 255, 0, 359); - - - - uint8_t sat; - light_state.getHSB(nullptr, &sat, nullptr); - light_state.setHS(hue, sat); - light_controller.calcLevels(Light.new_color); -} - -void LightSetPower(void) -{ - - Light.old_power = Light.power; - - uint32_t mask = 1; - if (Light.pwm_multi_channels) { - mask = (1 << Light.subtype) - 1; - } else if (!light_controller.isCTRGBLinked()) { - mask = 3; - } - uint32_t shift = Light.device - 1; - - - - - - Light.power = (XdrvMailbox.index & (mask << shift)) >> shift; - if (Light.wakeup_active) { - Light.wakeup_active--; - } -#ifdef DEBUG_LIGHT - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightSetPower XdrvMailbox.index=%d Light.old_power=%d Light.power=%d mask=%d shift=%d", - XdrvMailbox.index, Light.old_power, Light.power, mask, shift); -#endif - if (Light.power != Light.old_power) { - Light.update = true; - } - LightAnimate(); -} - - - - -void LightAnimate(void) -{ - uint16_t light_still_on = 0; - bool power_off = false; - - - light_controller.setAlexaCTRange(Settings.flag4.alexa_ct_range); - Light.strip_timer_counter++; - - - - if (Light.power || Light.fade_running) { - if (Settings.sleep > PWM_MAX_SLEEP) { - sleep = PWM_MAX_SLEEP; - } else { - sleep = Settings.sleep; - } - } else { - sleep = Settings.sleep; - } - - if (!Light.power) { - Light.strip_timer_counter = 0; - if (Settings.light_scheme >= LS_MAX) { - power_off = true; - } - } else { - switch (Settings.light_scheme) { - case LS_POWER: - light_controller.calcLevels(Light.new_color); - break; - case LS_WAKEUP: - if (2 == Light.wakeup_active) { - Light.wakeup_active = 1; - for (uint32_t i = 0; i < Light.subtype; i++) { - Light.new_color[i] = 0; - } - Light.wakeup_counter = 0; - Light.wakeup_dimmer = 0; - } - Light.wakeup_counter++; - if (Light.wakeup_counter > ((Settings.light_wakeup * STATES) / Settings.light_dimmer)) { - Light.wakeup_counter = 0; - Light.wakeup_dimmer++; - if (Light.wakeup_dimmer <= Settings.light_dimmer) { - light_state.setDimmer(Light.wakeup_dimmer); - light_controller.calcLevels(); - for (uint32_t i = 0; i < Light.subtype; i++) { - Light.new_color[i] = Light.current_color[i]; - } - } else { - - - - - Response_P(PSTR("{\"" D_CMND_WAKEUP "\":\"" D_JSON_DONE "\"")); - LightState(1); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_WAKEUP)); - XdrvRulesProcess(); - - Light.wakeup_active = 0; - Settings.light_scheme = LS_POWER; - } - } - break; - case LS_CYCLEUP: - LightCycleColor(1); - break; - case LS_CYCLEDN: - LightCycleColor(-1); - break; - case LS_RANDOM: - LightCycleColor(0); - break; - default: - XlgtCall(FUNC_SET_SCHEME); - } - } - - if ((Settings.light_scheme < LS_MAX) || power_off) { - - - LightApplyPower(Light.new_color, Light.power); - - - - - - - - if (memcmp(Light.last_color, Light.new_color, Light.subtype)) { - Light.update = true; - } - if (Light.update) { - uint16_t cur_col_10[LST_MAX]; - Light.update = false; - - - for (uint32_t i = 0; i < LST_MAX; i++) { - Light.last_color[i] = Light.new_color[i]; - - cur_col_10[i] = change8to10(Light.new_color[i]); - } - - if (Light.pwm_multi_channels) { - calcGammaMultiChannels(cur_col_10); - } else { - calcGammaBulbs(cur_col_10); - - - - if ((LST_RGBW <= Light.subtype) && (0 == Settings.rgbwwTable[4]) && (0 == cur_col_10[3]+cur_col_10[4])) { - uint32_t min_rgb_10 = min3(cur_col_10[0], cur_col_10[1], cur_col_10[2]); - for (uint32_t i=0; i<3; i++) { - - uint32_t adjust10 = change8to10(Settings.rgbwwTable[i]); - cur_col_10[i] = changeUIntScale(cur_col_10[i] - min_rgb_10, 0, 1023, 0, adjust10); - } - - - uint32_t adjust_w_10 = changeUIntScale(Settings.rgbwwTable[3], 0, 255, 0, 1023); - uint32_t white_10 = changeUIntScale(min_rgb_10, 0, 1023, 0, adjust_w_10); - if (LST_RGBW == Light.subtype) { - - cur_col_10[3] = white_10; - } else { - - uint32_t ct = light_state.getCT10bits(); - cur_col_10[4] = changeUIntScale(ct, 0, 1023, 0, white_10); - cur_col_10[3] = white_10 - cur_col_10[4]; - } - } - } - - - if (0 != Settings.rgbwwTable[4]) { - for (uint32_t i = 0; i 0) ? changeUIntScale(cur_col_10[i], 1, 1023, 1, Settings.pwm_range) : 0; - } - - - uint16_t orig_col_10bits[LST_MAX]; - memcpy(orig_col_10bits, cur_col_10, sizeof(orig_col_10bits)); - for (uint32_t i = 0; i < LST_MAX; i++) { - cur_col_10[i] = orig_col_10bits[Light.color_remap[i]]; - } - - if (!Settings.light_fade || power_off || (!Light.fade_initialized)) { - - memcpy(Light.fade_start_10, cur_col_10, sizeof(Light.fade_start_10)); - - LightSetOutputs(cur_col_10); - Light.fade_initialized = true; - } else { - if (Light.fade_running) { - - memcpy(Light.fade_start_10, Light.fade_cur_10, sizeof(Light.fade_start_10)); - } - memcpy(Light.fade_end_10, cur_col_10, sizeof(Light.fade_start_10)); - Light.fade_running = true; - Light.fade_duration = 0; - Light.fade_start = 0; - - } - } - if (Light.fade_running) { - if (LightApplyFade()) { - - - - LightSetOutputs(Light.fade_cur_10); - } - } - } -} - -bool isChannelGammaCorrected(uint32_t channel) { - if (!Settings.light_correction) { return false; } - if (channel >= Light.subtype) { return false; } - - if (PHILIPS == my_module_type) { - if ((LST_COLDWARM == Light.subtype) && (1 == channel)) { return false; } - if ((LST_RGBCW == Light.subtype) && (4 == channel)) { return false; } - } - return true; -} - - -uint16_t fadeGamma(uint32_t channel, uint16_t v) { - if (isChannelGammaCorrected(channel)) { - return ledGamma_internal(v, gamma_table_fast); - } else { - return v; - } -} -uint16_t fadeGammaReverse(uint32_t channel, uint16_t vg) { - if (isChannelGammaCorrected(channel)) { - return ledGammaReverse_internal(vg, gamma_table_fast); - } else { - return vg; - } -} - -bool LightApplyFade(void) { - static uint32_t last_millis = 0; - uint32_t now = millis(); - - if ((now - last_millis) <= 5) { - return false; - } - last_millis = now; - - - if (0 == Light.fade_duration) { - Light.fade_start = now; - - uint32_t distance = 0; - for (uint32_t i = 0; i < Light.subtype; i++) { - int32_t channel_distance = fadeGammaReverse(i, Light.fade_end_10[i]) - fadeGammaReverse(i, Light.fade_start_10[i]); - if (channel_distance < 0) { channel_distance = - channel_distance; } - if (channel_distance > distance) { distance = channel_distance; } - } - if (distance > 0) { - - - - Light.fade_duration = (distance * Settings.light_speed * 500) / 1023; - if (Settings.save_data) { - - uint32_t delay_seconds = 1 + (Light.fade_duration + 999) / 1000; - - if (save_data_counter < delay_seconds) { - save_data_counter = delay_seconds; - } - } - } else { - - Light.fade_running = false; - } - } - - uint16_t fade_current = now - Light.fade_start; - if (fade_current <= Light.fade_duration) { - - for (uint32_t i = 0; i < Light.subtype; i++) { - Light.fade_cur_10[i] = fadeGamma(i, - changeUIntScale(fadeGammaReverse(i, fade_current), - 0, Light.fade_duration, - fadeGammaReverse(i, Light.fade_start_10[i]), - fadeGammaReverse(i, Light.fade_end_10[i]))); - - - - } - } else { - - - Light.fade_running = false; - Light.fade_start = 0; - Light.fade_duration = 0; - - memcpy(Light.fade_cur_10, Light.fade_end_10, sizeof(Light.fade_end_10)); - - memcpy(Light.fade_start_10, Light.fade_end_10, sizeof(Light.fade_start_10)); - } - return true; -} - - - -void LightApplyPower(uint8_t new_color[LST_MAX], power_t power) { - - if (Light.pwm_multi_channels) { - - for (uint32_t i = 0; i < LST_MAX; i++) { - if (0 == bitRead(power,i)) { - new_color[i] = 0; - } - } - - - - - - } else { - if (!light_controller.isCTRGBLinked()) { - - if (0 == (power & 1)) { - new_color[0] = new_color[1] = new_color[2] = 0; - } - if (0 == (power & 2)) { - new_color[3] = new_color[4] = 0; - } - } else if (!power) { - for (uint32_t i = 0; i < LST_MAX; i++) { - new_color[i] = 0; - } - } - } -} - -void LightSetOutputs(const uint16_t *cur_col_10) { - - if (light_type < LT_PWM6) { - for (uint32_t i = 0; i < (Light.subtype - Light.pwm_offset); i++) { - if (pin[GPIO_PWM1 +i] < 99) { - - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col_10[(i + Light.pwm_offset)] : cur_col_10[(i + Light.pwm_offset)]); - } - } - } - - - - - uint8_t cur_col[LST_MAX]; - for (uint32_t i = 0; i < LST_MAX; i++) { - cur_col[i] = change10to8(cur_col_10[i]); - } - - - uint8_t scale_col[3]; - uint32_t max = (cur_col[0] > cur_col[1] && cur_col[0] > cur_col[2]) ? cur_col[0] : (cur_col[1] > cur_col[2]) ? cur_col[1] : cur_col[2]; - for (uint32_t i = 0; i < 3; i++) { - scale_col[i] = (0 == max) ? 255 : (255 > max) ? changeUIntScale(cur_col[i], 0, max, 0, 255) : cur_col[i]; - } - - char *tmp_data = XdrvMailbox.data; - char *tmp_topic = XdrvMailbox.topic; - XdrvMailbox.data = (char*)cur_col; - XdrvMailbox.topic = (char*)scale_col; - if (XlgtCall(FUNC_SET_CHANNELS)) { } - else if (XdrvCall(FUNC_SET_CHANNELS)) { } - XdrvMailbox.data = tmp_data; - XdrvMailbox.topic = tmp_topic; -} - - -void calcGammaMultiChannels(uint16_t cur_col_10[5]) { - - if (Settings.light_correction) { - for (uint32_t i = 0; i < LST_MAX; i++) { - cur_col_10[i] = ledGamma10_10(cur_col_10[i]); - } - } -} - -void calcGammaBulbs(uint16_t cur_col_10[5]) { - - - - if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { - - uint32_t cw1 = Light.subtype - 1; - uint32_t cw0 = Light.subtype - 2; - uint16_t white_bri10 = cur_col_10[cw0] + cur_col_10[cw1]; - uint16_t white_bri10_1023 = (white_bri10 > 1023) ? 1023 : white_bri10; - - if (PHILIPS == my_module_type) { - - cur_col_10[cw1] = light_state.getCT10bits(); - - if (Settings.light_correction) { - cur_col_10[cw0] = ledGamma10_10(white_bri10_1023); - } else { - cur_col_10[cw0] = white_bri10_1023; - } - } else if (Settings.light_correction) { - - if (white_bri10 <= 1031) { - - uint16_t white_bri_gamma10 = ledGamma10_10(white_bri10_1023); - - cur_col_10[cw0] = changeUIntScale(cur_col_10[cw0], 0, white_bri10_1023, 0, white_bri_gamma10); - cur_col_10[cw1] = changeUIntScale(cur_col_10[cw1], 0, white_bri10_1023, 0, white_bri_gamma10); - } else { - cur_col_10[cw0] = ledGamma10_10(cur_col_10[cw0]); - cur_col_10[cw1] = ledGamma10_10(cur_col_10[cw1]); - } - } - } - - if (Settings.light_correction) { - - if (LST_RGB <= Light.subtype) { - for (uint32_t i = 0; i < 3; i++) { - cur_col_10[i] = ledGamma10_10(cur_col_10[i]); - } - } - - if ((LST_SINGLE == Light.subtype) || (LST_RGBW == Light.subtype)) { - cur_col_10[Light.subtype - 1] = ledGamma10_10(cur_col_10[Light.subtype - 1]); - } - } -} - - - - - -bool LightColorEntry(char *buffer, uint32_t buffer_length) -{ - char scolor[10]; - char *p; - char *str; - uint32_t entry_type = 0; - uint8_t value = Light.fixed_color_index; - - if (buffer[0] == '#') { - buffer++; - buffer_length--; - } - - if (Light.subtype >= LST_RGB) { - char option = (1 == buffer_length) ? buffer[0] : '\0'; - if (('+' == option) && (Light.fixed_color_index < MAX_FIXED_COLOR)) { - value++; - } - else if (('-' == option) && (Light.fixed_color_index > 1)) { - value--; - } else { - value = atoi(buffer); - } - } - - memset(&Light.entry_color, 0x00, sizeof(Light.entry_color)); - - while ((buffer_length > 0) && ('=' == buffer[buffer_length - 1])) { - buffer_length--; - memcpy(&Light.entry_color, &Light.current_color, sizeof(Light.entry_color)); - } - if (strstr(buffer, ",") != nullptr) { - int8_t i = 0; - for (str = strtok_r(buffer, ",", &p); str && i < 6; str = strtok_r(nullptr, ",", &p)) { - if (i < LST_MAX) { - Light.entry_color[i++] = atoi(str); - } - } - entry_type = 2; - } - else if (((2 * Light.subtype) == buffer_length) || (buffer_length > 3)) { - for (uint32_t i = 0; i < tmin((uint)(buffer_length / 2), sizeof(Light.entry_color)); i++) { - strlcpy(scolor, buffer + (i *2), 3); - Light.entry_color[i] = (uint8_t)strtol(scolor, &p, 16); - } - entry_type = 1; - } - else if ((Light.subtype >= LST_RGB) && (value > 0) && (value <= MAX_FIXED_COLOR)) { - Light.fixed_color_index = value; - memcpy_P(&Light.entry_color, &kFixedColor[value -1], 3); - entry_type = 1; - } - else if ((value > 199) && (value <= 199 + MAX_FIXED_COLD_WARM)) { - if (LST_RGBW == Light.subtype) { - memcpy_P(&Light.entry_color[3], &kFixedWhite[value -200], 1); - entry_type = 1; - } - else if (LST_COLDWARM == Light.subtype) { - memcpy_P(&Light.entry_color, &kFixedColdWarm[value -200], 2); - entry_type = 1; - } - else if (LST_RGBCW == Light.subtype) { - memcpy_P(&Light.entry_color[3], &kFixedColdWarm[value -200], 2); - entry_type = 1; - } - } - if (entry_type) { - Settings.flag.decimal_text = entry_type -1; - } - return (entry_type); -} - - - -void CmndSupportColor(void) -{ - bool valid_entry = false; - bool coldim = false; - - if (XdrvMailbox.data_len > 0) { - valid_entry = LightColorEntry(XdrvMailbox.data, XdrvMailbox.data_len); - if (valid_entry) { - if (XdrvMailbox.index <= 2) { - uint32_t old_bri = light_state.getBri(); - - light_controller.changeChannels(Light.entry_color); - if (2 == XdrvMailbox.index) { - - light_controller.changeBri(old_bri); - } - - Settings.light_scheme = 0; - coldim = true; - } else { - for (uint32_t i = 0; i < LST_RGB; i++) { - Settings.ws_color[XdrvMailbox.index -3][i] = Light.entry_color[i]; - } - } - } - } - char scolor[LIGHT_COLOR_SIZE]; - if (!valid_entry && (XdrvMailbox.index <= 2)) { - ResponseCmndChar(LightGetColor(scolor)); - } - if (XdrvMailbox.index >= 3) { - scolor[0] = '\0'; - for (uint32_t i = 0; i < LST_RGB; i++) { - if (Settings.flag.decimal_text) { - snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.ws_color[XdrvMailbox.index -3][i]); - } else { - snprintf_P(scolor, sizeof(scolor), PSTR("%s%02X"), scolor, Settings.ws_color[XdrvMailbox.index -3][i]); - } - } - ResponseCmndIdxChar(scolor); - } - if (coldim) { - LightPreparePower(); - } -} - -void CmndColor(void) -{ - - - - - - - - if ((Light.subtype > LST_SINGLE) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 6)) { - CmndSupportColor(); - } -} - -void CmndWhite(void) -{ - - - if (Light.pwm_multi_channels) { return; } - if ( ((Light.subtype >= LST_RGBW) || (LST_COLDWARM == Light.subtype)) && (XdrvMailbox.index == 1)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - light_controller.changeDimmer(XdrvMailbox.payload, 2); - LightPreparePower(2); - } else { - ResponseCmndNumber(light_state.getDimmer(2)); - } - } -} - -void CmndChannel(void) -{ - - - - - if ((XdrvMailbox.index >= Light.device) && (XdrvMailbox.index < Light.device + Light.subtype )) { - uint32_t light_index = XdrvMailbox.index - Light.device; - power_t coldim = 0; - - - if (1 == XdrvMailbox.data_len) { - uint8_t channel = changeUIntScale(Light.current_color[light_index],0,255,0,100); - if ('+' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (channel > 89) ? 100 : channel + 10; - } else if ('-' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (channel < 11) ? 1 : channel - 10; - } - } - - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - Light.current_color[light_index] = changeUIntScale(XdrvMailbox.payload,0,100,0,255); - if (Light.pwm_multi_channels) { - coldim = 1 << light_index; - } else { - if (light_controller.isCTRGBLinked()) { - - if ((light_index < 3) && (light_controller.isCTRGBLinked())) { - Light.current_color[3] = Light.current_color[4] = 0; - } else { - Light.current_color[0] = Light.current_color[1] = Light.current_color[2] = 0; - } - coldim = 1; - } else { - if (light_index < 3) { coldim = 1; } - else { coldim = 2; } - } - } - light_controller.changeChannels(Light.current_color); - } - ResponseCmndIdxNumber(changeUIntScale(Light.current_color[light_index],0,255,0,100)); - if (coldim) { - LightPreparePower(coldim); - } - } -} - -void CmndHsbColor(void) -{ - - - - - - - - if (Light.subtype >= LST_RGB) { - if (XdrvMailbox.data_len > 0) { - uint16_t c_hue; - uint8_t c_sat; - light_state.getHSB(&c_hue, &c_sat, nullptr); - uint32_t HSB[3]; - HSB[0] = c_hue; - HSB[1] = c_sat; - HSB[2] = light_state.getBriRGB(); - if ((2 == XdrvMailbox.index) || (3 == XdrvMailbox.index)) { - if ((uint32_t)XdrvMailbox.payload > 100) { XdrvMailbox.payload = 100; } - HSB[XdrvMailbox.index-1] = changeUIntScale(XdrvMailbox.payload, 0, 100, 0, 255); - } else { - uint32_t paramcount = ParseParameters(3, HSB); - if (HSB[0] > 360) { HSB[0] = 360; } - for (uint32_t i = 1; i < paramcount; i++) { - if (HSB[i] > 100) { HSB[i] == 100; } - HSB[i] = changeUIntScale(HSB[i], 0, 100, 0, 255); - } - } - light_controller.changeHSB(HSB[0], HSB[1], HSB[2]); - LightPreparePower(1); - } else { - LightState(0); - } - } -} - -void CmndScheme(void) -{ - - - - - - if (Light.subtype >= LST_RGB) { - uint32_t max_scheme = Light.max_scheme; - - if (1 == XdrvMailbox.data_len) { - if (('+' == XdrvMailbox.data[0]) && (Settings.light_scheme < max_scheme)) { - XdrvMailbox.payload = Settings.light_scheme + ((0 == Settings.light_scheme) ? 2 : 1); - } - else if (('-' == XdrvMailbox.data[0]) && (Settings.light_scheme > 0)) { - XdrvMailbox.payload = Settings.light_scheme - ((2 == Settings.light_scheme) ? 2 : 1); - } - } - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= max_scheme)) { - uint32_t parm[2]; - if (ParseParameters(2, parm) > 1) { - Light.wheel = parm[1]; - } - Settings.light_scheme = XdrvMailbox.payload; - if (LS_WAKEUP == Settings.light_scheme) { - Light.wakeup_active = 3; - } - LightPowerOn(); - Light.strip_timer_counter = 0; - - if (Settings.flag3.hass_tele_on_power) { - MqttPublishTeleState(); - } - } - ResponseCmndNumber(Settings.light_scheme); - } -} - -void CmndWakeup(void) -{ - - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - light_controller.changeDimmer(XdrvMailbox.payload); - } - Light.wakeup_active = 3; - Settings.light_scheme = LS_WAKEUP; - LightPowerOn(); - ResponseCmndChar(D_JSON_STARTED); -} - -void CmndColorTemperature(void) -{ - - - - - if (Light.pwm_multi_channels) { return; } - if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) { - uint32_t ct = light_state.getCT(); - if (1 == XdrvMailbox.data_len) { - if ('+' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (ct > (CT_MAX-34)) ? CT_MAX : ct + 34; - } - else if ('-' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (ct < (CT_MIN+34)) ? CT_MIN : ct - 34; - } - } - if ((XdrvMailbox.payload >= CT_MIN) && (XdrvMailbox.payload <= CT_MAX)) { - light_controller.changeCTB(XdrvMailbox.payload, light_state.getBriCT()); - LightPreparePower(2); - } else { - ResponseCmndNumber(ct); - } - } -} - -void CmndDimmer(void) -{ - - - - - - - uint32_t dimmer; - if (XdrvMailbox.index > 2) { XdrvMailbox.index = 1; } - - if ((light_controller.isCTRGBLinked()) || (0 == XdrvMailbox.index)) { - dimmer = light_state.getDimmer(); - } else { - dimmer = light_state.getDimmer(XdrvMailbox.index); - } - - if (1 == XdrvMailbox.data_len) { - if ('+' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (dimmer > 89) ? 100 : dimmer + 10; - } else if ('-' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (dimmer < 11) ? 1 : dimmer - 10; - } - } - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - if (light_controller.isCTRGBLinked()) { - - light_controller.changeDimmer(XdrvMailbox.payload); - LightPreparePower(); - } else { - if (0 != XdrvMailbox.index) { - light_controller.changeDimmer(XdrvMailbox.payload, XdrvMailbox.index); - LightPreparePower(1 << (XdrvMailbox.index - 1)); - } else { - - light_controller.changeDimmer(XdrvMailbox.payload, 1); - light_controller.changeDimmer(XdrvMailbox.payload, 2); - LightPreparePower(); - } - } - Light.update = true; - } else { - ResponseCmndNumber(dimmer); - } -} - -void CmndDimmerRange(void) -{ - - - if (XdrvMailbox.data_len > 0) { - uint32_t parm[2]; - parm[0] = Settings.dimmer_hw_min; - parm[1] = Settings.dimmer_hw_max; - ParseParameters(2, parm); - if (parm[0] < parm[1]) { - Settings.dimmer_hw_min = parm[0]; - Settings.dimmer_hw_max = parm[1]; - } else { - Settings.dimmer_hw_min = parm[1]; - Settings.dimmer_hw_max = parm[0]; - } - restart_flag = 2; - } - Response_P(PSTR("{\"" D_CMND_DIMMER_RANGE "\":{\"Min\":%d,\"Max\":%d}}"), Settings.dimmer_hw_min, Settings.dimmer_hw_max); -} - -void CmndLedTable(void) -{ - - - - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { - switch (XdrvMailbox.payload) { - case 0: - case 1: - Settings.light_correction = XdrvMailbox.payload; - break; - case 2: - Settings.light_correction ^= 1; - break; - } - Light.update = true; - } - ResponseCmndStateText(Settings.light_correction); -} - -void CmndRgbwwTable(void) -{ - - - if ((XdrvMailbox.data_len > 0)) { - uint32_t parm[LST_RGBCW -1]; - uint32_t parmcount = ParseParameters(LST_RGBCW, parm); - for (uint32_t i = 0; i < parmcount; i++) { - Settings.rgbwwTable[i] = parm[i]; - } - Light.update = true; - } - char scolor[LIGHT_COLOR_SIZE]; - scolor[0] = '\0'; - for (uint32_t i = 0; i < LST_RGBCW; i++) { - snprintf_P(scolor, sizeof(scolor), PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.rgbwwTable[i]); - } - ResponseCmndChar(scolor); -} - -void CmndFade(void) -{ - - - - - switch (XdrvMailbox.payload) { - case 0: - case 1: - Settings.light_fade = XdrvMailbox.payload; - break; - case 2: - Settings.light_fade ^= 1; - break; - } - if (!Settings.light_fade) { Light.fade_running = false; } - ResponseCmndStateText(Settings.light_fade); -} - -void CmndSpeed(void) -{ - - - - - if (1 == XdrvMailbox.data_len) { - if (('+' == XdrvMailbox.data[0]) && (Settings.light_speed > 1)) { - XdrvMailbox.payload = Settings.light_speed - 1; - } - else if (('-' == XdrvMailbox.data[0]) && (Settings.light_speed < 40)) { - XdrvMailbox.payload = Settings.light_speed + 1; - } - } - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 40)) { - Settings.light_speed = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.light_speed); -} - -void CmndWakeupDuration(void) -{ - - - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3001)) { - Settings.light_wakeup = XdrvMailbox.payload; - Light.wakeup_active = 0; - } - ResponseCmndNumber(Settings.light_wakeup); -} - -void CmndUndocA(void) -{ - - char scolor[LIGHT_COLOR_SIZE]; - LightGetColor(scolor, true); - scolor[6] = '\0'; - Response_P(PSTR("%s,%d,%d,%d,%d,%d"), scolor, Settings.light_fade, Settings.light_correction, Settings.light_scheme, Settings.light_speed, Settings.light_width); - MqttPublishPrefixTopic_P(STAT, XdrvMailbox.topic); - mqtt_data[0] = '\0'; -} - - - - - -bool Xdrv04(uint8_t function) -{ - bool result = false; - - if (FUNC_MODULE_INIT == function) { - return LightModuleInit(); - } - else if (light_type) { - switch (function) { - case FUNC_SERIAL: - result = XlgtCall(FUNC_SERIAL); - break; - case FUNC_LOOP: - if (Light.fade_running) { - if (LightApplyFade()) { - LightSetOutputs(Light.fade_cur_10); - } - } - break; - case FUNC_EVERY_50_MSECOND: - LightAnimate(); - break; - case FUNC_SET_POWER: - LightSetPower(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kLightCommands, LightCommand); - if (!result) { - result = XlgtCall(FUNC_COMMAND); - } - break; - case FUNC_PRE_INIT: - LightInit(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_05_irremote.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_05_irremote.ino" -#if defined(USE_IR_REMOTE) && !defined(USE_IR_REMOTE_FULL) - - - - -#define XDRV_05 5 - -#include - -enum IrErrors { IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND }; - -const char kIrRemoteCommands[] PROGMEM = "|" D_CMND_IRSEND ; - - -void (* const IrRemoteCommand[])(void) PROGMEM = { - &CmndIrSend }; - - -static const uint8_t MAX_STANDARD_IR = NEC; -const char kIrRemoteProtocols[] PROGMEM = "UNKNOWN|RC5|RC6|NEC"; - - - - - -#include - -IRsend *irsend = nullptr; -bool irsend_active = false; - -void IrSendInit(void) -{ - irsend = new IRsend(pin[GPIO_IRSEND]); - irsend->begin(); -} - -#ifdef USE_IR_RECEIVE - - - - -const bool IR_RCV_SAVE_BUFFER = false; -const uint32_t IR_TIME_AVOID_DUPLICATE = 500; - -#include - -IRrecv *irrecv = nullptr; - -unsigned long ir_lasttime = 0; - -void IrReceiveUpdateThreshold(void) -{ - if (irrecv != nullptr) { - if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } - irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); - } -} - -void IrReceiveInit(void) -{ - - irrecv = new IRrecv(pin[GPIO_IRRECV], IR_RCV_BUFFER_SIZE, IR_RCV_TIMEOUT, IR_RCV_SAVE_BUFFER); - irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); - irrecv->enableIRIn(); - - -} - -void IrReceiveCheck(void) -{ - char sirtype[8]; - int8_t iridx = 0; - - decode_results results; - - if (irrecv->decode(&results)) { - char hvalue[65]; - - iridx = results.decode_type; - if ((iridx < 0) || (iridx > MAX_STANDARD_IR)) { iridx = 0; } - - if (iridx) { - if (results.bits > 64) { - - uint32_t digits2 = results.bits / 8; - if (results.bits % 8) { digits2++; } - ToHex_P((unsigned char*)results.state, digits2, hvalue, sizeof(hvalue)); - } else { - Uint64toHex(results.value, hvalue, results.bits); - } - } else { - Uint64toHex(results.value, hvalue, 32); - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value 0x%s, Decode %d"), - irsend_active, results.rawlen, results.overflow, results.bits, hvalue, results.decode_type); - - unsigned long now = millis(); - - if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { - ir_lasttime = now; - - char svalue[64]; - if (Settings.flag.ir_receive_decimal) { - ulltoa(results.value, svalue, 10); - } else { - snprintf_P(svalue, sizeof(svalue), PSTR("\"0x%s\""), hvalue); - } - ResponseTime_P(PSTR(",\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d"), - GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits); - if (iridx) { - ResponseAppend_P(PSTR(",\"" D_JSON_IR_DATA "\":%s"), svalue); - } else { - ResponseAppend_P(PSTR(",\"" D_JSON_IR_HASH "\":%s"), svalue); - } - - if (Settings.flag3.receive_raw) { - ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":[")); - uint16_t i; - for (i = 1; i < results.rawlen; i++) { - if (i > 1) { ResponseAppend_P(PSTR(",")); } - uint32_t usecs; - for (usecs = results.rawbuf[i] * kRawTick; usecs > UINT16_MAX; usecs -= UINT16_MAX) { - ResponseAppend_P(PSTR("%d,0,"), UINT16_MAX); - } - ResponseAppend_P(PSTR("%d"), usecs); - if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } - } - uint16_t extended_length = results.rawlen - 1; - for (uint32_t j = 0; j < results.rawlen - 1; j++) { - uint32_t usecs = results.rawbuf[j] * kRawTick; - - extended_length += (usecs / (UINT16_MAX + 1)) * 2; - } - ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); - } - - ResponseJsonEndEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); - - XdrvRulesProcess(); -#ifdef USE_DOMOTICZ - if (iridx) { - unsigned long value = results.value | (iridx << 28); - DomoticzSensor(DZ_COUNT, value); - } -#endif - } - - irrecv->resume(); - } -} -#endif - - - - - -uint32_t IrRemoteCmndIrSendJson(void) -{ - - - - - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { - return IE_INVALID_JSON; - } - - StaticJsonBuffer<140> jsonBuf; - JsonObject &root = jsonBuf.parseObject(dataBufUc); - if (!root.success()) { - return IE_INVALID_JSON; - } - - - - char parm_uc[10]; - const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))]; - uint16_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))]; - uint64_t data = strtoull(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], nullptr, 0); - uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))]; - - if (XdrvMailbox.index > repeat + 1) { - repeat = XdrvMailbox.index - 1; - } - if (!(protocol && bits)) { - return IE_SYNTAX_IRSEND; - } - - char protocol_text[20]; - int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols); - - char dvalue[64]; - char hvalue[20]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (0x%s), repeat %d, protocol_code %d"), - protocol_text, protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat, protocol_code); - - irsend_active = true; - switch (protocol_code) { -#ifdef USE_IR_SEND_RC5 - case RC5: - irsend->sendRC5(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_RC6 - case RC6: - irsend->sendRC6(data, bits, repeat); break; -#endif -#ifdef USE_IR_SEND_NEC - case NEC: - irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits, repeat); break; -#endif - default: - irsend_active = false; - ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); - } - - return IE_NO_ERROR; -} - -void CmndIrSend(void) -{ - uint8_t error = IE_SYNTAX_IRSEND; - - if (XdrvMailbox.data_len) { - - if (strstr(XdrvMailbox.data, "{") == nullptr) { - error = IE_INVALID_JSON; - } else { - error = IrRemoteCmndIrSendJson(); - } - } - IrRemoteCmndResponse(error); -} - -void IrRemoteCmndResponse(uint32_t error) -{ - switch (error) { - case IE_INVALID_RAWDATA: - ResponseCmndChar(D_JSON_INVALID_RAWDATA); - break; - case IE_INVALID_JSON: - ResponseCmndChar(D_JSON_INVALID_JSON); - break; - case IE_SYNTAX_IRSEND: - Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_PROTOCOL ", " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); - break; - default: - ResponseCmndDone(); - } -} - - - - - -bool Xdrv05(uint8_t function) -{ - bool result = false; - - if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) { - switch (function) { - case FUNC_PRE_INIT: - if (pin[GPIO_IRSEND] < 99) { - IrSendInit(); - } -#ifdef USE_IR_RECEIVE - if (pin[GPIO_IRRECV] < 99) { - IrReceiveInit(); - } -#endif - break; - case FUNC_EVERY_50_MSECOND: -#ifdef USE_IR_RECEIVE - if (pin[GPIO_IRRECV] < 99) { - IrReceiveCheck(); - } -#endif - irsend_active = false; - break; - case FUNC_COMMAND: - if (pin[GPIO_IRSEND] < 99) { - result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); - } - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_05_irremote_full.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_05_irremote_full.ino" -#ifdef USE_IR_REMOTE_FULL - - - - -#define XDRV_05 5 - -#include -#include -#include -#include -#include - -enum IrErrors { IE_RESPONSE_PROVIDED, IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC, - IE_UNSUPPORTED_HVAC, IE_UNSUPPORTED_PROTOCOL }; - -const char kIrRemoteCommands[] PROGMEM = "|" - D_CMND_IRHVAC "|" D_CMND_IRSEND ; - -void (* const IrRemoteCommand[])(void) PROGMEM = { - &CmndIrHvac, &CmndIrSend }; - - - - - -IRsend *irsend = nullptr; -bool irsend_active = false; - -void IrSendInit(void) -{ - irsend = new IRsend(pin[GPIO_IRSEND]); - irsend->begin(); -} - - - -uint8_t reverseBitsInByte(uint8_t b) { - b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; - b = (b & 0xCC) >> 2 | (b & 0x33) << 2; - b = (b & 0xAA) >> 1 | (b & 0x55) << 1; - return b; -} - - -uint64_t reverseBitsInBytes64(uint64_t b) { - union { - uint8_t b[8]; - uint64_t i; - } a; - a.i = b; - for (uint32_t i=0; i<8; i++) { - a.b[i] = reverseBitsInByte(a.b[i]); - } - return a.i; -} - - - - - -const bool IR_FULL_RCV_SAVE_BUFFER = false; -const uint32_t IR_TIME_AVOID_DUPLICATE = 500; - - - - -const uint16_t IR_FULL_BUFFER_SIZE = 1024; - - - -const uint8_t IR__FULL_RCV_TIMEOUT = 50; - -IRrecv *irrecv = nullptr; - -unsigned long ir_lasttime = 0; - -void IrReceiveUpdateThreshold(void) -{ - if (irrecv != nullptr) { - if (Settings.param[P_IR_UNKNOW_THRESHOLD] < 6) { Settings.param[P_IR_UNKNOW_THRESHOLD] = 6; } - irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); - } -} - -void IrReceiveInit(void) -{ - - irrecv = new IRrecv(pin[GPIO_IRRECV], IR_FULL_BUFFER_SIZE, IR__FULL_RCV_TIMEOUT, IR_FULL_RCV_SAVE_BUFFER); - irrecv->setUnknownThreshold(Settings.param[P_IR_UNKNOW_THRESHOLD]); - irrecv->enableIRIn(); -} - -String sendACJsonState(const stdAc::state_t &state) { - DynamicJsonBuffer jsonBuffer; - JsonObject& json = jsonBuffer.createObject(); - json[D_JSON_IRHVAC_VENDOR] = typeToString(state.protocol); - json[D_JSON_IRHVAC_MODEL] = state.model; - json[D_JSON_IRHVAC_POWER] = IRac::boolToString(state.power); - json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(state.mode); - - if (state.mode == stdAc::opmode_t::kOff || !state.power) { - json[D_JSON_IRHVAC_MODE] = IRac::opmodeToString(stdAc::opmode_t::kOff); - json[D_JSON_IRHVAC_POWER] = IRac::boolToString(false); - } - json[D_JSON_IRHVAC_CELSIUS] = IRac::boolToString(state.celsius); - if (floorf(state.degrees) == state.degrees) { - json[D_JSON_IRHVAC_TEMP] = floorf(state.degrees); - } else { - json[D_JSON_IRHVAC_TEMP] = RawJson(String(state.degrees, 1)); - } - json[D_JSON_IRHVAC_FANSPEED] = IRac::fanspeedToString(state.fanspeed); - json[D_JSON_IRHVAC_SWINGV] = IRac::swingvToString(state.swingv); - json[D_JSON_IRHVAC_SWINGH] = IRac::swinghToString(state.swingh); - json[D_JSON_IRHVAC_QUIET] = IRac::boolToString(state.quiet); - json[D_JSON_IRHVAC_TURBO] = IRac::boolToString(state.turbo); - json[D_JSON_IRHVAC_ECONO] = IRac::boolToString(state.econo); - json[D_JSON_IRHVAC_LIGHT] = IRac::boolToString(state.light); - json[D_JSON_IRHVAC_FILTER] = IRac::boolToString(state.filter); - json[D_JSON_IRHVAC_CLEAN] = IRac::boolToString(state.clean); - json[D_JSON_IRHVAC_BEEP] = IRac::boolToString(state.beep); - json[D_JSON_IRHVAC_SLEEP] = state.sleep; - - String payload = ""; - payload.reserve(200); - json.printTo(payload); - return payload; -} - -String sendIRJsonState(const struct decode_results &results) { - String json("{"); - json += "\"" D_JSON_IR_PROTOCOL "\":\""; - json += typeToString(results.decode_type); - json += "\",\"" D_JSON_IR_BITS "\":"; - json += results.bits; - - if (hasACState(results.decode_type)) { - json += ",\"" D_JSON_IR_DATA "\":\"0x"; - json += resultToHexidecimal(&results); - json += "\""; - } else { - if (UNKNOWN != results.decode_type) { - json += ",\"" D_JSON_IR_DATA "\":"; - } else { - json += ",\"" D_JSON_IR_HASH "\":"; - } - if (Settings.flag.ir_receive_decimal) { - char svalue[32]; - ulltoa(results.value, svalue, 10); - json += svalue; - } else { - char hvalue[64]; - if (UNKNOWN != results.decode_type) { - Uint64toHex(results.value, hvalue, results.bits); - json += "\"0x"; - json += hvalue; - json += "\",\"" D_JSON_IR_DATALSB "\":\"0x"; - Uint64toHex(reverseBitsInBytes64(results.value), hvalue, results.bits); - json += hvalue; - json += "\""; - } else { - Uint64toHex(results.value, hvalue, 32); - json += "\"0x"; - json += hvalue; - json += "\""; - } - } - } - json += ",\"" D_JSON_IR_REPEAT "\":"; - json += results.repeat; - - stdAc::state_t ac_result; - if (IRAcUtils::decodeToState(&results, &ac_result, nullptr)) { - - json += ",\"" D_CMND_IRHVAC "\":"; - json += sendACJsonState(ac_result); - } - - return json; -} - -void IrReceiveCheck(void) -{ - decode_results results; - - if (irrecv->decode(&results)) { - uint32_t now = millis(); - - - if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) { - ir_lasttime = now; - Response_P(PSTR("{\"" D_JSON_IRRECEIVED "\":%s"), sendIRJsonState(results).c_str()); - - if (Settings.flag3.receive_raw) { - ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":[")); - uint16_t i; - for (i = 1; i < results.rawlen; i++) { - if (i > 1) { ResponseAppend_P(PSTR(",")); } - uint32_t usecs; - for (usecs = results.rawbuf[i] * kRawTick; usecs > UINT16_MAX; usecs -= UINT16_MAX) { - ResponseAppend_P(PSTR("%d,0,"), UINT16_MAX); - } - ResponseAppend_P(PSTR("%d"), usecs); - if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } - } - uint16_t extended_length = results.rawlen - 1; - for (uint32_t j = 0; j < results.rawlen - 1; j++) { - uint32_t usecs = results.rawbuf[j] * kRawTick; - - extended_length += (usecs / (UINT16_MAX + 1)) * 2; - } - ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow); - } - - ResponseJsonEndEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); - - XdrvRulesProcess(); - } - - irrecv->resume(); - } -} - - - - - - - -String listSupportedProtocols(bool hvac) { - String l(""); - bool first = true; - for (uint32_t i = UNUSED + 1; i <= kLastDecodeType; i++) { - bool found = false; - if (hvac) { - found = IRac::isProtocolSupported((decode_type_t)i); - } else { - found = (IRsend::defaultBits((decode_type_t)i) > 0) && (!IRac::isProtocolSupported((decode_type_t)i)); - } - if (found) { - if (first) { - first = false; - } else { - l += "|"; - } - l += typeToString((decode_type_t)i); - } - } - return l; -} - - -const stdAc::fanspeed_t IrHvacFanSpeed[] PROGMEM = { stdAc::fanspeed_t::kAuto, - stdAc::fanspeed_t::kMin, stdAc::fanspeed_t::kLow,stdAc::fanspeed_t::kMedium, - stdAc::fanspeed_t::kHigh, stdAc::fanspeed_t::kMax }; - -uint32_t IrRemoteCmndIrHvacJson(void) -{ - stdAc::state_t state, prev; - char parm_uc[12]; - - - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } - - DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(dataBufUc); - if (!json.success()) { return IE_INVALID_JSON; } - - - state.protocol = decode_type_t::UNKNOWN; - state.model = 1; - state.mode = stdAc::opmode_t::kAuto; - state.power = false; - state.celsius = true; - state.degrees = 21.0f; - state.fanspeed = stdAc::fanspeed_t::kMedium; - state.swingv = stdAc::swingv_t::kOff; - state.swingh = stdAc::swingh_t::kOff; - state.light = false; - state.beep = false; - state.econo = false; - state.filter = false; - state.turbo = false; - state.quiet = false; - state.sleep = -1; - state.clean = false; - state.clock = -1; - - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); - if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); - if (json.containsKey(parm_uc)) { state.protocol = strToDecodeType(json[parm_uc]); } - if (decode_type_t::UNKNOWN == state.protocol) { return IE_UNSUPPORTED_HVAC; } - if (!IRac::isProtocolSupported(state.protocol)) { return IE_UNSUPPORTED_HVAC; } - - - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FANSPEED)); - if (json.containsKey(parm_uc)) { - uint32_t fan_speed = json[parm_uc]; - if ((fan_speed >= 1) && (fan_speed <= 5)) { - state.fanspeed = (stdAc::fanspeed_t) pgm_read_byte(&IrHvacFanSpeed[fan_speed]); - } else { - state.fanspeed = IRac::strToFanspeed(json[parm_uc]); - } - } - - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODEL)); - if (json.containsKey(parm_uc)) { state.model = IRac::strToModel(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_MODE)); - if (json.containsKey(parm_uc)) { state.mode = IRac::strToOpmode(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SWINGV)); - if (json.containsKey(parm_uc)) { state.swingv = IRac::strToSwingV(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SWINGH)); - if (json.containsKey(parm_uc)) { state.swingh = IRac::strToSwingH(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TEMP)); - if (json.containsKey(parm_uc)) { state.degrees = json[parm_uc]; } - - - - - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_POWER)); - if (json.containsKey(parm_uc)) { state.power = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_CELSIUS)); - if (json.containsKey(parm_uc)) { state.celsius = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_LIGHT)); - if (json.containsKey(parm_uc)) { state.light = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_BEEP)); - if (json.containsKey(parm_uc)) { state.beep = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_ECONO)); - if (json.containsKey(parm_uc)) { state.econo = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_FILTER)); - if (json.containsKey(parm_uc)) { state.filter = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_TURBO)); - if (json.containsKey(parm_uc)) { state.turbo = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_QUIET)); - if (json.containsKey(parm_uc)) { state.quiet = IRac::strToBool(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_CLEAN)); - if (json.containsKey(parm_uc)) { state.clean = IRac::strToBool(json[parm_uc]); } - - - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_SLEEP)); - if (json[parm_uc]) { state.sleep = json[parm_uc]; } - - - IRac ac(pin[GPIO_IRSEND]); - bool success = ac.sendAc(state, &prev); - if (!success) { return IE_SYNTAX_IRHVAC; } - - Response_P(PSTR("{\"" D_CMND_IRHVAC "\":%s}"), sendACJsonState(state).c_str()); - return IE_RESPONSE_PROVIDED; -} - -void CmndIrHvac(void) -{ - uint8_t error = IE_SYNTAX_IRHVAC; - - if (XdrvMailbox.data_len) { - error = IrRemoteCmndIrHvacJson(); - } - if (error != IE_RESPONSE_PROVIDED) { IrRemoteCmndResponse(error); } -} - - - - - -uint32_t IrRemoteCmndIrSendJson(void) -{ - char parm_uc[12]; - - - - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - RemoveSpace(dataBufUc); - if (strlen(dataBufUc) < 8) { return IE_INVALID_JSON; } - - DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(dataBufUc); - if (!json.success()) { return IE_INVALID_JSON; } - - - - decode_type_t protocol = decode_type_t::UNKNOWN; - uint16_t bits = 0; - uint64_t data; - uint8_t repeat = 0; - - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_VENDOR)); - if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } - UpperCase_P(parm_uc, PSTR(D_JSON_IRHVAC_PROTOCOL)); - if (json.containsKey(parm_uc)) { protocol = strToDecodeType(json[parm_uc]); } - if (decode_type_t::UNKNOWN == protocol) { return IE_UNSUPPORTED_PROTOCOL; } - - UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS)); - if (json.containsKey(parm_uc)) { bits = json[parm_uc]; } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT)); - if (json.containsKey(parm_uc)) { repeat = json[parm_uc]; } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATALSB)); - if (json.containsKey(parm_uc)) { data = reverseBitsInBytes64(strtoull(json[parm_uc], nullptr, 0)); } - UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA)); - if (json.containsKey(parm_uc)) { data = strtoull(json[parm_uc], nullptr, 0); } - if (0 == bits) { return IE_SYNTAX_IRSEND; } - - - if (XdrvMailbox.index > repeat + 1) { repeat = XdrvMailbox.index - 1; } - - char dvalue[32]; - char hvalue[32]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol %d, bits %d, data 0x%s (%s), repeat %d"), - protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat); - - irsend_active = true; - bool success = irsend->send(protocol, data, bits, repeat); - - if (!success) { - irsend_active = false; - ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); - } - return IE_NO_ERROR; -} - -uint32_t IrRemoteCmndIrSendRaw(void) -{ - - - - - - - - char *p; - char *str = strtok_r(XdrvMailbox.data, ", ", &p); - if (p == nullptr) { - return IE_INVALID_RAWDATA; - } - - - uint16_t repeat = XdrvMailbox.index > 0 ? XdrvMailbox.index - 1 : 0; - - uint16_t freq = atoi(str); - if (!freq && (*str != '0')) { - uint16_t count = 0; - char *q = p; - for (; *q; count += (*q++ == ',')); - if (count < 2) { - return IE_INVALID_RAWDATA; - } - - uint16_t parm[count]; - for (uint32_t i = 0; i < count; i++) { - parm[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); - if (!parm[i]) { - if (!i) { - parm[0] = 38000; - } else { - return IE_INVALID_RAWDATA; - } - } - } - - uint16_t i = 0; - if (count < 4) { - - uint16_t mark = parm[1] *2; - if (3 == count) { - if (parm[2] < parm[1]) { - - mark = parm[1] * parm[2]; - } else { - - mark = parm[2]; - } - } - uint16_t raw_array[strlen(p)]; - for (; *p; *p++) { - if (*p == '0') { - raw_array[i++] = parm[1]; - } - else if (*p == '1') { - raw_array[i++] = mark; - } - } - irsend_active = true; - for (uint32_t r = 0; r <= repeat; r++) { - irsend->sendRaw(raw_array, i, parm[0]); - if (r < repeat) { - irsend->space(40000); - } - } - } - else if (6 == count) { - - uint16_t raw_array[strlen(p)*2+3]; - raw_array[i++] = parm[1]; - raw_array[i++] = parm[2]; - uint32_t inter_message_32 = (parm[1] + parm[2]) * 3; - uint16_t inter_message = (inter_message_32 > 65000) ? 65000 : inter_message_32; - for (; *p; *p++) { - if (*p == '0') { - raw_array[i++] = parm[3]; - raw_array[i++] = parm[4]; - } - else if (*p == '1') { - raw_array[i++] = parm[3]; - raw_array[i++] = parm[5]; - } - } - raw_array[i++] = parm[3]; - irsend_active = true; - for (uint32_t r = 0; r <= repeat; r++) { - irsend->sendRaw(raw_array, i, parm[0]); - if (r < repeat) { - irsend->space(inter_message); - } - } - } - else { - return IE_INVALID_RAWDATA; - } - } else { - if (!freq) { freq = 38000; } - uint16_t count = 0; - char *q = p; - for (; *q; count += (*q++ == ',')); - if (0 == count) { - return IE_INVALID_RAWDATA; - } - - - count++; - if (count < 200) { - uint16_t raw_array[count]; - for (uint32_t i = 0; i < count; i++) { - raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); - } - - - - irsend_active = true; - for (uint32_t r = 0; r <= repeat; r++) { - irsend->sendRaw(raw_array, count, freq); - } - } else { - uint16_t *raw_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); - if (raw_array == nullptr) { - return IE_INVALID_RAWDATA; - } - - for (uint32_t i = 0; i < count; i++) { - raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); - } - - - - irsend_active = true; - for (uint32_t r = 0; r <= repeat; r++) { - irsend->sendRaw(raw_array, count, freq); - } - free(raw_array); - } - } - - return IE_NO_ERROR; -} - -void CmndIrSend(void) -{ - uint8_t error = IE_SYNTAX_IRSEND; - - if (XdrvMailbox.data_len) { - if (strstr(XdrvMailbox.data, "{") == nullptr) { - error = IrRemoteCmndIrSendRaw(); - } else { - error = IrRemoteCmndIrSendJson(); - } - } - IrRemoteCmndResponse(error); -} - -void IrRemoteCmndResponse(uint32_t error) -{ - switch (error) { - case IE_INVALID_RAWDATA: - ResponseCmndChar(D_JSON_INVALID_RAWDATA); - break; - case IE_INVALID_JSON: - ResponseCmndChar(D_JSON_INVALID_JSON); - break; - case IE_SYNTAX_IRSEND: - Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); - break; - case IE_SYNTAX_IRHVAC: - Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR ", " D_JSON_IRHVAC_MODE " " D_JSON_OR " " D_JSON_IRHVAC_FANSPEED "\"}")); - break; - case IE_UNSUPPORTED_HVAC: - Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR " (%s)\"}"), listSupportedProtocols(true).c_str()); - break; - case IE_UNSUPPORTED_PROTOCOL: - Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_PROTOCOL " (%s)\"}"), listSupportedProtocols(false).c_str()); - break; - default: - ResponseCmndDone(); - } -} - - - - - -bool Xdrv05(uint8_t function) -{ - bool result = false; - - if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) { - switch (function) { - case FUNC_PRE_INIT: - if (pin[GPIO_IRSEND] < 99) { - IrSendInit(); - } - if (pin[GPIO_IRRECV] < 99) { - IrReceiveInit(); - } - break; - case FUNC_EVERY_50_MSECOND: - if (pin[GPIO_IRRECV] < 99) { - IrReceiveCheck(); - } - irsend_active = false; - break; - case FUNC_COMMAND: - if (pin[GPIO_IRSEND] < 99) { - result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); - } - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_06_snfbridge.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_06_snfbridge.ino" -#ifdef USE_SONOFF_RF - - - - -#define XDRV_06 6 - -const uint32_t SFB_TIME_AVOID_DUPLICATE = 2000; - -enum SonoffBridgeCommands { - CMND_RFSYNC, CMND_RFLOW, CMND_RFHIGH, CMND_RFHOST, CMND_RFCODE }; - -const char kSonoffBridgeCommands[] PROGMEM = "|" - D_CMND_RFSYNC "|" D_CMND_RFLOW "|" D_CMND_RFHIGH "|" D_CMND_RFHOST "|" D_CMND_RFCODE "|" D_CMND_RFKEY "|" D_CMND_RFRAW; - -void (* const SonoffBridgeCommand[])(void) PROGMEM = { - &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfBridge, &CmndRfKey, &CmndRfRaw }; - -struct SONOFFBRIDGE { - uint32_t last_received_id = 0; - uint32_t last_send_code = 0; - uint32_t last_time = 0; - uint32_t last_learn_time = 0; - uint8_t receive_flag = 0; - uint8_t receive_raw_flag = 0; - uint8_t learn_key = 1; - uint8_t learn_active = 0; - uint8_t expected_bytes = 0; -} SnfBridge; - -#ifdef USE_RF_FLASH - - - - - - - -#include "ihx.h" -#include "c2.h" - -const ssize_t RF_RECORD_NO_START_FOUND = -1; -const ssize_t RF_RECORD_NO_END_FOUND = -2; - -ssize_t rf_find_hex_record_start(uint8_t *buf, size_t size) -{ - for (size_t i = 0; i < size; i++) { - if (buf[i] == ':') { - return i; - } - } - return RF_RECORD_NO_START_FOUND; -} - -ssize_t rf_find_hex_record_end(uint8_t *buf, size_t size) -{ - for (size_t i = 0; i < size; i++) { - if (buf[i] == '\n') { - return i; - } - } - return RF_RECORD_NO_END_FOUND; -} - -ssize_t rf_glue_remnant_with_new_data_and_write(const uint8_t *remnant_data, uint8_t *new_data, size_t new_data_len) -{ - ssize_t record_start; - ssize_t record_end; - ssize_t glue_record_sz; - uint8_t *glue_buf; - ssize_t result; - - if (remnant_data[0] != ':') { return -8; } - - - record_end = rf_find_hex_record_end(new_data, new_data_len); - record_start = rf_find_hex_record_start(new_data, new_data_len); - - - - - if ((record_start != RF_RECORD_NO_START_FOUND) && (record_start < record_end)) { - return -8; - } - - glue_record_sz = strlen((const char *) remnant_data) + record_end; - - glue_buf = (uint8_t *) malloc(glue_record_sz); - if (glue_buf == nullptr) { return -2; } - - - memcpy(glue_buf, remnant_data, strlen((const char *) remnant_data)); - memcpy(glue_buf + strlen((const char *) remnant_data), new_data, record_end); - - result = rf_decode_and_write(glue_buf, glue_record_sz); - free(glue_buf); - return result; -} - -ssize_t rf_decode_and_write(uint8_t *record, size_t size) -{ - uint8_t err = ihx_decode(record, size); - if (err != IHX_SUCCESS) { return -13; } - - ihx_t *h = (ihx_t *) record; - if (h->record_type == IHX_RT_DATA) { - int retries = 5; - uint16_t address = h->address_high * 0x100 + h->address_low; - - do { - err = c2_programming_init(); - err = c2_block_write(address, h->data, h->len); - } while (err != C2_SUCCESS && retries--); - } else if (h->record_type == IHX_RT_END_OF_FILE) { - - err = c2_reset(); - } - - if (err != C2_SUCCESS) { return -12; } - - return 0; -} - -ssize_t rf_search_and_write(uint8_t *buf, size_t size) -{ - - ssize_t rec_end; - ssize_t rec_start; - ssize_t err; - - for (size_t i = 0; i < size; i++) { - - rec_start = rf_find_hex_record_start(buf + i, size - i); - if (rec_start == RF_RECORD_NO_START_FOUND) { - - return -8; - } - - - rec_start += i; - rec_end = rf_find_hex_record_end(buf + rec_start, size - rec_start); - if (rec_end == RF_RECORD_NO_END_FOUND) { - - return rec_start; - } - - - rec_end += rec_start; - - err = rf_decode_and_write(buf + rec_start, rec_end - rec_start); - if (err < 0) { return err; } - i = rec_end; - } - - return 0; -} - -uint8_t rf_erase_flash(void) -{ - uint8_t err; - - for (uint32_t i = 0; i < 4; i++) { - err = c2_programming_init(); - if (err != C2_SUCCESS) { - return 10; - } - err = c2_device_erase(); - if (err != C2_SUCCESS) { - if (i < 3) { - c2_reset(); - } else { - return 11; - } - } else { - break; - } - } - return 0; -} - -uint8_t SnfBrUpdateInit(void) -{ - pinMode(PIN_C2CK, OUTPUT); - pinMode(PIN_C2D, INPUT); - - return rf_erase_flash(); -} -#endif - - - -void SonoffBridgeReceivedRaw(void) -{ - - uint8_t buckets = 0; - - if (0xB1 == serial_in_buffer[1]) { buckets = serial_in_buffer[2] << 1; } - - ResponseTime_P(PSTR(",\"" D_CMND_RFRAW "\":{\"" D_JSON_DATA "\":\"")); - for (uint32_t i = 0; i < serial_in_byte_counter; i++) { - ResponseAppend_P(PSTR("%02X"), serial_in_buffer[i]); - if (0xB1 == serial_in_buffer[1]) { - if ((i > 3) && buckets) { buckets--; } - if ((i < 3) || (buckets % 2) || (i == serial_in_byte_counter -2)) { - ResponseAppend_P(PSTR(" ")); - } - } - } - ResponseAppend_P(PSTR("\"}}")); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_CMND_RFRAW)); - - XdrvRulesProcess(); -} - - - -void SonoffBridgeLearnFailed(void) -{ - SnfBridge.learn_active = 0; - Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, SnfBridge.learn_key, D_JSON_LEARN_FAILED); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY)); -} - -void SonoffBridgeReceived(void) -{ - uint16_t sync_time = 0; - uint16_t low_time = 0; - uint16_t high_time = 0; - uint32_t received_id = 0; - char rfkey[8]; - char stemp[16]; - - AddLogSerial(LOG_LEVEL_DEBUG); - - if (0xA2 == serial_in_buffer[0]) { - SonoffBridgeLearnFailed(); - } - else if (0xA3 == serial_in_buffer[0]) { - SnfBridge.learn_active = 0; - low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; - high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; - if (low_time && high_time) { - for (uint32_t i = 0; i < 9; i++) { - Settings.rf_code[SnfBridge.learn_key][i] = serial_in_buffer[i +1]; - } - Response_P(S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, SnfBridge.learn_key, D_JSON_LEARNED); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY)); - } else { - SonoffBridgeLearnFailed(); - } - } - else if (0xA4 == serial_in_buffer[0]) { - if (SnfBridge.learn_active) { - SonoffBridgeLearnFailed(); - } else { - sync_time = serial_in_buffer[1] << 8 | serial_in_buffer[2]; - low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; - high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; - received_id = serial_in_buffer[7] << 16 | serial_in_buffer[8] << 8 | serial_in_buffer[9]; - - unsigned long now = millis(); - if (!((received_id == SnfBridge.last_received_id) && (now - SnfBridge.last_time < SFB_TIME_AVOID_DUPLICATE))) { - SnfBridge.last_received_id = received_id; - SnfBridge.last_time = now; - strncpy_P(rfkey, PSTR("\"" D_JSON_NONE "\""), sizeof(rfkey)); - for (uint32_t i = 1; i <= 16; i++) { - if (Settings.rf_code[i][0]) { - uint32_t send_id = Settings.rf_code[i][6] << 16 | Settings.rf_code[i][7] << 8 | Settings.rf_code[i][8]; - if (send_id == received_id) { - snprintf_P(rfkey, sizeof(rfkey), PSTR("%d"), i); - break; - } - } - } - if (Settings.flag.rf_receive_decimal) { - snprintf_P(stemp, sizeof(stemp), PSTR("%u"), received_id); - } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"%06X\""), received_id); - } - ResponseTime_P(PSTR(",\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":%s,\"" D_CMND_RFKEY "\":%s}}"), - sync_time, low_time, high_time, stemp, rfkey); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED)); - XdrvRulesProcess(); - #ifdef USE_DOMOTICZ - DomoticzSensor(DZ_COUNT, received_id); - #endif - } - } - } -} - -bool SonoffBridgeSerialInput(void) -{ - - static int8_t receive_len = 0; - - if (SnfBridge.receive_flag) { - if (SnfBridge.receive_raw_flag) { - if (!serial_in_byte_counter) { - serial_in_buffer[serial_in_byte_counter++] = 0xAA; - } - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - if (serial_in_byte_counter == 3) { - if ((0xA6 == serial_in_buffer[1]) || (0xAB == serial_in_buffer[1])) { - receive_len = serial_in_buffer[2] + 4; - } - } - if ((!receive_len && (0x55 == serial_in_byte)) || (receive_len && (serial_in_byte_counter == receive_len))) { - SonoffBridgeReceivedRaw(); - SnfBridge.receive_flag = 0; - return 1; - } - } - else if (!((0 == serial_in_byte_counter) && (0 == serial_in_byte))) { - if (0 == serial_in_byte_counter) { - SnfBridge.expected_bytes = 2; - if (serial_in_byte >= 0xA3) { - SnfBridge.expected_bytes = 11; - } - if (serial_in_byte == 0xA6) { - SnfBridge.expected_bytes = 0; - serial_in_buffer[serial_in_byte_counter++] = 0xAA; - SnfBridge.receive_raw_flag = 1; - } - } - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - if ((SnfBridge.expected_bytes == serial_in_byte_counter) && (0x55 == serial_in_byte)) { - SonoffBridgeReceived(); - SnfBridge.receive_flag = 0; - return 1; - } - } - serial_in_byte = 0; - } - if (0xAA == serial_in_byte) { - serial_in_byte_counter = 0; - serial_in_byte = 0; - SnfBridge.receive_flag = 1; - receive_len = 0; - } - return 0; -} - -void SonoffBridgeSendCommand(uint8_t code) -{ - Serial.write(0xAA); - Serial.write(code); - Serial.write(0x55); -} - -void SonoffBridgeSendAck(void) -{ - Serial.write(0xAA); - Serial.write(0xA0); - Serial.write(0x55); -} - -void SonoffBridgeSendCode(uint32_t code) -{ - Serial.write(0xAA); - Serial.write(0xA5); - for (uint32_t i = 0; i < 6; i++) { - Serial.write(Settings.rf_code[0][i]); - } - Serial.write((code >> 16) & 0xff); - Serial.write((code >> 8) & 0xff); - Serial.write(code & 0xff); - Serial.write(0x55); - Serial.flush(); -} - -void SonoffBridgeSend(uint8_t idx, uint8_t key) -{ - uint8_t code; - - key--; - Serial.write(0xAA); - Serial.write(0xA5); - for (uint32_t i = 0; i < 8; i++) { - Serial.write(Settings.rf_code[idx][i]); - } - if (0 == idx) { - code = (0x10 << (key >> 2)) | (1 << (key & 3)); - } else { - code = Settings.rf_code[idx][8]; - } - Serial.write(code); - Serial.write(0x55); - Serial.flush(); -#ifdef USE_DOMOTICZ - - -#endif -} - -void SonoffBridgeLearn(uint8_t key) -{ - SnfBridge.learn_key = key; - SnfBridge.learn_active = 1; - SnfBridge.last_learn_time = millis(); - Serial.write(0xAA); - Serial.write(0xA1); - Serial.write(0x55); -} - - - - - -void CmndRfBridge(void) -{ - char *p; - char stemp [10]; - uint32_t code = 0; - uint8_t radix = 10; - - uint32_t set_index = XdrvMailbox.command_code *2; - - if (XdrvMailbox.data[0] == '#') { - XdrvMailbox.data++; - XdrvMailbox.data_len--; - radix = 16; - } - - if (XdrvMailbox.data_len) { - code = strtol(XdrvMailbox.data, &p, radix); - if (code) { - if (CMND_RFCODE == XdrvMailbox.command_code) { - SnfBridge.last_send_code = code; - SonoffBridgeSendCode(code); - } else { - if (1 == XdrvMailbox.payload) { - code = pgm_read_byte(kDefaultRfCode + set_index) << 8 | pgm_read_byte(kDefaultRfCode + set_index +1); - } - uint8_t msb = code >> 8; - uint8_t lsb = code & 0xFF; - if ((code > 0) && (code < 0x7FFF) && (msb != 0x55) && (lsb != 0x55)) { - Settings.rf_code[0][set_index] = msb; - Settings.rf_code[0][set_index +1] = lsb; - } - } - } - } - if (CMND_RFCODE == XdrvMailbox.command_code) { - code = SnfBridge.last_send_code; - } else { - code = Settings.rf_code[0][set_index] << 8 | Settings.rf_code[0][set_index +1]; - } - if (10 == radix) { - snprintf_P(stemp, sizeof(stemp), PSTR("%d"), code); - } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"#%06X\""), code); - } - Response_P(S_JSON_COMMAND_XVALUE, XdrvMailbox.command, stemp); -} - -void CmndRfKey(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 16)) { - unsigned long now = millis(); - if ((!SnfBridge.learn_active) || (now - SnfBridge.last_learn_time > 60100)) { - SnfBridge.learn_active = 0; - if (2 == XdrvMailbox.payload) { - SonoffBridgeLearn(XdrvMailbox.index); - ResponseCmndIdxChar(D_JSON_START_LEARNING); - } - else if (3 == XdrvMailbox.payload) { - Settings.rf_code[XdrvMailbox.index][0] = 0; - ResponseCmndIdxChar(D_JSON_SET_TO_DEFAULT); - } - else if (4 == XdrvMailbox.payload) { - for (uint32_t i = 0; i < 6; i++) { - Settings.rf_code[XdrvMailbox.index][i] = Settings.rf_code[0][i]; - } - Settings.rf_code[XdrvMailbox.index][6] = (SnfBridge.last_send_code >> 16) & 0xff; - Settings.rf_code[XdrvMailbox.index][7] = (SnfBridge.last_send_code >> 8) & 0xff; - Settings.rf_code[XdrvMailbox.index][8] = SnfBridge.last_send_code & 0xff; - ResponseCmndIdxChar(D_JSON_SAVED); - } else if (5 == XdrvMailbox.payload) { - uint8_t key = XdrvMailbox.index; - uint8_t index = (0 == Settings.rf_code[key][0]) ? 0 : key; - uint16_t sync_time = (Settings.rf_code[index][0] << 8) | Settings.rf_code[index][1]; - uint16_t low_time = (Settings.rf_code[index][2] << 8) | Settings.rf_code[index][3]; - uint16_t high_time = (Settings.rf_code[index][4] << 8) | Settings.rf_code[index][5]; - uint32_t code = (Settings.rf_code[index][6] << 16) | (Settings.rf_code[index][7] << 8); - if (0 == index) { - key--; - code |= (uint8_t)((0x10 << (key >> 2)) | (1 << (key & 3))); - } else { - code |= Settings.rf_code[index][8]; - } - Response_P(PSTR("{\"%s%d\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":\"%06X\"}}"), - XdrvMailbox.command, XdrvMailbox.index, sync_time, low_time, high_time, code); - } else { - if ((1 == XdrvMailbox.payload) || (0 == Settings.rf_code[XdrvMailbox.index][0])) { - SonoffBridgeSend(0, XdrvMailbox.index); - ResponseCmndIdxChar(D_JSON_DEFAULT_SENT); - } else { - SonoffBridgeSend(XdrvMailbox.index, 0); - ResponseCmndIdxChar(D_JSON_LEARNED_SENT); - } - } - } else { - Response_P(S_JSON_COMMAND_INDEX_SVALUE, XdrvMailbox.command, SnfBridge.learn_key, D_JSON_LEARNING_ACTIVE); - } - } -} - -void CmndRfRaw(void) -{ - if (XdrvMailbox.data_len) { - if (XdrvMailbox.data_len < 6) { - switch (XdrvMailbox.payload) { - case 0: - SonoffBridgeSendCommand(0xA7); - case 1: - SnfBridge.receive_raw_flag = XdrvMailbox.payload; - break; - case 166: - case 167: - case 169: - case 176: - case 177: - case 255: - SonoffBridgeSendCommand(XdrvMailbox.payload); - SnfBridge.receive_raw_flag = 1; - break; - case 192: - char beep[] = "AAC000C055\0"; - SerialSendRaw(beep); - break; - } - } else { - SerialSendRaw(RemoveSpace(XdrvMailbox.data)); - SnfBridge.receive_raw_flag = 1; - } - } - ResponseCmndStateText(SnfBridge.receive_raw_flag); -} - - - - - -bool Xdrv06(uint8_t function) -{ - bool result = false; - - if (SONOFF_BRIDGE == my_module_type) { - switch (function) { - case FUNC_SERIAL: - result = SonoffBridgeSerialInput(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kSonoffBridgeCommands, SonoffBridgeCommand); - break; - case FUNC_INIT: - SnfBridge.receive_raw_flag = 0; - SonoffBridgeSendCommand(0xA7); - break; - case FUNC_PRE_INIT: - SetSerial(19200, TS_SERIAL_8N1); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_07_domoticz.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_07_domoticz.ino" -#ifdef USE_DOMOTICZ - -#define XDRV_07 7 - -#define D_PRFX_DOMOTICZ "Domoticz" -#define D_CMND_IDX "Idx" -#define D_CMND_KEYIDX "KeyIdx" -#define D_CMND_SWITCHIDX "SwitchIdx" -#define D_CMND_SENSORIDX "SensorIdx" -#define D_CMND_UPDATETIMER "UpdateTimer" - -const char kDomoticzCommands[] PROGMEM = D_PRFX_DOMOTICZ "|" - D_CMND_IDX "|" D_CMND_KEYIDX "|" D_CMND_SWITCHIDX "|" D_CMND_SENSORIDX "|" D_CMND_UPDATETIMER ; - -void (* const DomoticzCommand[])(void) PROGMEM = { - &CmndDomoticzIdx, &CmndDomoticzKeyIdx, &CmndDomoticzSwitchIdx, &CmndDomoticzSensorIdx, &CmndDomoticzUpdateTimer }; - -const char DOMOTICZ_MESSAGE[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\",\"Battery\":%d,\"RSSI\":%d}"; - -#if MAX_DOMOTICZ_SNS_IDX < DZ_MAX_SENSORS - #error "Domoticz: Too many sensors or change settings.h layout" -#endif - -const char kDomoticzSensors[] PROGMEM = - D_DOMOTICZ_TEMP "|" D_DOMOTICZ_TEMP_HUM "|" D_DOMOTICZ_TEMP_HUM_BARO "|" D_DOMOTICZ_POWER_ENERGY "|" D_DOMOTICZ_ILLUMINANCE "|" - D_DOMOTICZ_COUNT "|" D_DOMOTICZ_VOLTAGE "|" D_DOMOTICZ_CURRENT "|" D_DOMOTICZ_AIRQUALITY "|" D_DOMOTICZ_P1_SMART_METER "|" D_DOMOTICZ_SHUTTER ; - -char domoticz_in_topic[] = DOMOTICZ_IN_TOPIC; - -int domoticz_update_timer = 0; -uint32_t domoticz_fan_debounce = 0; -bool domoticz_subscribe = false; -bool domoticz_update_flag = true; - -#ifdef USE_SHUTTER -bool domoticz_is_shutter = false; -#endif - -int DomoticzBatteryQuality(void) -{ - - - - - int quality = 100; - -#ifdef USE_ADC_VCC - uint16_t voltage = ESP.getVcc(); - if (voltage <= 2600) { - quality = 0; - } else if (voltage >= 4600) { - quality = 200; - } else { - quality = (voltage - 2600) / 10; - } -#endif - return quality; -} - -int DomoticzRssiQuality(void) -{ - - - return WifiGetRssiAsQuality(WiFi.RSSI()) / 10; -} - -#ifdef USE_SONOFF_IFAN -void MqttPublishDomoticzFanState(void) -{ - if (Settings.flag.mqtt_enabled && Settings.domoticz_relay_idx[1]) { - char svalue[8]; - - int fan_speed = GetFanspeed(); - snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fan_speed * 10); - Response_P(DOMOTICZ_MESSAGE, (int)Settings.domoticz_relay_idx[1], (0 == fan_speed) ? 0 : 2, svalue, DomoticzBatteryQuality(), DomoticzRssiQuality()); - MqttPublish(domoticz_in_topic); - - domoticz_fan_debounce = millis(); - } -} - -void DomoticzUpdateFanState(void) -{ - if (domoticz_update_flag) { - MqttPublishDomoticzFanState(); - } - domoticz_update_flag = true; -} -#endif - -void MqttPublishDomoticzPowerState(uint8_t device) -{ - if (Settings.flag.mqtt_enabled) { - if ((device < 1) || (device > devices_present)) { device = 1; } - if (Settings.domoticz_relay_idx[device -1]) { -#ifdef USE_SHUTTER - if (domoticz_is_shutter) { - - } else { -#endif -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan() && (device > 1)) { - - } else { -#endif - char svalue[8]; - - snprintf_P(svalue, sizeof(svalue), PSTR("%d"), Settings.light_dimmer); - Response_P(DOMOTICZ_MESSAGE, (int)Settings.domoticz_relay_idx[device -1], (power & (1 << (device -1))) ? 1 : 0, (light_type) ? svalue : "", DomoticzBatteryQuality(), DomoticzRssiQuality()); - MqttPublish(domoticz_in_topic); -#ifdef USE_SONOFF_IFAN - } -#endif -#ifdef USE_SHUTTER - } -#endif - } - } -} - -void DomoticzUpdatePowerState(uint8_t device) -{ - if (domoticz_update_flag) { - MqttPublishDomoticzPowerState(device); - } - domoticz_update_flag = true; -} - -void DomoticzMqttUpdate(void) -{ - if (domoticz_subscribe && (Settings.domoticz_update_timer || domoticz_update_timer)) { - domoticz_update_timer--; - if (domoticz_update_timer <= 0) { - domoticz_update_timer = Settings.domoticz_update_timer; - for (uint32_t i = 1; i <= devices_present; i++) { -#ifdef USE_SHUTTER - if (domoticz_is_shutter) - { - - break; - } -#endif -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan() && (i > 1)) { - MqttPublishDomoticzFanState(); - break; - } else { -#endif - MqttPublishDomoticzPowerState(i); -#ifdef USE_SONOFF_IFAN - } -#endif - } - } - } -} - -void DomoticzMqttSubscribe(void) -{ - uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; - for (uint32_t i = 0; i < maxdev; i++) { - if (Settings.domoticz_relay_idx[i]) { - domoticz_subscribe = true; - } - } - - if (domoticz_subscribe) { - char stopic[TOPSZ]; - snprintf_P(stopic, sizeof(stopic), PSTR(DOMOTICZ_OUT_TOPIC "/#")); - MqttSubscribe(stopic); - } -} -# 219 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_07_domoticz.ino" -bool DomoticzMqttData(void) -{ - domoticz_update_flag = true; - - if (strncasecmp_P(XdrvMailbox.topic, PSTR(DOMOTICZ_OUT_TOPIC), strlen(DOMOTICZ_OUT_TOPIC)) != 0) { - return false; - } - - - if (XdrvMailbox.data_len < 20) { - return true; - } - StaticJsonBuffer<400> jsonBuf; - JsonObject& domoticz = jsonBuf.parseObject(XdrvMailbox.data); - if (!domoticz.success()) { - return true; - } - - - - uint32_t idx = domoticz["idx"]; - int16_t nvalue = -1; - if (domoticz.containsKey("nvalue")) { - nvalue = domoticz["nvalue"]; - } - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ "idx %d, nvalue %d"), idx, nvalue); - - bool found = false; - if ((idx > 0) && (nvalue >= 0) && (nvalue <= 15)) { - uint8_t maxdev = (devices_present > MAX_DOMOTICZ_IDX) ? MAX_DOMOTICZ_IDX : devices_present; - for (uint32_t i = 0; i < maxdev; i++) { - if (idx == Settings.domoticz_relay_idx[i]) { - bool iscolordimmer = strcmp_P(domoticz["dtype"],PSTR("Color Switch")) == 0; - bool isShutter = strcmp_P(domoticz["dtype"],PSTR("Light/Switch")) == 0 & strncmp_P(domoticz["switchType"],PSTR("Blinds"), 6) == 0; - - char stemp1[10]; - snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1); -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan() && (1 == i)) { - uint8_t svalue = 0; - if (domoticz.containsKey("svalue1")) { - svalue = domoticz["svalue1"]; - } else { - return true; - } - svalue = (nvalue == 2) ? svalue / 10 : 0; - if (GetFanspeed() == svalue) { - return true; - } - if (TimePassedSince(domoticz_fan_debounce) < 1000) { - return true; - } - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_FANSPEED)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), svalue); - found = true; - } else -#endif -#ifdef USE_SHUTTER - if (isShutter) - { - if (domoticz.containsKey("nvalue")) { - nvalue = domoticz["nvalue"]; - } - - uint8_t position = 0; - if (domoticz.containsKey("svalue1")) { - position = domoticz["svalue1"]; - } - if (nvalue != 2) { - position = nvalue == 0 ? 0 : 100; - } - - snprintf_P(XdrvMailbox.topic, TOPSZ, PSTR("/" D_PRFX_SHUTTER D_CMND_SHUTTER_POSITION)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), position); - XdrvMailbox.data_len = position > 99 ? 3 : (position > 9 ? 2 : 1); - - found = true; - } else -#endif - if (iscolordimmer && 10 == nvalue) { - - JsonObject& color = domoticz["Color"]; - uint16_t level = nvalue = domoticz["svalue1"]; - uint16_t r = color["r"]; r = r * level / 100; - uint16_t g = color["g"]; g = g * level / 100; - uint16_t b = color["b"]; b = b * level / 100; - uint16_t cw = color["cw"]; cw = cw * level / 100; - uint16_t ww = color["ww"]; ww = ww * level / 100; - uint16_t m = 0; - uint16_t t = 0; - if (color.containsKey("m")) { - m = color["m"]; - t = color["t"]; - } - if (2 == m) { - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_BACKLOG)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR(D_CMND_COLORTEMPERATURE " %d;" D_CMND_DIMMER " %d"), changeUIntScale(t, 0, 255, CT_MIN, CT_MAX), level); - } else { - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_COLOR)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%02x%02x%02x%02x%02x"), r, g, b, cw, ww); - } - found = true; - } - else if ((!iscolordimmer && 2 == nvalue) || - (iscolordimmer && 15 == nvalue)) { - if (domoticz.containsKey("svalue1")) { - nvalue = domoticz["svalue1"]; - } else { - return true; - } - if (light_type && (Settings.light_dimmer == nvalue) && ((power >> i) &1)) { - return true; - } - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_DIMMER)); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); - found = true; - } - else if (1 == nvalue || 0 == nvalue) { - if (((power >> i) &1) == (power_t)nvalue) { - return true; - } - snprintf_P(XdrvMailbox.topic, XdrvMailbox.index, PSTR("/" D_CMND_POWER "%s"), (devices_present > 1) ? stemp1 : ""); - snprintf_P(XdrvMailbox.data, XdrvMailbox.data_len, PSTR("%d"), nvalue); - found = true; - } - break; - } - } - } - if (!found) { return true; } - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_DOMOTICZ D_RECEIVED_TOPIC " %s, " D_DATA " %s"), XdrvMailbox.topic, XdrvMailbox.data); - - domoticz_update_flag = false; - return false; -} - - - -bool DomoticzSendKey(uint8_t key, uint8_t device, uint8_t state, uint8_t svalflg) -{ - bool result = false; - - if (device <= MAX_DOMOTICZ_IDX) { - if ((Settings.domoticz_key_idx[device -1] || Settings.domoticz_switch_idx[device -1]) && (svalflg)) { - Response_P(PSTR("{\"command\":\"switchlight\",\"idx\":%d,\"switchcmd\":\"%s\"}"), - (key) ? Settings.domoticz_switch_idx[device -1] : Settings.domoticz_key_idx[device -1], (state) ? (POWER_TOGGLE == state) ? "Toggle" : "On" : "Off"); - MqttPublish(domoticz_in_topic); - result = true; - } - } - return result; -} -# 391 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_07_domoticz.ino" -uint8_t DomoticzHumidityState(char *hum) -{ - uint8_t h = atoi(hum); - return (!h) ? 0 : (h < 40) ? 2 : (h > 70) ? 3 : 1; -} - -void DomoticzSensor(uint8_t idx, char *data) -{ - if (Settings.domoticz_sensor_idx[idx]) { - char dmess[128]; - - memcpy(dmess, mqtt_data, sizeof(dmess)); - if (DZ_AIRQUALITY == idx) { - Response_P(PSTR("{\"idx\":%d,\"nvalue\":%s,\"Battery\":%d,\"RSSI\":%d}"), - Settings.domoticz_sensor_idx[idx], data, DomoticzBatteryQuality(), DomoticzRssiQuality()); - } else { - uint8_t nvalue = 0; -#ifdef USE_SHUTTER - if (DZ_SHUTTER == idx) { - uint8_t position = atoi(data); - nvalue = position < 2 ? 0 : (position == 100 ? 1 : 2); - } -#endif - Response_P(DOMOTICZ_MESSAGE, - Settings.domoticz_sensor_idx[idx], nvalue, data, DomoticzBatteryQuality(), DomoticzRssiQuality()); - } - MqttPublish(domoticz_in_topic); - memcpy(mqtt_data, dmess, sizeof(dmess)); - } -} - -void DomoticzSensor(uint8_t idx, uint32_t value) -{ - char data[16]; - snprintf_P(data, sizeof(data), PSTR("%d"), value); - DomoticzSensor(idx, data); -} - -void DomoticzTempHumSensor(char *temp, char *hum) -{ - char data[16]; - snprintf_P(data, sizeof(data), PSTR("%s;%s;%d"), temp, hum, DomoticzHumidityState(hum)); - DomoticzSensor(DZ_TEMP_HUM, data); -} - -void DomoticzTempHumPressureSensor(char *temp, char *hum, char *baro) -{ - char data[32]; - snprintf_P(data, sizeof(data), PSTR("%s;%s;%d;%s;5"), temp, hum, DomoticzHumidityState(hum), baro); - DomoticzSensor(DZ_TEMP_HUM_BARO, data); -} - -void DomoticzSensorPowerEnergy(int power, char *energy) -{ - char data[16]; - snprintf_P(data, sizeof(data), PSTR("%d;%s"), power, energy); - DomoticzSensor(DZ_POWER_ENERGY, data); -} - -void DomoticzSensorP1SmartMeter(char *usage1, char *usage2, char *return1, char *return2, int power) -{ - - - - - - int consumed = power; - int produced = 0; - if (power < 0) { - consumed = 0; - produced = -power; - } - char data[64]; - snprintf_P(data, sizeof(data), PSTR("%s;%s;%s;%s;%d;%d"), usage1, usage2, return1, return2, consumed, produced); - DomoticzSensor(DZ_P1_SMART_METER, data); -} - - - - - -void CmndDomoticzIdx(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { - if (XdrvMailbox.payload >= 0) { - Settings.domoticz_relay_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; - restart_flag = 2; - } - ResponseCmndIdxNumber(Settings.domoticz_relay_idx[XdrvMailbox.index -1]); - } -} - -void CmndDomoticzKeyIdx(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { - if (XdrvMailbox.payload >= 0) { - Settings.domoticz_key_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.domoticz_key_idx[XdrvMailbox.index -1]); - } -} - -void CmndDomoticzSwitchIdx(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_DOMOTICZ_IDX)) { - if (XdrvMailbox.payload >= 0) { - Settings.domoticz_switch_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.domoticz_switch_idx[XdrvMailbox.index -1]); - } -} - -void CmndDomoticzSensorIdx(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= DZ_MAX_SENSORS)) { - if (XdrvMailbox.payload >= 0) { - Settings.domoticz_sensor_idx[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.domoticz_sensor_idx[XdrvMailbox.index -1]); - } -} - -void CmndDomoticzUpdateTimer(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) { - Settings.domoticz_update_timer = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.domoticz_update_timer); -} - - - - - -#ifdef USE_WEBSERVER - -#define WEB_HANDLE_DOMOTICZ "dm" - -const char S_CONFIGURE_DOMOTICZ[] PROGMEM = D_CONFIGURE_DOMOTICZ; - -const char HTTP_BTN_MENU_DOMOTICZ[] PROGMEM = - "

"; - -const char HTTP_FORM_DOMOTICZ[] PROGMEM = - "
 " D_DOMOTICZ_PARAMETERS " " - "
" - ""; -const char HTTP_FORM_DOMOTICZ_RELAY[] PROGMEM = - "" - ""; -const char HTTP_FORM_DOMOTICZ_SWITCH[] PROGMEM = - ""; -const char HTTP_FORM_DOMOTICZ_SENSOR[] PROGMEM = - ""; -const char HTTP_FORM_DOMOTICZ_TIMER[] PROGMEM = - ""; - -void HandleDomoticzConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_DOMOTICZ); - - if (WebServer->hasArg("save")) { - DomoticzSaveSettings(); - WebRestart(1); - return; - } - - char stemp[40]; - - WSContentStart_P(S_CONFIGURE_DOMOTICZ); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_DOMOTICZ); - for (uint32_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { - if (i < devices_present) { - WSContentSend_P(HTTP_FORM_DOMOTICZ_RELAY, - i +1, i, Settings.domoticz_relay_idx[i], - i +1, i, Settings.domoticz_key_idx[i]); - } - if (pin[GPIO_SWT1 +i] < 99) { - WSContentSend_P(HTTP_FORM_DOMOTICZ_SWITCH, - i +1, i, Settings.domoticz_switch_idx[i]); - } -#ifdef USE_SONOFF_IFAN - if (IsModuleIfan() && (1 == i)) { break; } -#endif - } - for (uint32_t i = 0; i < DZ_MAX_SENSORS; i++) { - WSContentSend_P(HTTP_FORM_DOMOTICZ_SENSOR, - i +1, GetTextIndexed(stemp, sizeof(stemp), i, kDomoticzSensors), i, Settings.domoticz_sensor_idx[i]); - } - WSContentSend_P(HTTP_FORM_DOMOTICZ_TIMER, Settings.domoticz_update_timer); - WSContentSend_P(PSTR("
" D_DOMOTICZ_IDX " %d
" D_DOMOTICZ_KEY_IDX " %d
" D_DOMOTICZ_SWITCH_IDX " %d
" D_DOMOTICZ_SENSOR_IDX " %d %s
" D_DOMOTICZ_UPDATE_TIMER " (" STR(DOMOTICZ_UPDATE_TIMER) ")
")); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void DomoticzSaveSettings(void) -{ - char stemp[20]; - char ssensor_indices[6 * MAX_DOMOTICZ_SNS_IDX]; - char tmp[100]; - - for (uint32_t i = 0; i < MAX_DOMOTICZ_IDX; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("r%d"), i); - WebGetArg(stemp, tmp, sizeof(tmp)); - Settings.domoticz_relay_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); - snprintf_P(stemp, sizeof(stemp), PSTR("k%d"), i); - WebGetArg(stemp, tmp, sizeof(tmp)); - Settings.domoticz_key_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); - snprintf_P(stemp, sizeof(stemp), PSTR("s%d"), i); - WebGetArg(stemp, tmp, sizeof(tmp)); - Settings.domoticz_switch_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); - } - ssensor_indices[0] = '\0'; - for (uint32_t i = 0; i < DZ_MAX_SENSORS; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("l%d"), i); - WebGetArg(stemp, tmp, sizeof(tmp)); - Settings.domoticz_sensor_idx[i] = (!strlen(tmp)) ? 0 : atoi(tmp); - snprintf_P(ssensor_indices, sizeof(ssensor_indices), PSTR("%s%s%d"), ssensor_indices, (strlen(ssensor_indices)) ? "," : "", Settings.domoticz_sensor_idx[i]); - } - WebGetArg("ut", tmp, sizeof(tmp)); - Settings.domoticz_update_timer = (!strlen(tmp)) ? DOMOTICZ_UPDATE_TIMER : atoi(tmp); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_DOMOTICZ D_CMND_IDX " %d,%d,%d,%d, " D_CMND_KEYIDX " %d,%d,%d,%d, " D_CMND_SWITCHIDX " %d,%d,%d,%d, " D_CMND_SENSORIDX " %s, " D_CMND_UPDATETIMER " %d"), - Settings.domoticz_relay_idx[0], Settings.domoticz_relay_idx[1], Settings.domoticz_relay_idx[2], Settings.domoticz_relay_idx[3], - Settings.domoticz_key_idx[0], Settings.domoticz_key_idx[1], Settings.domoticz_key_idx[2], Settings.domoticz_key_idx[3], - Settings.domoticz_switch_idx[0], Settings.domoticz_switch_idx[1], Settings.domoticz_switch_idx[2], Settings.domoticz_switch_idx[3], - ssensor_indices, Settings.domoticz_update_timer); -} -#endif - - - - - -bool Xdrv07(uint8_t function) -{ - bool result = false; - - if (Settings.flag.mqtt_enabled) { - switch (function) { - case FUNC_EVERY_SECOND: - DomoticzMqttUpdate(); - break; - case FUNC_MQTT_DATA: - result = DomoticzMqttData(); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_ADD_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_DOMOTICZ); - break; - case FUNC_WEB_ADD_HANDLER: - WebServer->on("/" WEB_HANDLE_DOMOTICZ, HandleDomoticzConfiguration); - break; -#endif - case FUNC_MQTT_SUBSCRIBE: - DomoticzMqttSubscribe(); -#ifdef USE_SHUTTER - if (Settings.domoticz_sensor_idx[DZ_SHUTTER]) { domoticz_is_shutter = true; } -#endif - break; - case FUNC_MQTT_INIT: - domoticz_update_timer = 2; - break; - case FUNC_SHOW_SENSOR: - - break; - case FUNC_COMMAND: - result = DecodeCommand(kDomoticzCommands, DomoticzCommand); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_08_serial_bridge.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_08_serial_bridge.ino" -#ifdef USE_SERIAL_BRIDGE - - - - -#define XDRV_08 8 - -const uint8_t SERIAL_BRIDGE_BUFFER_SIZE = 130; - -const char kSerialBridgeCommands[] PROGMEM = "|" - D_CMND_SSERIALSEND "|" D_CMND_SBAUDRATE; - -void (* const SerialBridgeCommand[])(void) PROGMEM = { - &CmndSSerialSend, &CmndSBaudrate }; - -#include - -TasmotaSerial *SerialBridgeSerial = nullptr; - -unsigned long serial_bridge_polling_window = 0; -char *serial_bridge_buffer = nullptr; -int serial_bridge_in_byte_counter = 0; -bool serial_bridge_active = true; -bool serial_bridge_raw = false; - -void SerialBridgeInput(void) -{ - while (SerialBridgeSerial->available()) { - yield(); - uint8_t serial_in_byte = SerialBridgeSerial->read(); - - if ((serial_in_byte > 127) && !serial_bridge_raw) { - serial_bridge_in_byte_counter = 0; - SerialBridgeSerial->flush(); - return; - } - if (serial_in_byte || serial_bridge_raw) { - - if ((serial_bridge_in_byte_counter < SERIAL_BRIDGE_BUFFER_SIZE -1) && - ((isprint(serial_in_byte) && (128 == Settings.serial_delimiter)) || - ((serial_in_byte != Settings.serial_delimiter) && (128 != Settings.serial_delimiter)) || - serial_bridge_raw)) { - serial_bridge_buffer[serial_bridge_in_byte_counter++] = serial_in_byte; - serial_bridge_polling_window = millis(); - } else { - serial_bridge_polling_window = 0; - break; - } - } - } - - if (serial_bridge_in_byte_counter && (millis() > (serial_bridge_polling_window + SERIAL_POLLING))) { - serial_bridge_buffer[serial_bridge_in_byte_counter] = 0; - char hex_char[(serial_bridge_in_byte_counter * 2) + 2]; - bool assume_json = (!serial_bridge_raw && (serial_bridge_buffer[0] == '{')); - Response_P(PSTR("{\"" D_JSON_SSERIALRECEIVED "\":%s%s%s}"), - (assume_json) ? "" : """", - (serial_bridge_raw) ? ToHex_P((unsigned char*)serial_bridge_buffer, serial_bridge_in_byte_counter, hex_char, sizeof(hex_char)) : serial_bridge_buffer, - (assume_json) ? "" : """"); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_SSERIALRECEIVED)); - XdrvRulesProcess(); - serial_bridge_in_byte_counter = 0; - } -} - - - -void SerialBridgeInit(void) -{ - serial_bridge_active = false; - 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 * 300)) { - if (SerialBridgeSerial->hardwareSerial()) { - ClaimSerial(); - serial_bridge_buffer = serial_in_buffer; - } else { - serial_bridge_buffer = (char*)(malloc(SERIAL_BRIDGE_BUFFER_SIZE)); - } - serial_bridge_active = true; - SerialBridgeSerial->flush(); - } - } -} - - - - - -void CmndSSerialSend(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 5)) { - serial_bridge_raw = (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) || (4 == 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); - } - else if (5 == XdrvMailbox.index) { - char *p; - char stemp[3]; - uint8_t code; - - char *codes = RemoveSpace(XdrvMailbox.data); - int size = strlen(XdrvMailbox.data); - - while (size > 1) { - strlcpy(stemp, codes, sizeof(stemp)); - code = strtol(stemp, &p, 16); - SerialBridgeSerial->write(code); - size -= 2; - codes += 2; - } - } - ResponseCmndDone(); - } - } -} - -void CmndSBaudrate(void) -{ - if (XdrvMailbox.payload >= 300) { - XdrvMailbox.payload /= 300; - Settings.sbaudrate = XdrvMailbox.payload; - SerialBridgeSerial->begin(Settings.sbaudrate * 300); - } - ResponseCmndNumber(Settings.sbaudrate * 300); -} - - - - - -bool Xdrv08(uint8_t function) -{ - bool result = false; - - if (serial_bridge_active) { - switch (function) { - case FUNC_LOOP: - if (SerialBridgeSerial) { SerialBridgeInput(); } - break; - case FUNC_PRE_INIT: - SerialBridgeInit(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kSerialBridgeCommands, SerialBridgeCommand); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_09_timers.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_09_timers.ino" -#ifdef USE_TIMERS -# 39 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_09_timers.ino" -#define XDRV_09 9 - -const char kTimerCommands[] PROGMEM = "|" - D_CMND_TIMER "|" D_CMND_TIMERS -#ifdef USE_SUNRISE - "|" D_CMND_LATITUDE "|" D_CMND_LONGITUDE -#endif - ; - -void (* const TimerCommand[])(void) PROGMEM = { - &CmndTimer, &CmndTimers -#ifdef USE_SUNRISE - , &CmndLatitude, &CmndLongitude -#endif - }; - -uint16_t timer_last_minute = 60; -int8_t timer_window[MAX_TIMERS] = { 0 }; - -#ifdef USE_SUNRISE -# 67 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_09_timers.ino" -const float pi2 = TWO_PI; -const float pi = PI; -const float RAD = DEG_TO_RAD; - -float JulianischesDatum(void) -{ - - int Gregor; - int Jahr = RtcTime.year; - int Monat = RtcTime.month; - int Tag = RtcTime.day_of_month; - - if (Monat <= 2) { - Monat += 12; - Jahr -= 1; - } - Gregor = (Jahr / 400) - (Jahr / 100) + (Jahr / 4); - return 2400000.5f + 365.0f*Jahr - 679004.0f + Gregor + (int)(30.6001f * (Monat +1)) + Tag + 0.5f; -} - -float InPi(float x) -{ - int n = (int)(x / pi2); - x = x - n*pi2; - if (x < 0) x += pi2; - return x; -} - -float eps(float T) -{ - - return RAD * (23.43929111f + (-46.8150f*T - 0.00059f*T*T + 0.001813f*T*T*T)/3600.0f); -} - -float BerechneZeitgleichung(float *DK,float T) -{ - float RA_Mittel = 18.71506921f + 2400.0513369f*T +(2.5862e-5f - 1.72e-9f*T)*T*T; - float M = InPi(pi2 * (0.993133f + 99.997361f*T)); - float L = InPi(pi2 * (0.7859453f + M/pi2 + (6893.0f*sinf(M)+72.0f*sinf(2.0f*M)+6191.2f*T) / 1296.0e3f)); - float e = eps(T); - float RA = atanf(tanf(L)*cosf(e)); - if (RA < 0.0) RA += pi; - if (L > pi) RA += pi; - RA = 24.0*RA/pi2; - *DK = asinf(sinf(e)*sinf(L)); - - RA_Mittel = 24.0f * InPi(pi2*RA_Mittel/24.0f)/pi2; - float dRA = RA_Mittel - RA; - if (dRA < -12.0f) dRA += 24.0f; - if (dRA > 12.0f) dRA -= 24.0f; - dRA = dRA * 1.0027379f; - return dRA; -} - -void DuskTillDawn(uint8_t *hour_up,uint8_t *minute_up, uint8_t *hour_down, uint8_t *minute_down) -{ - float JD2000 = 2451545.0f; - float JD = JulianischesDatum(); - float T = (JD - JD2000) / 36525.0f; - float DK; - - - - - - - - float h = SUNRISE_DAWN_ANGLE *RAD; - float B = (((float)Settings.latitude)/1000000) * RAD; - float GeographischeLaenge = ((float)Settings.longitude)/1000000; - - - - float Zeitzone = ((float)Rtc.time_timezone) / 60; - float Zeitgleichung = BerechneZeitgleichung(&DK, T); - float Zeitdifferenz = 12.0f*acosf((sinf(h) - sinf(B)*sinf(DK)) / (cosf(B)*cosf(DK)))/pi; - float AufgangOrtszeit = 12.0f - Zeitdifferenz - Zeitgleichung; - float UntergangOrtszeit = 12.0f + Zeitdifferenz - Zeitgleichung; - float AufgangWeltzeit = AufgangOrtszeit - GeographischeLaenge / 15.0f; - float UntergangWeltzeit = UntergangOrtszeit - GeographischeLaenge / 15.0f; - float Aufgang = AufgangWeltzeit + Zeitzone; - if (Aufgang < 0.0f) { - Aufgang += 24.0f; - } else { - if (Aufgang >= 24.0f) Aufgang -= 24.0f; - } - float Untergang = UntergangWeltzeit + Zeitzone; - if (Untergang < 0.0f) { - Untergang += 24.0f; - } else { - if (Untergang >= 24.0f) Untergang -= 24.0f; - } - int AufgangMinuten = (int)(60.0f*(Aufgang - (int)Aufgang)+0.5f); - int AufgangStunden = (int)Aufgang; - if (AufgangMinuten >= 60.0f) { - AufgangMinuten -= 60.0f; - AufgangStunden++; - } else { - if (AufgangMinuten < 0.0f) { - AufgangMinuten += 60.0f; - AufgangStunden--; - if (AufgangStunden < 0.0f) AufgangStunden += 24.0f; - } - } - int UntergangMinuten = (int)(60.0f*(Untergang - (int)Untergang)+0.5f); - int UntergangStunden = (int)Untergang; - if (UntergangMinuten >= 60.0f) { - UntergangMinuten -= 60.0f; - UntergangStunden++; - } else { - if (UntergangMinuten<0) { - UntergangMinuten += 60.0f; - UntergangStunden--; - if (UntergangStunden < 0.0f) UntergangStunden += 24.0f; - } - } - *hour_up = AufgangStunden; - *minute_up = AufgangMinuten; - *hour_down = UntergangStunden; - *minute_down = UntergangMinuten; -} - -void ApplyTimerOffsets(Timer *duskdawn) -{ - uint8_t hour[2]; - uint8_t minute[2]; - Timer stored = (Timer)*duskdawn; - - - DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); - uint8_t mode = (duskdawn->mode -1) &1; - duskdawn->time = (hour[mode] *60) + minute[mode]; - - - uint16_t timeBuffer; - if ((uint16_t)stored.time > 719) { - - timeBuffer = (uint16_t)stored.time - 720; - - if (timeBuffer > (uint16_t)duskdawn->time) { - timeBuffer = 1440 - (timeBuffer - (uint16_t)duskdawn->time); - duskdawn->days = duskdawn->days >> 1; - duskdawn->days |= (stored.days << 6); - } else { - timeBuffer = (uint16_t)duskdawn->time - timeBuffer; - } - } else { - - timeBuffer = (uint16_t)duskdawn->time + (uint16_t)stored.time; - - if (timeBuffer > 1440) { - timeBuffer -= 1440; - duskdawn->days = duskdawn->days << 1; - duskdawn->days |= (stored.days >> 6); - } - } - duskdawn->time = timeBuffer; -} - -String GetSun(uint32_t dawn) -{ - char stime[6]; - - uint8_t hour[2]; - uint8_t minute[2]; - - DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); - dawn &= 1; - snprintf_P(stime, sizeof(stime), PSTR("%02d:%02d"), hour[dawn], minute[dawn]); - return String(stime); -} - -uint16_t SunMinutes(uint32_t dawn) -{ - uint8_t hour[2]; - uint8_t minute[2]; - - DuskTillDawn(&hour[0], &minute[0], &hour[1], &minute[1]); - dawn &= 1; - return (hour[dawn] *60) + minute[dawn]; -} - -#endif - - - -void TimerSetRandomWindow(uint32_t index) -{ - timer_window[index] = 0; - if (Settings.timer[index].window) { - timer_window[index] = (random(0, (Settings.timer[index].window << 1) +1)) - Settings.timer[index].window; - } -} - -void TimerSetRandomWindows(void) -{ - for (uint32_t i = 0; i < MAX_TIMERS; i++) { TimerSetRandomWindow(i); } -} - -void TimerEverySecond(void) -{ - if (RtcTime.valid) { - if (!RtcTime.hour && !RtcTime.minute && !RtcTime.second) { TimerSetRandomWindows(); } - if (Settings.flag3.timers_enable && - (uptime > 60) && (RtcTime.minute != timer_last_minute)) { - timer_last_minute = RtcTime.minute; - int32_t time = (RtcTime.hour *60) + RtcTime.minute; - uint8_t days = 1 << (RtcTime.day_of_week -1); - - for (uint32_t i = 0; i < MAX_TIMERS; i++) { - - Timer xtimer = Settings.timer[i]; -#ifdef USE_SUNRISE - if ((1 == xtimer.mode) || (2 == xtimer.mode)) { - ApplyTimerOffsets(&xtimer); - } -#endif - if (xtimer.arm) { - int32_t set_time = xtimer.time + timer_window[i]; - if (set_time < 0) { - set_time = abs(timer_window[i]); - } - if (set_time > 1439) { - set_time = xtimer.time - abs(timer_window[i]); - } - if (set_time > 1439) { set_time = 1439; } - - DEBUG_DRIVER_LOG(PSTR("TIM: Timer %d, Time %d, Window %d, SetTime %d"), i +1, xtimer.time, timer_window[i], set_time); - - if (time == set_time) { - if (xtimer.days & days) { - Settings.timer[i].arm = xtimer.repeat; -#if defined(USE_RULES) || defined(USE_SCRIPT) - if (POWER_BLINK == xtimer.power) { - Response_P(PSTR("{\"Clock\":{\"Timer\":%d}}"), i +1); - XdrvRulesProcess(); - } else -#endif - if (devices_present) { ExecuteCommandPower(xtimer.device +1, xtimer.power, SRC_TIMER); } - } - } - } - } - } - } -} - -void PrepShowTimer(uint32_t index) -{ - Timer xtimer = Settings.timer[index -1]; - - char days[8] = { 0 }; - for (uint32_t i = 0; i < 7; i++) { - uint8_t mask = 1 << i; - snprintf(days, sizeof(days), "%s%d", days, ((xtimer.days & mask) > 0)); - } - - char soutput[80]; - soutput[0] = '\0'; - if (devices_present) { - snprintf_P(soutput, sizeof(soutput), PSTR(",\"" D_JSON_TIMER_OUTPUT "\":%d"), xtimer.device +1); - } -#ifdef USE_SUNRISE - char sign[2] = { 0 }; - int16_t hour = xtimer.time / 60; - if ((1 == xtimer.mode) || (2 == xtimer.mode)) { - if (hour > 11) { - hour -= 12; - sign[0] = '-'; - } - } - ResponseAppend_P(PSTR("\"" D_CMND_TIMER "%d\":{\"" D_JSON_TIMER_ARM "\":%d,\"" D_JSON_TIMER_MODE "\":%d,\"" D_JSON_TIMER_TIME "\":\"%s%02d:%02d\",\"" D_JSON_TIMER_WINDOW "\":%d,\"" D_JSON_TIMER_DAYS "\":\"%s\",\"" D_JSON_TIMER_REPEAT "\":%d%s,\"" D_JSON_TIMER_ACTION "\":%d}"), - index, xtimer.arm, xtimer.mode, sign, hour, xtimer.time % 60, xtimer.window, days, xtimer.repeat, soutput, xtimer.power); -#else - ResponseAppend_P(PSTR("\"" D_CMND_TIMER "%d\":{\"" D_JSON_TIMER_ARM "\":%d,\"" D_JSON_TIMER_TIME "\":\"%02d:%02d\",\"" D_JSON_TIMER_WINDOW "\":%d,\"" D_JSON_TIMER_DAYS "\":\"%s\",\"" D_JSON_TIMER_REPEAT "\":%d%s,\"" D_JSON_TIMER_ACTION "\":%d}"), - index, xtimer.arm, xtimer.time / 60, xtimer.time % 60, xtimer.window, days, xtimer.repeat, soutput, xtimer.power); -#endif -} - - - - - -void CmndTimer(void) -{ - uint32_t index = XdrvMailbox.index; - if ((index > 0) && (index <= MAX_TIMERS)) { - uint32_t error = 0; - if (XdrvMailbox.data_len) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= MAX_TIMERS)) { - if (XdrvMailbox.payload == 0) { - Settings.timer[index -1].data = 0; - } else { - Settings.timer[index -1].data = Settings.timer[XdrvMailbox.payload -1].data; - } - } else { - -#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 - if (devices_present) { -#endif - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - StaticJsonBuffer<256> jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(dataBufUc); - if (!root.success()) { - Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); - error = 1; - } - else { - char parm_uc[10]; - index--; - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ARM))].success()) { - Settings.timer[index].arm = (root[parm_uc] != 0); - } -#ifdef USE_SUNRISE - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_MODE))].success()) { - Settings.timer[index].mode = (uint8_t)root[parm_uc] & 0x03; - } -#endif - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_TIME))].success()) { - uint16_t itime = 0; - int8_t value = 0; - uint8_t sign = 0; - char time_str[10]; - - strlcpy(time_str, root[parm_uc], sizeof(time_str)); - const char *substr = strtok(time_str, ":"); - if (substr != nullptr) { - if (strchr(substr, '-')) { - sign = 1; - substr++; - } - value = atoi(substr); - if (sign) { value += 12; } - if (value > 23) { value = 23; } - itime = value * 60; - substr = strtok(nullptr, ":"); - if (substr != nullptr) { - value = atoi(substr); - if (value < 0) { value = 0; } - if (value > 59) { value = 59; } - itime += value; - } - } - Settings.timer[index].time = itime; - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_WINDOW))].success()) { - Settings.timer[index].window = (uint8_t)root[parm_uc] & 0x0F; - TimerSetRandomWindow(index); - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_DAYS))].success()) { - - Settings.timer[index].days = 0; - const char *tday = root[parm_uc]; - uint8_t i = 0; - char ch = *tday++; - while ((ch != '\0') && (i < 7)) { - if (ch == '-') { ch = '0'; } - uint8_t mask = 1 << i++; - Settings.timer[index].days |= (ch == '0') ? 0 : mask; - ch = *tday++; - } - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_REPEAT))].success()) { - Settings.timer[index].repeat = (root[parm_uc] != 0); - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_OUTPUT))].success()) { - uint8_t device = ((uint8_t)root[parm_uc] -1) & 0x0F; - Settings.timer[index].device = (device < devices_present) ? device : 0; - } - if (root[UpperCase_P(parm_uc, PSTR(D_JSON_TIMER_ACTION))].success()) { - uint8_t action = (uint8_t)root[parm_uc] & 0x03; - Settings.timer[index].power = (devices_present) ? action : 3; - } - - index++; - } - -#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 - } else { - Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_TIMER_NO_DEVICE "\"}"), index); - error = 1; - } -#endif - } - } - if (!error) { - Response_P(PSTR("{")); - PrepShowTimer(index); - ResponseJsonEnd(); - } - } -} - -void CmndTimers(void) -{ - if (XdrvMailbox.data_len) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - Settings.flag3.timers_enable = XdrvMailbox.payload; - } - if (XdrvMailbox.payload == 2) { - Settings.flag3.timers_enable = !Settings.flag3.timers_enable; - } - } - - ResponseCmndStateText(Settings.flag3.timers_enable); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, XdrvMailbox.command); - - uint32_t jsflg = 0; - uint32_t lines = 1; - for (uint32_t i = 0; i < MAX_TIMERS; i++) { - if (!jsflg) { - Response_P(PSTR("{\"" D_CMND_TIMERS "%d\":{"), lines++); - } else { - ResponseAppend_P(PSTR(",")); - } - jsflg++; - PrepShowTimer(i +1); - if (jsflg > 3) { - ResponseJsonEndEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_TIMERS)); - jsflg = 0; - } - } - mqtt_data[0] = '\0'; -} - -#ifdef USE_SUNRISE -void CmndLongitude(void) -{ - if (XdrvMailbox.data_len) { - Settings.longitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); - } - ResponseCmndFloat((float)(Settings.longitude) /1000000, 6); -} - -void CmndLatitude(void) -{ - if (XdrvMailbox.data_len) { - Settings.latitude = (int)(CharToFloat(XdrvMailbox.data) *1000000); - } - ResponseCmndFloat((float)(Settings.latitude) /1000000, 6); -} -#endif - - - - - -#ifdef USE_WEBSERVER -#ifdef USE_TIMERS_WEB - -#define WEB_HANDLE_TIMER "tm" - -const char S_CONFIGURE_TIMER[] PROGMEM = D_CONFIGURE_TIMER; - -const char HTTP_BTN_MENU_TIMER[] PROGMEM = - "

"; - -const char HTTP_TIMER_SCRIPT1[] PROGMEM = - "var pt=[],ct=99;" - "function ce(i,q){" - "var o=document.createElement('option');" - "o.textContent=i;" - "q.appendChild(o);" - "}"; -#ifdef USE_SUNRISE -const char HTTP_TIMER_SCRIPT2[] PROGMEM = - "function gt(){" - "var m,p,q;" - "m=qs('input[name=\"rd\"]:checked').value;" - "p=pt[ct]&0x7FF;" - "if(m==0){" - "so(0);" - "q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" - "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" - "}" - "if((m==1)||(m==2)){" - "so(1);" - "q=Math.floor(p/60);" - "if(q>=12){q-=12;qs('#dr').selectedIndex=1;}" - "else{qs('#dr').selectedIndex=0;}" - "if(q<10){q='0'+q;}qs('#ho').value=q;" - "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" - "}" - "}" - "function so(b){" - "o=qs('#ho');" - "e=o.childElementCount;" - "if(b==1){" - "qs('#dr').style.visibility='';" - "if(e>12){for(i=12;i<=23;i++){o.removeChild(o.lastElementChild);}}" - "}else{" - "qs('#dr').style.visibility='hidden';" - "if(e<23){for(i=12;i<=23;i++){ce(i,o);}}" - "}" - "}"; -#endif -const char HTTP_TIMER_SCRIPT3[] PROGMEM = - "function st(){" - "var i,l,m,n,p,s;" - "m=0;s=0;" - "n=1<<31;if(eb('a0').checked){s|=n;}" - "n=1<<15;if(eb('r0').checked){s|=n;}" - "for(i=0;i<7;i++){n=1<<(16+i);if(eb('w'+i).checked){s|=n;}}" -#ifdef USE_SUNRISE - "m=qs('input[name=\"rd\"]:checked').value;" - "s|=(qs('input[name=\"rd\"]:checked').value<<29);" -#endif - "if(%d>0){" - "i=qs('#d1').selectedIndex;if(i>=0){s|=(i<<23);}" - "s|=(qs('#p1').selectedIndex<<27);" - "}else{" - "s|=3<<27;" - "}" - "l=((qs('#ho').selectedIndex*60)+qs('#mi').selectedIndex)&0x7FF;" - "if(m==0){s|=l;}" -#ifdef USE_SUNRISE - "if((m==1)||(m==2)){" - "if(qs('#dr').selectedIndex>0){if(l>0){l+=720;}}" - "s|=l&0x7FF;" - "}" -#endif - "s|=((qs('#mw').selectedIndex)&0x0F)<<11;" - "pt[ct]=s;" - "eb('t0').value=pt.join();" - "}"; -const char HTTP_TIMER_SCRIPT4[] PROGMEM = - "function ot(t,e){" - "var i,n,o,p,q,s;" - "if(ct<99){st();}" - "ct=t;" - "o=document.getElementsByClassName('tl');" - "for(i=0;i>29)&3;eb('b'+p).checked=1;" - "gt();" -#else - "p=s&0x7FF;" - "q=Math.floor(p/60);if(q<10){q='0'+q;}qs('#ho').value=q;" - "q=p%%60;if(q<10){q='0'+q;}qs('#mi').value=q;" -#endif - "q=(s>>11)&0xF;if(q<10){q='0'+q;}qs('#mw').value=q;" - "for(i=0;i<7;i++){p=(s>>(16+i))&1;eb('w'+i).checked=p;}" - "if(%d>0){" - "p=(s>>23)&0xF;qs('#d1').value=p+1;" - "p=(s>>27)&3;qs('#p1').selectedIndex=p;" - "}" - "p=(s>>15)&1;eb('r0').checked=p;" - "p=(s>>31)&1;eb('a0').checked=p;" - "}"; -const char HTTP_TIMER_SCRIPT5[] PROGMEM = - "function it(){" - "var b,i,o,s;" - "pt=eb('t0').value.split(',').map(Number);" - "s='';" - "for(i=0;i<%d;i++){" - "b='';" - "if(0==i){b=\" id='dP'\";}" - "s+=\"\"" - "}" - "eb('bt').innerHTML=s;" - "if(%d>0){" - "eb('oa').innerHTML=\"" D_TIMER_OUTPUT " " D_TIMER_ACTION " \";" - "o=qs('#p1');ce('" D_OFF "',o);ce('" D_ON "',o);ce('" D_TOGGLE "',o);" -#if defined(USE_RULES) || defined(USE_SCRIPT) - "ce('" D_RULE "',o);" -#else - "ce('" D_BLINK "',o);" -#endif - "}else{" - "eb('oa').innerHTML=\"" D_TIMER_ACTION " " D_RULE "\";" - "}"; -const char HTTP_TIMER_SCRIPT6[] PROGMEM = -#ifdef USE_SUNRISE - "o=qs('#dr');ce('+',o);ce('-',o);" -#endif - "o=qs('#ho');for(i=0;i<=23;i++){ce((i<10)?('0'+i):i,o);}" - "o=qs('#mi');for(i=0;i<=59;i++){ce((i<10)?('0'+i):i,o);}" - "o=qs('#mw');for(i=0;i<=15;i++){ce((i<10)?('0'+i):i,o);}" - "o=qs('#d1');for(i=0;i<%d;i++){ce(i+1,o);}" - "var a='" D_DAY3LIST "';" - "s='';for(i=0;i<7;i++){s+=\"\"+a.substring(i*3,(i*3)+3)+\" \"}" - "eb('ds').innerHTML=s;" - "eb('dP').click();" - "}" - "wl(it);"; -const char HTTP_TIMER_STYLE[] PROGMEM = - ".tl{float:left;border-radius:0;border:1px solid #%06x;padding:1px;width:6.25%%;}"; -const char HTTP_FORM_TIMER1[] PROGMEM = - "
" - " " D_TIMER_PARAMETERS " " - "
" - "
" D_TIMER_ENABLE "


" - "



" - "

" - "
" - "" D_TIMER_ARM " " - "" D_TIMER_REPEAT "" - "

" - "
"; -#ifdef USE_SUNRISE -const char HTTP_FORM_TIMER3[] PROGMEM = - "
" - "" D_TIMER_TIME "
" - "" D_SUNRISE " (%s)
" - "" D_SUNSET " (%s)
" - "
" - "

" - "" - " "; -#else -const char HTTP_FORM_TIMER3[] PROGMEM = - "" D_TIMER_TIME " "; -#endif -const char HTTP_FORM_TIMER4[] PROGMEM = - "" - " " D_HOUR_MINUTE_SEPARATOR " " - "" - " +/- " - "" - "

" - "
"; - -void HandleTimerConfiguration(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TIMER); - - if (WebServer->hasArg("save")) { - TimerSaveSettings(); - HandleConfiguration(); - return; - } - - WSContentStart_P(S_CONFIGURE_TIMER); - WSContentSend_P(HTTP_TIMER_SCRIPT1); -#ifdef USE_SUNRISE - WSContentSend_P(HTTP_TIMER_SCRIPT2); -#endif - WSContentSend_P(HTTP_TIMER_SCRIPT3, devices_present); - WSContentSend_P(HTTP_TIMER_SCRIPT4, WebColor(COL_TIMER_TAB_BACKGROUND), WebColor(COL_TIMER_TAB_TEXT), WebColor(COL_FORM), WebColor(COL_TEXT), devices_present); - WSContentSend_P(HTTP_TIMER_SCRIPT5, MAX_TIMERS, devices_present); - WSContentSend_P(HTTP_TIMER_SCRIPT6, devices_present); - WSContentSendStyle_P(HTTP_TIMER_STYLE, WebColor(COL_FORM)); - WSContentSend_P(HTTP_FORM_TIMER1, (Settings.flag3.timers_enable) ? " checked" : ""); - for (uint32_t i = 0; i < MAX_TIMERS; i++) { - WSContentSend_P(PSTR("%s%u"), (i > 0) ? "," : "", Settings.timer[i].data); - } - WSContentSend_P(HTTP_FORM_TIMER2); -#ifdef USE_SUNRISE - WSContentSend_P(HTTP_FORM_TIMER3, 100 + (strlen(D_SUNSET) *12), GetSun(0).c_str(), GetSun(1).c_str()); -#else - WSContentSend_P(HTTP_FORM_TIMER3); -#endif - WSContentSend_P(HTTP_FORM_TIMER4); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void TimerSaveSettings(void) -{ - char tmp[MAX_TIMERS *12]; - char message[LOGSZ]; - Timer timer; - - Settings.flag3.timers_enable = WebServer->hasArg("e0"); - WebGetArg("t0", tmp, sizeof(tmp)); - char *p = tmp; - snprintf_P(message, sizeof(message), PSTR(D_LOG_MQTT D_CMND_TIMERS " %d"), Settings.flag3.timers_enable); - for (uint32_t i = 0; i < MAX_TIMERS; i++) { - timer.data = strtol(p, &p, 10); - p++; - if (timer.time < 1440) { - bool flag = (timer.window != Settings.timer[i].window); - Settings.timer[i].data = timer.data; - if (flag) TimerSetRandomWindow(i); - } - snprintf_P(message, sizeof(message), PSTR("%s,0x%08X"), message, Settings.timer[i].data); - } - AddLog_P(LOG_LEVEL_DEBUG, message); -} -#endif -#endif - - - - - -bool Xdrv09(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_PRE_INIT: - TimerSetRandomWindows(); - break; -#ifdef USE_WEBSERVER -#ifdef USE_TIMERS_WEB - case FUNC_WEB_ADD_BUTTON: -#if defined(USE_RULES) || defined(USE_SCRIPT) - WSContentSend_P(HTTP_BTN_MENU_TIMER); -#else - if (devices_present) { WSContentSend_P(HTTP_BTN_MENU_TIMER); } -#endif - break; - case FUNC_WEB_ADD_HANDLER: - WebServer->on("/" WEB_HANDLE_TIMER, HandleTimerConfiguration); - break; -#endif -#endif - case FUNC_EVERY_SECOND: - TimerEverySecond(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kTimerCommands, TimerCommand); - break; - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -#ifdef USE_RULES -#ifndef USE_SCRIPT -# 67 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -#define XDRV_10 10 - -#define D_CMND_RULE "Rule" -#define D_CMND_RULETIMER "RuleTimer" -#define D_CMND_EVENT "Event" -#define D_CMND_VAR "Var" -#define D_CMND_MEM "Mem" -#define D_CMND_ADD "Add" -#define D_CMND_SUB "Sub" -#define D_CMND_MULT "Mult" -#define D_CMND_SCALE "Scale" -#define D_CMND_CALC_RESOLUTION "CalcRes" -#define D_CMND_SUBSCRIBE "Subscribe" -#define D_CMND_UNSUBSCRIBE "Unsubscribe" -#define D_CMND_IF "If" - -#define D_JSON_INITIATED "Initiated" - -#define COMPARE_OPERATOR_NONE -1 -#define COMPARE_OPERATOR_EQUAL 0 -#define COMPARE_OPERATOR_BIGGER 1 -#define COMPARE_OPERATOR_SMALLER 2 -#define COMPARE_OPERATOR_EXACT_DIVISION 3 -#define COMPARE_OPERATOR_NUMBER_EQUAL 4 -#define COMPARE_OPERATOR_NOT_EQUAL 5 -#define COMPARE_OPERATOR_BIGGER_EQUAL 6 -#define COMPARE_OPERATOR_SMALLER_EQUAL 7 -#define MAXIMUM_COMPARE_OPERATOR COMPARE_OPERATOR_SMALLER_EQUAL -const char kCompareOperators[] PROGMEM = "=\0>\0<\0|\0==!=>=<="; - -#ifdef USE_EXPRESSION - #include - - const char kExpressionOperators[] PROGMEM = "+-*/%^\0"; - #define EXPRESSION_OPERATOR_ADD 0 - #define EXPRESSION_OPERATOR_SUBTRACT 1 - #define EXPRESSION_OPERATOR_MULTIPLY 2 - #define EXPRESSION_OPERATOR_DIVIDEDBY 3 - #define EXPRESSION_OPERATOR_MODULO 4 - #define EXPRESSION_OPERATOR_POWER 5 - - const uint8_t kExpressionOperatorsPriorities[] PROGMEM = {1, 1, 2, 2, 3, 4}; - #define MAX_EXPRESSION_OPERATOR_PRIORITY 4 - - - #define LOGIC_OPERATOR_AND 1 - #define LOGIC_OPERATOR_OR 2 - - #define IF_BLOCK_INVALID -1 - #define IF_BLOCK_ANY 0 - #define IF_BLOCK_ELSEIF 1 - #define IF_BLOCK_ELSE 2 - #define IF_BLOCK_ENDIF 3 -#endif - -const char kRulesCommands[] PROGMEM = "|" - D_CMND_RULE "|" D_CMND_RULETIMER "|" D_CMND_EVENT "|" D_CMND_VAR "|" D_CMND_MEM "|" - D_CMND_ADD "|" D_CMND_SUB "|" D_CMND_MULT "|" D_CMND_SCALE "|" D_CMND_CALC_RESOLUTION -#ifdef SUPPORT_MQTT_EVENT - "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE -#endif -#ifdef SUPPORT_IF_STATEMENT - "|" D_CMND_IF -#endif - ; - -void (* const RulesCommand[])(void) PROGMEM = { - &CmndRule, &CmndRuleTimer, &CmndEvent, &CmndVariable, &CmndMemory, - &CmndAddition, &CmndSubtract, &CmndMultiply, &CmndScale, &CmndCalcResolution -#ifdef SUPPORT_MQTT_EVENT - , &CmndSubscribe, &CmndUnsubscribe -#endif -#ifdef SUPPORT_IF_STATEMENT - , &CmndIf -#endif - }; - -#ifdef SUPPORT_MQTT_EVENT - #include - typedef struct { - String Event; - String Topic; - String Key; - } MQTT_Subscription; - LinkedList subscriptions; -#endif - -struct RULES { - String event_value; - unsigned long timer[MAX_RULE_TIMERS] = { 0 }; - uint32_t triggers[MAX_RULE_SETS] = { 0 }; - uint8_t trigger_count[MAX_RULE_SETS] = { 0 }; - - long new_power = -1; - long old_power = -1; - long old_dimm = -1; - - uint16_t last_minute = 60; - uint16_t vars_event = 0; - uint8_t mems_event = 0; - bool teleperiod = false; - - char event_data[100]; -} Rules; - -char rules_vars[MAX_RULE_VARS][33] = {{ 0 }}; - -#if (MAX_RULE_VARS>16) -#error MAX_RULE_VARS is bigger than 16 -#endif -#if (MAX_RULE_MEMS>16) -#error MAX_RULE_MEMS is bigger than 16 -#endif - - - -bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) -{ - - - - - bool match = false; - char stemp[10]; - - - int pos = rule.indexOf('#'); - if (pos == -1) { return false; } - - String rule_task = rule.substring(0, pos); - if (Rules.teleperiod) { - int ppos = rule_task.indexOf("TELE-"); - if (ppos == -1) { return false; } - rule_task = rule.substring(5, pos); - } - - String rule_expr = rule.substring(pos +1); - String rule_name, rule_param; - int8_t compareOperator = parseCompareExpression(rule_expr, rule_name, rule_param); - - char rule_svalue[80] = { 0 }; - float rule_value = 0; - if (compareOperator != COMPARE_OPERATOR_NONE) { - for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); - if (rule_param.startsWith(stemp)) { - rule_param = rules_vars[i]; - break; - } - } - for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); - if (rule_param.startsWith(stemp)) { - rule_param = SettingsText(SET_MEM1 + i); - break; - } - } - snprintf_P(stemp, sizeof(stemp), PSTR("%%TIME%%")); - if (rule_param.startsWith(stemp)) { - rule_param = String(MinutesPastMidnight()); - } - snprintf_P(stemp, sizeof(stemp), PSTR("%%UPTIME%%")); - if (rule_param.startsWith(stemp)) { - rule_param = String(MinutesUptime()); - } - snprintf_P(stemp, sizeof(stemp), PSTR("%%TIMESTAMP%%")); - if (rule_param.startsWith(stemp)) { - rule_param = GetDateAndTime(DT_LOCAL).c_str(); - } -#if defined(USE_TIMERS) && defined(USE_SUNRISE) - snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNRISE%%")); - if (rule_param.startsWith(stemp)) { - rule_param = String(SunMinutes(0)); - } - snprintf_P(stemp, sizeof(stemp), PSTR("%%SUNSET%%")); - if (rule_param.startsWith(stemp)) { - rule_param = String(SunMinutes(1)); - } -#endif - rule_param.toUpperCase(); - strlcpy(rule_svalue, rule_param.c_str(), sizeof(rule_svalue)); - - int temp_value = GetStateNumber(rule_svalue); - if (temp_value > -1) { - rule_value = temp_value; - } else { - rule_value = CharToFloat((char*)rule_svalue); - } - } - - - int rule_name_idx = 0; - if ((pos = rule_name.indexOf("[")) > 0) { - rule_name_idx = rule_name.substring(pos +1).toInt(); - if ((rule_name_idx < 1) || (rule_name_idx > 6)) { - rule_name_idx = 1; - } - rule_name = rule_name.substring(0, pos); - } - - StaticJsonBuffer<1024> jsonBuf; - JsonObject &root = jsonBuf.parseObject(event); - if (!root.success()) { return false; } - if (!root[rule_task].success()) { return false; } - - JsonObject &obj1 = root[rule_task]; - JsonObject *obj = &obj1; - String subtype; - uint32_t i = 0; - while ((pos = rule_name.indexOf("#")) > 0) { - subtype = rule_name.substring(0, pos); - if (!(*obj)[subtype].success()) { return false; } - JsonObject &obj2 = (*obj)[subtype]; - obj = &obj2; - rule_name = rule_name.substring(pos +1); - if (i++ > 10) { return false; } - } - if (!(*obj)[rule_name].success()) { return false; } - const char* str_value; - if (rule_name_idx) { - str_value = (*obj)[rule_name][rule_name_idx -1]; - } else { - str_value = (*obj)[rule_name]; - } - - - - - Rules.event_value = str_value; - - - float value = 0; - if (str_value) { - value = CharToFloat((char*)str_value); - int int_value = int(value); - int int_rule_value = int(rule_value); - switch (compareOperator) { - case COMPARE_OPERATOR_EXACT_DIVISION: - match = (int_rule_value && (int_value % int_rule_value) == 0); - break; - case COMPARE_OPERATOR_EQUAL: - match = (!strcasecmp(str_value, rule_svalue)); - break; - case COMPARE_OPERATOR_BIGGER: - match = (value > rule_value); - break; - case COMPARE_OPERATOR_SMALLER: - match = (value < rule_value); - break; - case COMPARE_OPERATOR_NUMBER_EQUAL: - match = (value == rule_value); - break; - case COMPARE_OPERATOR_NOT_EQUAL: - match = (value != rule_value); - break; - case COMPARE_OPERATOR_BIGGER_EQUAL: - match = (value >= rule_value); - break; - case COMPARE_OPERATOR_SMALLER_EQUAL: - match = (value <= rule_value); - break; - default: - match = true; - } - } else match = true; - - if (bitRead(Settings.rule_once, rule_set)) { - if (match) { - if (!bitRead(Rules.triggers[rule_set], Rules.trigger_count[rule_set])) { - bitSet(Rules.triggers[rule_set], Rules.trigger_count[rule_set]); - } else { - match = false; - } - } else { - bitClear(Rules.triggers[rule_set], Rules.trigger_count[rule_set]); - } - } - - return match; -} -# 363 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -int8_t parseCompareExpression(String &expr, String &leftExpr, String &rightExpr) -{ - char compare_operator[3]; - int8_t compare = COMPARE_OPERATOR_NONE; - leftExpr = expr; - int position; - for (int8_t i = MAXIMUM_COMPARE_OPERATOR; i >= 0; i--) { - snprintf_P(compare_operator, sizeof(compare_operator), kCompareOperators + (i *2)); - if ((position = expr.indexOf(compare_operator)) > 0) { - compare = i; - leftExpr = expr.substring(0, position); - leftExpr.trim(); - rightExpr = expr.substring(position + strlen(compare_operator)); - rightExpr.trim(); - break; - } - } - return compare; -} - -void RulesVarReplace(String &commands, const String &sfind, const String &replace) -{ - - - - char *find = (char*)sfind.c_str(); - uint32_t flen = strlen(find); - - String ucommand = commands; - ucommand.toUpperCase(); - char *read_from = (char*)ucommand.c_str(); - char *write_to = (char*)commands.c_str(); - char *found_at; - while ((found_at = strstr(read_from, find)) != nullptr) { - write_to += (found_at - read_from); - memmove_P(write_to, find, flen); - write_to += flen; - read_from = found_at + flen; - } - - commands.replace(find, replace); -} - - - -bool RuleSetProcess(uint8_t rule_set, String &event_saved) -{ - bool serviced = false; - char stemp[10]; - - delay(0); - - - - String rules = Settings.rules[rule_set]; - - Rules.trigger_count[rule_set] = 0; - int plen = 0; - int plen2 = 0; - bool stop_all_rules = false; - while (true) { - rules = rules.substring(plen); - rules.trim(); - if (!rules.length()) { return serviced; } - - String rule = rules; - rule.toUpperCase(); - if (!rule.startsWith("ON ")) { return serviced; } - - int pevt = rule.indexOf(" DO "); - if (pevt == -1) { return serviced; } - String event_trigger = rule.substring(3, pevt); - - plen = rule.indexOf(" ENDON"); - plen2 = rule.indexOf(" BREAK"); - if ((plen == -1) && (plen2 == -1)) { return serviced; } - - if (plen == -1) { plen = 9999; } - if (plen2 == -1) { plen2 = 9999; } - plen = tmin(plen, plen2); - - String commands = rules.substring(pevt +4, plen); - Rules.event_value = ""; - String event = event_saved; - - - - if (RulesRuleMatch(rule_set, event, event_trigger)) { - if (plen == plen2) { stop_all_rules = true; } - commands.trim(); - String ucommand = commands; - ucommand.toUpperCase(); - - - - if ((ucommand.indexOf("IF ") == -1) && - (ucommand.indexOf("EVENT ") != -1) && - (ucommand.indexOf("BACKLOG ") == -1)) { - commands = "backlog " + commands; - } - - RulesVarReplace(commands, F("%VALUE%"), Rules.event_value); - for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%%VAR%d%%"), i +1); - RulesVarReplace(commands, stemp, rules_vars[i]); - } - for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%%MEM%d%%"), i +1); - RulesVarReplace(commands, stemp, SettingsText(SET_MEM1 +i)); - } - RulesVarReplace(commands, F("%TIME%"), String(MinutesPastMidnight())); - RulesVarReplace(commands, F("%UPTIME%"), String(MinutesUptime())); - RulesVarReplace(commands, F("%TIMESTAMP%"), GetDateAndTime(DT_LOCAL)); - RulesVarReplace(commands, F("%TOPIC%"), SettingsText(SET_MQTT_TOPIC)); -#if defined(USE_TIMERS) && defined(USE_SUNRISE) - RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0))); - RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1))); -#endif - - char command[commands.length() +1]; - strlcpy(command, commands.c_str(), sizeof(command)); - - AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: %s performs \"%s\""), event_trigger.c_str(), command); - - - -#ifdef SUPPORT_IF_STATEMENT - char *pCmd = command; - RulesPreprocessCommand(pCmd); -#endif - ExecuteCommand(command, SRC_RULE); - serviced = true; - if (stop_all_rules) { return serviced; } - } - plen += 6; - Rules.trigger_count[rule_set]++; - } - return serviced; -} - - - -bool RulesProcessEvent(char *json_event) -{ - bool serviced = false; - -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("RulesProcessEvent")); -#endif - - String event_saved = json_event; - - - - char *p = strchr(json_event, ':'); - if ((p != NULL) && !(strchr(++p, ':'))) { - event_saved.replace(F(":"), F(":{\"Data\":")); - event_saved += F("}"); - - } - event_saved.toUpperCase(); - - - - for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { - if (strlen(Settings.rules[i]) && bitRead(Settings.rule_enabled, i)) { - if (RuleSetProcess(i, event_saved)) { serviced = true; } - } - } - return serviced; -} - -bool RulesProcess(void) -{ - return RulesProcessEvent(mqtt_data); -} - -void RulesInit(void) -{ - rules_flag.data = 0; - for (uint32_t i = 0; i < MAX_RULE_SETS; i++) { - if (Settings.rules[i][0] == '\0') { - bitWrite(Settings.rule_enabled, i, 0); - bitWrite(Settings.rule_once, i, 0); - } - } - Rules.teleperiod = false; -} - -void RulesEvery50ms(void) -{ - if (Settings.rule_enabled) { - char json_event[120]; - - if (-1 == Rules.new_power) { Rules.new_power = power; } - if (Rules.new_power != Rules.old_power) { - if (Rules.old_power != -1) { - for (uint32_t i = 0; i < devices_present; i++) { - uint8_t new_state = (Rules.new_power >> i) &1; - if (new_state != ((Rules.old_power >> i) &1)) { - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"State\":%d}}"), i +1, new_state); - RulesProcessEvent(json_event); - } - } - } else { - - for (uint32_t i = 0; i < devices_present; i++) { - uint8_t new_state = (Rules.new_power >> i) &1; - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Power%d\":{\"Boot\":%d}}"), i +1, new_state); - RulesProcessEvent(json_event); - } - - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { -#ifdef USE_TM1638 - if ((pin[GPIO_SWT1 +i] < 99) || ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99))) { -#else - if (pin[GPIO_SWT1 +i] < 99) { -#endif - snprintf_P(json_event, sizeof(json_event), PSTR("{\"" D_JSON_SWITCH "%d\":{\"Boot\":%d}}"), i +1, (SwitchState(i))); - RulesProcessEvent(json_event); - } - } - } - Rules.old_power = Rules.new_power; - } - else if (Rules.old_dimm != Settings.light_dimmer) { - if (Rules.old_dimm != -1) { - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Dimmer\":{\"State\":%d}}"), Settings.light_dimmer); - } else { - - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Dimmer\":{\"Boot\":%d}}"), Settings.light_dimmer); - } - RulesProcessEvent(json_event); - Rules.old_dimm = Settings.light_dimmer; - } - else if (Rules.event_data[0]) { - char *event; - char *parameter; - event = strtok_r(Rules.event_data, "=", ¶meter); - if (event) { - event = Trim(event); - if (parameter) { - parameter = Trim(parameter); - } else { - parameter = event + strlen(event); - } - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Event\":{\"%s\":\"%s\"}}"), event, parameter); - Rules.event_data[0] ='\0'; - RulesProcessEvent(json_event); - } else { - Rules.event_data[0] ='\0'; - } - } - else if (Rules.vars_event || Rules.mems_event){ - if (Rules.vars_event) { - for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { - if (bitRead(Rules.vars_event, i)) { - bitClear(Rules.vars_event, i); - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Var%d\":{\"State\":%s}}"), i+1, rules_vars[i]); - RulesProcessEvent(json_event); - break; - } - } - } - if (Rules.mems_event) { - for (uint32_t i = 0; i < MAX_RULE_MEMS; i++) { - if (bitRead(Rules.mems_event, i)) { - bitClear(Rules.mems_event, i); - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Mem%d\":{\"State\":%s}}"), i+1, SettingsText(SET_MEM1 +i)); - RulesProcessEvent(json_event); - break; - } - } - } - } - else if (rules_flag.data) { - uint16_t mask = 1; - for (uint32_t i = 0; i < MAX_RULES_FLAG; i++) { - if (rules_flag.data & mask) { - rules_flag.data ^= mask; - json_event[0] = '\0'; - switch (i) { - case 0: strncpy_P(json_event, PSTR("{\"System\":{\"Boot\":1}}"), sizeof(json_event)); break; - case 1: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Initialized\":%d}}"), MinutesPastMidnight()); break; - case 2: snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Set\":%d}}"), MinutesPastMidnight()); break; - case 3: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Connected\":1}}"), sizeof(json_event)); break; - case 4: strncpy_P(json_event, PSTR("{\"MQTT\":{\"Disconnected\":1}}"), sizeof(json_event)); break; - case 5: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Connected\":1}}"), sizeof(json_event)); break; - case 6: strncpy_P(json_event, PSTR("{\"WIFI\":{\"Disconnected\":1}}"), sizeof(json_event)); break; - case 7: strncpy_P(json_event, PSTR("{\"HTTP\":{\"Initialized\":1}}"), sizeof(json_event)); break; -#ifdef USE_SHUTTER - case 8: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moved\":1}}"), sizeof(json_event)); break; - case 9: strncpy_P(json_event, PSTR("{\"SHUTTER\":{\"Moving\":1}}"), sizeof(json_event)); break; -#endif - } - if (json_event[0]) { - RulesProcessEvent(json_event); - break; - } - } - mask <<= 1; - } - } - } -} - -uint8_t rules_xsns_index = 0; - -void RulesEvery100ms(void) -{ - if (Settings.rule_enabled && (uptime > 4)) { - mqtt_data[0] = '\0'; - int tele_period_save = tele_period; - tele_period = 2; - XsnsNextCall(FUNC_JSON_APPEND, rules_xsns_index); - tele_period = tele_period_save; - if (strlen(mqtt_data)) { - mqtt_data[0] = '{'; - ResponseJsonEnd(); - RulesProcess(); - } - } -} - -void RulesEverySecond(void) -{ - if (Settings.rule_enabled) { - char json_event[120]; - - if (RtcTime.valid) { - if ((uptime > 60) && (RtcTime.minute != Rules.last_minute)) { - Rules.last_minute = RtcTime.minute; - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Time\":{\"Minute\":%d}}"), MinutesPastMidnight()); - RulesProcessEvent(json_event); - } - } - for (uint32_t i = 0; i < MAX_RULE_TIMERS; i++) { - if (Rules.timer[i] != 0L) { - if (TimeReached(Rules.timer[i])) { - Rules.timer[i] = 0L; - snprintf_P(json_event, sizeof(json_event), PSTR("{\"Rules\":{\"Timer\":%d}}"), i +1); - RulesProcessEvent(json_event); - } - } - } - } -} - -void RulesSaveBeforeRestart(void) -{ - if (Settings.rule_enabled) { - char json_event[32]; - - strncpy_P(json_event, PSTR("{\"System\":{\"Save\":1}}"), sizeof(json_event)); - RulesProcessEvent(json_event); - } -} - -void RulesSetPower(void) -{ - Rules.new_power = XdrvMailbox.index; -} - -void RulesTeleperiod(void) -{ - Rules.teleperiod = true; - RulesProcess(); - Rules.teleperiod = false; -} - -#ifdef SUPPORT_MQTT_EVENT -# 744 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -bool RulesMqttData(void) -{ - if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { - return false; - } - bool serviced = false; - String sTopic = XdrvMailbox.topic; - String sData = XdrvMailbox.data; - - MQTT_Subscription event_item; - - for (uint32_t index = 0; index < subscriptions.size(); index++) { - event_item = subscriptions.get(index); - - - if (sTopic.startsWith(event_item.Topic)) { - - serviced = true; - String value; - if (event_item.Key.length() == 0) { - value = sData; - } else { - StaticJsonBuffer<500> jsonBuf; - JsonObject& jsonData = jsonBuf.parseObject(sData); - String key1 = event_item.Key; - String key2; - if (!jsonData.success()) break; - int dot; - if ((dot = key1.indexOf('.')) > 0) { - key2 = key1.substring(dot+1); - key1 = key1.substring(0, dot); - if (!jsonData[key1][key2].success()) break; - value = (const char *)jsonData[key1][key2]; - } else { - if (!jsonData[key1].success()) break; - value = (const char *)jsonData[key1]; - } - } - value.trim(); - - snprintf_P(Rules.event_data, sizeof(Rules.event_data), PSTR("%s=%s"), event_item.Event.c_str(), value.c_str()); - } - } - return serviced; -} -# 806 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -void CmndSubscribe(void) -{ - MQTT_Subscription subscription_item; - String events; - if (XdrvMailbox.data_len > 0) { - char parameters[XdrvMailbox.data_len+1]; - memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len); - parameters[XdrvMailbox.data_len] = '\0'; - String event_name, topic, key; - - char * pos = strtok(parameters, ","); - if (pos) { - event_name = Trim(pos); - pos = strtok(nullptr, ","); - if (pos) { - topic = Trim(pos); - pos = strtok(nullptr, ","); - if (pos) { - key = Trim(pos); - } - } - } - - event_name.toUpperCase(); - if (event_name.length() > 0 && topic.length() > 0) { - - for (uint32_t index=0; index < subscriptions.size(); index++) { - if (subscriptions.get(index).Event.equals(event_name)) { - - String stopic = subscriptions.get(index).Topic + "/#"; - MqttUnsubscribe(stopic.c_str()); - subscriptions.remove(index); - break; - } - } - - if (!topic.endsWith("#")) { - if (topic.endsWith("/")) { - topic.concat("#"); - } else { - topic.concat("/#"); - } - } - - - subscription_item.Event = event_name; - subscription_item.Topic = topic.substring(0, topic.length() - 2); - subscription_item.Key = key; - subscriptions.add(subscription_item); - - MqttSubscribe(topic.c_str()); - events.concat(event_name + "," + topic - + (key.length()>0 ? "," : "") - + key); - } else { - events = D_JSON_WRONG_PARAMETERS; - } - } else { - - for (uint32_t index=0; index < subscriptions.size(); index++) { - subscription_item = subscriptions.get(index); - events.concat(subscription_item.Event + "," + subscription_item.Topic - + (subscription_item.Key.length()>0 ? "," : "") - + subscription_item.Key + "; "); - } - } - ResponseCmndChar(events.c_str()); -} -# 886 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -void CmndUnsubscribe(void) -{ - MQTT_Subscription subscription_item; - String events; - if (XdrvMailbox.data_len > 0) { - for (uint32_t index = 0; index < subscriptions.size(); index++) { - subscription_item = subscriptions.get(index); - if (subscription_item.Event.equalsIgnoreCase(XdrvMailbox.data)) { - String stopic = subscription_item.Topic + "/#"; - MqttUnsubscribe(stopic.c_str()); - events = subscription_item.Event; - subscriptions.remove(index); - break; - } - } - } else { - - String stopic; - while (subscriptions.size() > 0) { - events.concat(subscriptions.get(0).Event + "; "); - stopic = subscriptions.get(0).Topic + "/#"; - MqttUnsubscribe(stopic.c_str()); - subscriptions.remove(0); - } - } - ResponseCmndChar(events.c_str()); -} - -#endif - -#ifdef USE_EXPRESSION -# 928 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -char * findClosureBracket(char * pStart) -{ - char * pointer = pStart + 1; - - bool bFindClosures = false; - uint8_t matchClosures = 1; - while (*pointer) - { - if (*pointer == ')') { - matchClosures--; - if (matchClosures == 0) { - bFindClosures = true; - break; - } - } else if (*pointer == '(') { - matchClosures++; - } - pointer++; - } - if (bFindClosures) { - return pointer; - } else { - return nullptr; - } -} -# 967 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -bool findNextNumber(char * &pNumber, float &value) -{ - bool bSucceed = false; - String sNumber = ""; - if (*pNumber == '-') { - sNumber = "-"; - pNumber++; - } - while (*pNumber) { - if (isdigit(*pNumber) || (*pNumber == '.')) { - sNumber += *pNumber; - pNumber++; - } else { - break; - } - } - if (sNumber.length() > 0) { - value = CharToFloat(sNumber.c_str()); - bSucceed = true; - } - return bSucceed; -} -# 1003 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -bool findNextVariableValue(char * &pVarname, float &value) -{ - bool succeed = true; - value = 0; - String sVarName = ""; - while (*pVarname) { - if (isalpha(*pVarname) || isdigit(*pVarname)) { - sVarName.concat(*pVarname); - pVarname++; - } else { - break; - } - } - sVarName.toUpperCase(); - if (sVarName.startsWith(F("VAR"))) { - int index = sVarName.substring(3).toInt(); - if (index > 0 && index <= MAX_RULE_VARS) { - value = CharToFloat(rules_vars[index -1]); - } - } else if (sVarName.startsWith(F("MEM"))) { - int index = sVarName.substring(3).toInt(); - if (index > 0 && index <= MAX_RULE_MEMS) { - value = CharToFloat(SettingsText(SET_MEM1 + index -1)); - } - } else if (sVarName.equals(F("TIME"))) { - value = MinutesPastMidnight(); - } else if (sVarName.equals(F("UPTIME"))) { - value = MinutesUptime(); - } else if (sVarName.equals(F("UTCTIME"))) { - value = UtcTime(); - } else if (sVarName.equals(F("LOCALTIME"))) { - value = LocalTime(); -#if defined(USE_TIMERS) && defined(USE_SUNRISE) - } else if (sVarName.equals(F("SUNRISE"))) { - value = SunMinutes(0); - } else if (sVarName.equals(F("SUNSET"))) { - value = SunMinutes(1); -#endif - } else { - succeed = false; - } - - return succeed; -} -# 1065 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -bool findNextObjectValue(char * &pointer, float &value) -{ - bool bSucceed = false; - while (*pointer) - { - if (isspace(*pointer)) { - pointer++; - continue; - } - if (isdigit(*pointer) || (*pointer) == '-') { - bSucceed = findNextNumber(pointer, value); - break; - } else if (isalpha(*pointer)) { - bSucceed = findNextVariableValue(pointer, value); - break; - } else if (*pointer == '(') { - char * closureBracket = findClosureBracket(pointer); - if (closureBracket != nullptr) { - value = evaluateExpression(pointer+1, closureBracket - pointer - 1); - pointer = closureBracket + 1; - bSucceed = true; - } - break; - } else { - break; - } - } - return bSucceed; -} -# 1109 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -bool findNextOperator(char * &pointer, int8_t &op) -{ - bool bSucceed = false; - while (*pointer) - { - if (isspace(*pointer)) { - pointer++; - continue; - } - op = EXPRESSION_OPERATOR_ADD; - const char *pch = kExpressionOperators; - char ch; - while ((ch = pgm_read_byte(pch++)) != '\0') { - if (ch == *pointer) { - bSucceed = true; - pointer++; - break; - } - op++; - } - break; - } - return bSucceed; -} -# 1146 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -float calculateTwoValues(float v1, float v2, uint8_t op) -{ - switch (op) - { - case EXPRESSION_OPERATOR_ADD: - return v1 + v2; - case EXPRESSION_OPERATOR_SUBTRACT: - return v1 - v2; - case EXPRESSION_OPERATOR_MULTIPLY: - return v1 * v2; - case EXPRESSION_OPERATOR_DIVIDEDBY: - return (0 == v2) ? 0 : (v1 / v2); - case EXPRESSION_OPERATOR_MODULO: - return (0 == v2) ? 0 : (int(v1) % int(v2)); - case EXPRESSION_OPERATOR_POWER: - return FastPrecisePow(v1, v2); - } - return 0; -} -# 1199 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -float evaluateExpression(const char * expression, unsigned int len) -{ - char expbuf[len + 1]; - memcpy(expbuf, expression, len); - expbuf[len] = '\0'; - char * scan_pointer = expbuf; - - LinkedList object_values; - LinkedList operators; - int8_t op; - float va; - - if (findNextObjectValue(scan_pointer, va)) { - object_values.add(va); - } else { - return 0; - } - while (*scan_pointer) - { - if (findNextOperator(scan_pointer, op) - && *scan_pointer - && findNextObjectValue(scan_pointer, va)) - { - operators.add(op); - object_values.add(va); - } else { - - break; - } - } - - - - for (int32_t priority = MAX_EXPRESSION_OPERATOR_PRIORITY; priority>0; priority--) { - int index = 0; - while (index < operators.size()) { - if (priority == pgm_read_byte(kExpressionOperatorsPriorities + operators.get(index))) { - - va = calculateTwoValues(object_values.get(index), object_values.remove(index + 1), operators.remove(index)); - - object_values.set(index, va); - } else { - index++; - } - } - } - return object_values.get(0); -} -#endif - -#ifdef SUPPORT_IF_STATEMENT -void CmndIf(void) -{ - if (XdrvMailbox.data_len > 0) { - char parameters[XdrvMailbox.data_len+1]; - memcpy(parameters, XdrvMailbox.data, XdrvMailbox.data_len); - parameters[XdrvMailbox.data_len] = '\0'; - ProcessIfStatement(parameters); - } - ResponseCmndDone(); -} -# 1273 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -bool evaluateComparisonExpression(const char *expression, int len) -{ - bool bResult = true; - char expbuf[len + 1]; - memcpy(expbuf, expression, len); - expbuf[len] = '\0'; - String compare_expression = expbuf; - String leftExpr, rightExpr; - int8_t compareOp = parseCompareExpression(compare_expression, leftExpr, rightExpr); - - double leftValue = evaluateExpression(leftExpr.c_str(), leftExpr.length()); - double rightValue = evaluateExpression(rightExpr.c_str(), rightExpr.length()); - switch (compareOp) { - case COMPARE_OPERATOR_EXACT_DIVISION: - bResult = (rightValue != 0 && leftValue == int(leftValue) - && rightValue == int(rightValue) && (int(leftValue) % int(rightValue)) == 0); - break; - case COMPARE_OPERATOR_EQUAL: - bResult = leftExpr.equalsIgnoreCase(rightExpr); - break; - case COMPARE_OPERATOR_BIGGER: - bResult = (leftValue > rightValue); - break; - case COMPARE_OPERATOR_SMALLER: - bResult = (leftValue < rightValue); - break; - case COMPARE_OPERATOR_NUMBER_EQUAL: - bResult = (leftValue == rightValue); - break; - case COMPARE_OPERATOR_NOT_EQUAL: - bResult = (leftValue != rightValue); - break; - case COMPARE_OPERATOR_BIGGER_EQUAL: - bResult = (leftValue >= rightValue); - break; - case COMPARE_OPERATOR_SMALLER_EQUAL: - bResult = (leftValue <= rightValue); - break; - } - return bResult; -} -# 1329 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -bool findNextLogicOperator(char * &pointer, int8_t &op) -{ - bool bSucceed = false; - while (*pointer && isspace(*pointer)) { - - pointer++; - } - if (*pointer) { - if (strncasecmp_P(pointer, PSTR("AND "), 4) == 0) { - op = LOGIC_OPERATOR_AND; - pointer += 4; - bSucceed = true; - } else if (strncasecmp_P(pointer, PSTR("OR "), 3) == 0) { - op = LOGIC_OPERATOR_OR; - pointer += 3; - bSucceed = true; - } - } - return bSucceed; -} -# 1366 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -bool findNextLogicObjectValue(char * &pointer, bool &value) -{ - bool bSucceed = false; - while (*pointer && isspace(*pointer)) { - - pointer++; - } - char * pExpr = pointer; - while (*pointer) { - if (isalpha(*pointer) - && (strncasecmp_P(pointer, PSTR("AND "), 4) == 0 - || strncasecmp_P(pointer, PSTR("OR "), 3) == 0)) - { - value = evaluateComparisonExpression(pExpr, pointer - pExpr); - bSucceed = true; - break; - } else if (*pointer == '(') { - char * closureBracket = findClosureBracket(pointer); - if (closureBracket != nullptr) { - value = evaluateLogicalExpression(pointer+1, closureBracket - pointer - 1); - pointer = closureBracket + 1; - bSucceed = true; - } - break; - } - pointer++; - } - if (!bSucceed && pointer > pExpr) { - - value = evaluateComparisonExpression(pExpr, pointer - pExpr); - bSucceed = true; - } - return bSucceed; -} -# 1415 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -bool evaluateLogicalExpression(const char * expression, int len) -{ - bool bResult = false; - - char expbuff[len + 1]; - memcpy(expbuff, expression, len); - expbuff[len] = '\0'; - - - char * pointer = expbuff; - LinkedList values; - LinkedList logicOperators; - - bool bValue; - if (findNextLogicObjectValue(pointer, bValue)) { - values.add(bValue); - } else { - return false; - } - int8_t op; - while (*pointer) { - if (findNextLogicOperator(pointer, op) - && (*pointer) && findNextLogicObjectValue(pointer, bValue)) - { - logicOperators.add(op); - values.add(bValue); - } else { - break; - } - } - - int index = 0; - while (index < logicOperators.size()) { - if (logicOperators.get(index) == LOGIC_OPERATOR_AND) { - values.set(index, values.get(index) && values.get(index+1)); - values.remove(index + 1); - logicOperators.remove(index); - } else { - index++; - } - } - - index = 0; - while (index < logicOperators.size()) { - if (logicOperators.get(index) == LOGIC_OPERATOR_OR) { - values.set(index, values.get(index) || values.get(index+1)); - values.remove(index + 1); - logicOperators.remove(index); - } else { - index++; - } - } - return values.get(0); -} -# 1486 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -int8_t findIfBlock(char * &pointer, int &lenWord, int8_t block_type) -{ - int8_t foundBlock = IF_BLOCK_INVALID; - - const char * word; - while (*pointer) { - if (!isalpha(*pointer)) { - pointer++; - continue; - } - word = pointer; - while (*pointer && isalpha(*pointer)) { - pointer++; - } - lenWord = pointer - word; - - if (2 == lenWord && 0 == strncasecmp_P(word, PSTR("IF"), 2)) { - - - if (findIfBlock(pointer, lenWord, IF_BLOCK_ENDIF) != IF_BLOCK_ENDIF) { - - break; - } - } else if ( (IF_BLOCK_ENDIF == block_type || IF_BLOCK_ANY == block_type) - && (5 == lenWord) && (0 == strncasecmp_P(word, PSTR("ENDIF"), 5))) - { - - foundBlock = IF_BLOCK_ENDIF; - break; - } else if ( (IF_BLOCK_ELSEIF == block_type || IF_BLOCK_ANY == block_type) - && (6 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSEIF"), 6))) - { - - foundBlock = IF_BLOCK_ELSEIF; - break; - } else if ( (IF_BLOCK_ELSE == block_type || IF_BLOCK_ANY == block_type) - && (4 == lenWord) && (0 == strncasecmp_P(word, PSTR("ELSE"), 4))) - { - - foundBlock = IF_BLOCK_ELSE; - break; - } - } - return foundBlock; -} -# 1543 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -void ExecuteCommandBlock(const char * commands, int len) -{ - char cmdbuff[len + 1]; - memcpy(cmdbuff, commands, len); - cmdbuff[len] = '\0'; - - - char oneCommand[len + 1]; - int insertPosition = 0; - char * pos = cmdbuff; - int lenEndBlock = 0; - while (*pos) { - if (isspace(*pos) || '\x1e' == *pos || ';' == *pos) { - pos++; - continue; - } - if (strncasecmp_P(pos, PSTR("BACKLOG "), 8) == 0) { - - pos += 8; - continue; - } - if (strncasecmp_P(pos, PSTR("IF "), 3) == 0) { - - - char *pEndif = pos + 3; - if (IF_BLOCK_ENDIF != findIfBlock(pEndif, lenEndBlock, IF_BLOCK_ENDIF)) { - - break; - } - - memcpy(oneCommand, pos, pEndif - pos); - oneCommand[pEndif - pos] = '\0'; - pos = pEndif; - } else { - - char *pEndOfCommand = strpbrk(pos, "\x1e;"); - if (NULL == pEndOfCommand) { - pEndOfCommand = pos + strlen(pos); - } - memcpy(oneCommand, pos, pEndOfCommand - pos); - oneCommand[pEndOfCommand - pos] = '\0'; - pos = pEndOfCommand; - } - - - String sCurrentCommand = oneCommand; - sCurrentCommand.trim(); - if (sCurrentCommand.length() > 0 - && backlog.size() < MAX_BACKLOG && !backlog_mutex) - { - - backlog_mutex = true; - backlog.add(insertPosition, sCurrentCommand); - backlog_mutex = false; - insertPosition++; - } - } - return; -} -# 1613 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -void ProcessIfStatement(const char* statements) -{ - String conditionExpression; - int len = strlen(statements); - char statbuff[len + 1]; - memcpy(statbuff, statements, len + 1); - char *pos = statbuff; - int lenEndBlock = 0; - while (true) { - - - while (*pos && *pos != '(') { - pos++; - } - if (0 == *pos) { break; } - char * posEnd = findClosureBracket(pos); - - if (true == evaluateLogicalExpression(pos + 1, posEnd - (pos + 1))) { - - char * cmdBlockStart = posEnd + 1; - char * cmdBlockEnd = cmdBlockStart; - int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ANY); - if (IF_BLOCK_INVALID == nextBlock) { - - break; - } - ExecuteCommandBlock(cmdBlockStart, cmdBlockEnd - cmdBlockStart - lenEndBlock); - pos = cmdBlockEnd; - break; - } else { - pos = posEnd + 1; - int8_t nextBlock = findIfBlock(pos, lenEndBlock, IF_BLOCK_ANY); - if (IF_BLOCK_ELSEIF == nextBlock) { - - continue; - } else if (IF_BLOCK_ELSE == nextBlock) { - - char * cmdBlockEnd = pos; - int8_t nextBlock = findIfBlock(cmdBlockEnd, lenEndBlock, IF_BLOCK_ENDIF); - if (IF_BLOCK_ENDIF != nextBlock) { - - break; - } - ExecuteCommandBlock(pos, cmdBlockEnd - pos - lenEndBlock); - break; - } else { - - break; - } - } - } -} -# 1677 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_rules.ino" -void RulesPreprocessCommand(char *pCommands) -{ - char * cmd = pCommands; - int lenEndBlock = 0; - while (*cmd) { - - if (';' == *cmd || isspace(*cmd)) { - cmd++; - } - else if (strncasecmp_P(cmd, PSTR("IF "), 3) == 0) { - - char * pIfStart = cmd; - char * pIfEnd = pIfStart + 3; - - if (IF_BLOCK_ENDIF == findIfBlock(pIfEnd, lenEndBlock, IF_BLOCK_ENDIF)) { - - cmd = pIfEnd; - - - while (pIfStart < pIfEnd) { - if (';' == *pIfStart) - *pIfStart = '\x1e'; - pIfStart++; - } - } - else { - break; - } - } - else { - while (*cmd && ';' != *cmd) { - cmd++; - } - } - } - return; -} -#endif - - - - - -void CmndRule(void) -{ - uint8_t index = XdrvMailbox.index; - if ((index > 0) && (index <= MAX_RULE_SETS)) { - if ((XdrvMailbox.data_len > 0) && (XdrvMailbox.data_len < sizeof(Settings.rules[index -1]))) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 10)) { - switch (XdrvMailbox.payload) { - case 0: - case 1: - bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); - break; - case 2: - bitWrite(Settings.rule_enabled, index -1, bitRead(Settings.rule_enabled, index -1) ^1); - break; - case 4: - case 5: - bitWrite(Settings.rule_once, index -1, XdrvMailbox.payload &1); - break; - case 6: - bitWrite(Settings.rule_once, index -1, bitRead(Settings.rule_once, index -1) ^1); - break; - case 8: - case 9: - bitWrite(Settings.rule_stop, index -1, XdrvMailbox.payload &1); - break; - case 10: - bitWrite(Settings.rule_stop, index -1, bitRead(Settings.rule_stop, index -1) ^1); - break; - } - } else { - int offset = 0; - if ('+' == XdrvMailbox.data[0]) { - offset = strlen(Settings.rules[index -1]); - if (XdrvMailbox.data_len < (sizeof(Settings.rules[index -1]) - offset -1)) { - XdrvMailbox.data[0] = ' '; - } else { - offset = -1; - } - } - if (offset != -1) { - strlcpy(Settings.rules[index -1] + offset, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(Settings.rules[index -1])); - } - } - Rules.triggers[index -1] = 0; - } - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":\"%s\",\"Once\":\"%s\",\"StopOnError\":\"%s\",\"Free\":%d,\"Rules\":\"%s\"}"), - XdrvMailbox.command, index, GetStateText(bitRead(Settings.rule_enabled, index -1)), GetStateText(bitRead(Settings.rule_once, index -1)), - GetStateText(bitRead(Settings.rule_stop, index -1)), sizeof(Settings.rules[index -1]) - strlen(Settings.rules[index -1]) -1, Settings.rules[index -1]); - } -} - -void CmndRuleTimer(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_TIMERS)) { - if (XdrvMailbox.data_len > 0) { -#ifdef USE_EXPRESSION - float timer_set = evaluateExpression(XdrvMailbox.data, XdrvMailbox.data_len); - Rules.timer[XdrvMailbox.index -1] = (timer_set > 0) ? millis() + (1000 * timer_set) : 0; -#else - Rules.timer[XdrvMailbox.index -1] = (XdrvMailbox.payload > 0) ? millis() + (1000 * XdrvMailbox.payload) : 0; -#endif - } - mqtt_data[0] = '\0'; - for (uint32_t i = 0; i < MAX_RULE_TIMERS; i++) { - ResponseAppend_P(PSTR("%c\"T%d\":%d"), (i) ? ',' : '{', i +1, (Rules.timer[i]) ? (Rules.timer[i] - millis()) / 1000 : 0); - } - ResponseJsonEnd(); - } -} - -void CmndEvent(void) -{ - if (XdrvMailbox.data_len > 0) { - strlcpy(Rules.event_data, XdrvMailbox.data, sizeof(Rules.event_data)); - } - ResponseCmndDone(); -} - -void CmndVariable(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { - if (!XdrvMailbox.usridx) { - mqtt_data[0] = '\0'; - for (uint32_t i = 0; i < MAX_RULE_VARS; i++) { - ResponseAppend_P(PSTR("%c\"Var%d\":\"%s\""), (i) ? ',' : '{', i +1, rules_vars[i]); - } - ResponseJsonEnd(); - } else { - if (XdrvMailbox.data_len > 0) { -#ifdef USE_EXPRESSION - if (XdrvMailbox.data[0] == '=') { - dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); - } else { - strlcpy(rules_vars[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(rules_vars[XdrvMailbox.index -1])); - } -#else - strlcpy(rules_vars[XdrvMailbox.index -1], ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data, sizeof(rules_vars[XdrvMailbox.index -1])); -#endif - bitSet(Rules.vars_event, XdrvMailbox.index -1); - } - ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); - } - } -} - -void CmndMemory(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_MEMS)) { - if (!XdrvMailbox.usridx) { - ResponseCmndAll(SET_MEM1, MAX_RULE_MEMS); - } else { - if (XdrvMailbox.data_len > 0) { -#ifdef USE_EXPRESSION - if (XdrvMailbox.data[0] == '=') { - dtostrfd(evaluateExpression(XdrvMailbox.data + 1, XdrvMailbox.data_len - 1), Settings.flag2.calc_resolution, SettingsText(SET_MEM1 + XdrvMailbox.index -1)); - } else { - SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); - } -#else - SettingsUpdateText(SET_MEM1 + XdrvMailbox.index -1, ('"' == XdrvMailbox.data[0]) ? "" : XdrvMailbox.data); -#endif - bitSet(Rules.mems_event, XdrvMailbox.index -1); - } - ResponseCmndIdxChar(SettingsText(SET_MEM1 + XdrvMailbox.index -1)); - } - } -} - -void CmndCalcResolution(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 7)) { - Settings.flag2.calc_resolution = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.flag2.calc_resolution); -} - -void CmndAddition(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { - if (XdrvMailbox.data_len > 0) { - float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) + CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); - bitSet(Rules.vars_event, XdrvMailbox.index -1); - } - ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); - } -} - -void CmndSubtract(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { - if (XdrvMailbox.data_len > 0) { - float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) - CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); - bitSet(Rules.vars_event, XdrvMailbox.index -1); - } - ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); - } -} - -void CmndMultiply(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { - if (XdrvMailbox.data_len > 0) { - float tempvar = CharToFloat(rules_vars[XdrvMailbox.index -1]) * CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); - bitSet(Rules.vars_event, XdrvMailbox.index -1); - } - ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); - } -} - -void CmndScale(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_RULE_VARS)) { - if (XdrvMailbox.data_len > 0) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { - char sub_string[XdrvMailbox.data_len +1]; - - float valueIN = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 1)); - float fromLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)); - float fromHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 3)); - float toLow = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)); - float toHigh = CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 5)); - float value = map_double(valueIN, fromLow, fromHigh, toLow, toHigh); - dtostrfd(value, Settings.flag2.calc_resolution, rules_vars[XdrvMailbox.index -1]); - bitSet(Rules.vars_event, XdrvMailbox.index -1); - } - } - ResponseCmndIdxChar(rules_vars[XdrvMailbox.index -1]); - } -} - -float map_double(float x, float in_min, float in_max, float out_min, float out_max) -{ - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} - - - - - -bool Xdrv10(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_50_MSECOND: - RulesEvery50ms(); - break; - case FUNC_EVERY_100_MSECOND: - RulesEvery100ms(); - break; - case FUNC_EVERY_SECOND: - RulesEverySecond(); - break; - case FUNC_SET_POWER: - RulesSetPower(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kRulesCommands, RulesCommand); - break; - case FUNC_RULES_PROCESS: - result = RulesProcess(); - break; - case FUNC_SAVE_BEFORE_RESTART: - RulesSaveBeforeRestart(); - break; -#ifdef SUPPORT_MQTT_EVENT - case FUNC_MQTT_DATA: - result = RulesMqttData(); - break; -#endif - case FUNC_PRE_INIT: - RulesInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" -#ifdef USE_SCRIPT -#ifndef USE_RULES -# 40 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" -#define XDRV_10 10 -#define XI2C_37 37 - -#define SCRIPT_DEBUG 0 - - -#ifndef MAXVARS -#define MAXVARS 50 -#endif -#ifndef MAXSVARS -#define MAXSVARS 5 -#endif -#define MAXNVARS MAXVARS-MAXSVARS - -#define MAXFILT 5 -#define SCRIPT_SVARSIZE 20 -#define SCRIPT_MAXSSIZE 48 -#define SCRIPT_EOL '\n' -#define SCRIPT_FLOAT_PRECISION 2 -#define PMEM_SIZE sizeof(Settings.script_pram) -#define SCRIPT_MAXPERM (PMEM_SIZE)-4/sizeof(float) -#define MAX_SCRIPT_SIZE MAX_RULE_SIZE*MAX_RULE_SETS - - -#define EPOCH_OFFSET 1546300800 - -enum {OPER_EQU=1,OPER_PLS,OPER_MIN,OPER_MUL,OPER_DIV,OPER_PLSEQU,OPER_MINEQU,OPER_MULEQU,OPER_DIVEQU,OPER_EQUEQU,OPER_NOTEQU,OPER_GRTEQU,OPER_LOWEQU,OPER_GRT,OPER_LOW,OPER_PERC,OPER_XOR,OPER_AND,OPER_OR,OPER_ANDEQU,OPER_OREQU,OPER_XOREQU,OPER_PERCEQU}; -enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD}; - -#ifdef USE_SCRIPT_FATFS -#include -#include -#ifndef FAT_SCRIPT_SIZE -#define FAT_SCRIPT_SIZE 4096 -#endif -#define FAT_SCRIPT_NAME "script.txt" -#if USE_LONG_FILE_NAMES==1 -#warning ("FATFS long filenames not supported"); -#endif -#if USE_STANDARD_SPI_LIBRARY==0 -#warning ("FATFS standard spi should be used"); -#endif -#endif - -#ifdef SUPPORT_MQTT_EVENT - #include - typedef struct { - String Event; - String Topic; - String Key; - } MQTT_Subscription; - LinkedList subscriptions; -#endif - -#ifdef USE_DISPLAY -#ifdef USE_TOUCH_BUTTONS -#include -extern VButton *buttons[MAXBUTTONS]; -#endif -#endif - -typedef union { - uint8_t data; - struct { - uint8_t is_string : 1; - uint8_t is_permanent : 1; - uint8_t is_timer : 1; - uint8_t is_autoinc : 1; - uint8_t changed : 1; - uint8_t settable : 1; - uint8_t is_filter : 1; - uint8_t constant : 1; - }; -} SCRIPT_TYPE; - -struct T_INDEX { - uint8_t index; - SCRIPT_TYPE bits; -}; - -struct M_FILT { - uint8_t numvals; - uint8_t index; - float maccu; - float rbuff[1]; -}; - -typedef union { - uint8_t data; - struct { - uint8_t nutu8 : 1; - uint8_t nutu7 : 1; - uint8_t nutu6 : 1; - uint8_t nutu5 : 1; - uint8_t nutu4 : 1; - uint8_t nutu3 : 1; - uint8_t is_dir : 1; - uint8_t is_open : 1; - }; -} FILE_FLAGS; - -#define SFS_MAX 4 - -struct SCRIPT_MEM { - float *fvars; - float *s_fvars; - struct T_INDEX *type; - struct M_FILT *mfilt; - char *glob_vnp; - uint8_t *vnp_offset; - char *glob_snp; - char *scriptptr; - char *section_ptr; - char *scriptptr_bu; - char *script_ram; - uint16_t script_size; - uint8_t *script_pram; - uint16_t script_pram_size; - uint8_t numvars; - void *script_mem; - uint16_t script_mem_size; - uint8_t script_dprec; - uint8_t var_not_found; - uint8_t glob_error; - uint8_t max_ssize; - uint8_t script_loglevel; - uint8_t flags; -#ifdef USE_SCRIPT_FATFS - File files[SFS_MAX]; - FILE_FLAGS file_flags[SFS_MAX]; - uint8_t script_sd_found; - char flink[2][14]; -#endif -} glob_script_mem; - - -int16_t last_findex; -uint8_t tasm_cmd_activ=0; -uint8_t fast_script=0; -uint32_t script_lastmillis; - - -#ifdef USE_BUTTON_EVENT -int8_t script_button[MAX_KEYS]; -#endif - -char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo); -char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo); -char *ForceStringVar(char *lp,char *dstr); -void send_download(void); -uint8_t reject(char *name); - -void ScriptEverySecond(void) { - - if (bitRead(Settings.rule_enabled, 0)) { - struct T_INDEX *vtp=glob_script_mem.type; - float delta=(millis()-script_lastmillis)/1000; - script_lastmillis=millis(); - for (uint8_t count=0; count0) { - - *fp-=delta; - if (*fp<0) *fp=0; - } - } - if (vtp[count].bits.is_autoinc) { - - float *fp=&glob_script_mem.fvars[vtp[count].index]; - if (*fp>=0) { - *fp+=delta; - } - } - } - Run_Scripter(">S",2,0); - } -} - -void RulesTeleperiod(void) { - if (bitRead(Settings.rule_enabled, 0) && mqtt_data[0]) Run_Scripter(">T",2, mqtt_data); -} - - -#ifdef USE_24C256 -#ifndef USE_SCRIPT_FATFS - -#include -#define EEPROM_ADDRESS 0x50 - -#ifndef EEP_SCRIPT_SIZE -#define EEP_SCRIPT_SIZE 4095 -#endif - - -static Eeprom24C128_256 eeprom(EEPROM_ADDRESS); - -#define EEP_WRITE(A,B,C) eeprom.writeBytes(A,B,(uint8_t*)C); - -#define EEP_READ(A,B,C) eeprom.readBytes(A,B,(uint8_t*)C); -#endif -#endif - -#define SCRIPT_SKIP_SPACES while (*lp==' ' || *lp=='\t') lp++; -#define SCRIPT_SKIP_EOL while (*lp==SCRIPT_EOL) lp++; - - -int16_t Init_Scripter(void) { -char *script; - - script=glob_script_mem.script_ram; - - - uint16_t lines=0,nvars=0,svars=0,vars=0; - char *lp=script; - char vnames[MAXVARS*10]; - char *vnames_p=vnames; - char *vnp[MAXVARS]; - char **vnp_p=vnp; - char strings[MAXSVARS*SCRIPT_MAXSSIZE]; - struct M_FILT mfilt[MAXFILT]; - - char *strings_p=strings; - char *snp[MAXSVARS]; - char **snp_p=snp; - uint8_t numperm=0,numflt=0,count; - - glob_script_mem.max_ssize=SCRIPT_SVARSIZE; - glob_script_mem.scriptptr=0; - - if (!*script) return -999; - - float fvalues[MAXVARS]; - struct T_INDEX vtypes[MAXVARS]; - char init=0; - while (1) { - - - SCRIPT_SKIP_SPACES - - if (*lp=='\n' || *lp=='\r') goto next_line; - - if (*lp==';') goto next_line; - if (init) { - - if (*lp=='>') { - init=0; - break; - } - char *op=strchr(lp,'='); - if (op) { - vtypes[vars].bits.data=0; - - if (*lp=='p' && *(lp+1)==':') { - lp+=2; - if (numpermMAXFILT) { - return -6; - } - } else { - vtypes[vars].bits.is_filter=0; - } - *vnp_p++=vnames_p; - while (lpMAXNVARS) { - return -1; - } - if (vtypes[vars].bits.is_filter) { - while (isdigit(*op) || *op=='.' || *op=='-') { - op++; - } - while (*op==' ') op++; - if (isdigit(*op)) { - - uint8_t flen=atoi(op); - mfilt[numflt-1].numvals&=0x80; - mfilt[numflt-1].numvals|=flen&0x7f; - } - } - - } else { - - op++; - *snp_p++=strings_p; - while (*op!='\"') { - if (*op==SCRIPT_EOL) break; - *strings_p++=*op++; - } - *strings_p++=0; - vtypes[vars].bits.is_string=1; - vtypes[vars].index=svars; - svars++; - if (svars>MAXSVARS) { - return -2; - } - } - vars++; - if (vars>MAXVARS) { - return -3; - } - } - } else { - if (!strncmp(lp,">D",2)) { - lp+=2; - SCRIPT_SKIP_SPACES - if (isdigit(*lp)) { - uint8_t ssize=atoi(lp)+1; - if (ssize<10 || ssize>SCRIPT_MAXSSIZE) ssize=SCRIPT_MAXSSIZE; - glob_script_mem.max_ssize=ssize; - } - init=1; - } - } - - next_line: - lp = strchr(lp, SCRIPT_EOL); - if (!lp) break; - lp++; - } - - uint16_t fsize=0; - for (count=0; count255) { - free(glob_script_mem.script_mem); - return -5; - } - } - - AddLog_P2(LOG_LEVEL_INFO, PSTR("Script: nv=%d, tv=%d, vns=%d, ram=%d"), nvars, svars, index, glob_script_mem.script_mem_size); - - - char *cp1=glob_script_mem.glob_snp; - char *sp=strings; - for (count=0; countnumvals=mfilt[count].numvals; - mp+=sizeof(struct M_FILT)+((mfilt[count].numvals&0x7f)-1)*sizeof(float); - } - - glob_script_mem.numvars=vars; - glob_script_mem.script_dprec=SCRIPT_FLOAT_PRECISION; - glob_script_mem.script_loglevel=LOG_LEVEL_INFO; - - -#if SCRIPT_DEBUG>2 - struct T_INDEX *dvtp=glob_script_mem.type; - for (uint8_t count=0; count0 - ClaimSerial(); - SetSerialBaudrate(9600); -#endif - - - glob_script_mem.scriptptr=lp-1; - glob_script_mem.scriptptr_bu=glob_script_mem.scriptptr; - return 0; - -} - -#ifdef USE_LIGHT -#ifdef USE_WS2812 -void ws2812_set_array(float *array ,uint8_t len) { - - Ws2812ForceSuspend(); - for (uint8_t cnt=0;cntSettings.light_pixels) break; - uint32_t col=array[cnt]; - Ws2812SetColor(cnt+1,col>>16,col>>8,col,0); - } - Ws2812ForceUpdate(); -} -#endif -#endif - -#define NUM_RES 0xfe -#define STR_RES 0xfd -#define VAR_NV 0xff - -#define NTYPE 0 -#define STYPE 0x80 - -#ifndef FLT_MAX -#define FLT_MAX 99999999 -#endif - -float median_array(float *array,uint8_t len) { - uint8_t ind[len]; - uint8_t mind=0,index=0,flg; - float min=FLT_MAX; - - for (uint8_t hcnt=0; hcntnumvals&0x7f; - return mflp->rbuff; - } - mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); - } - return 0; -} - - -float Get_MFVal(uint8_t index,uint8_t bind) { - uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; - for (uint8_t count=0; countnumvals&0x7f; - if (!bind) { - return mflp->index; - } - if (bind<1 || bind>maxind) bind=maxind; - return mflp->rbuff[bind-1]; - } - mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); - } - return 0; -} - -void Set_MFVal(uint8_t index,uint8_t bind,float val) { - uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; - for (uint8_t count=0; countnumvals&0x7f; - if (!bind) { - mflp->index=val; - } else { - if (bind<1 || bind>maxind) bind=maxind; - mflp->rbuff[bind-1]=val; - } - return; - } - mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); - } -} - - -float Get_MFilter(uint8_t index) { - uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; - for (uint8_t count=0; countnumvals&0x80) { - - return mflp->maccu/(mflp->numvals&0x7f); - } else { - - return median_array(mflp->rbuff,mflp->numvals); - } - } - mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); - } - return 0; -} - -void Set_MFilter(uint8_t index, float invar) { - uint8_t *mp=(uint8_t*)glob_script_mem.mfilt; - for (uint8_t count=0; countnumvals&0x80) { - - mflp->maccu-=mflp->rbuff[mflp->index]; - mflp->maccu+=invar; - mflp->rbuff[mflp->index]=invar; - mflp->index++; - if (mflp->index>=(mflp->numvals&0x7f)) mflp->index=0; - } else { - - mflp->rbuff[mflp->index]=invar; - mflp->index++; - if (mflp->index>=mflp->numvals) mflp->index=0; - } - break; - } - mp+=sizeof(struct M_FILT)+((mflp->numvals&0x7f)-1)*sizeof(float); - } -} - -#define MEDIAN_SIZE 5 -#define MEDIAN_FILTER_NUM 2 - -struct MEDIAN_FILTER { -float buffer[MEDIAN_SIZE]; -int8_t index; -} script_mf[MEDIAN_FILTER_NUM]; - -float DoMedian5(uint8_t index, float in) { - - if (index>=MEDIAN_FILTER_NUM) index=0; - - struct MEDIAN_FILTER* mf=&script_mf[index]; - mf->buffer[mf->index]=in; - mf->index++; - if (mf->index>=MEDIAN_SIZE) mf->index=0; - return median_array(mf->buffer,MEDIAN_SIZE); -} - -#ifdef USE_LIGHT - -uint32_t HSVToRGB(uint16_t hue, uint8_t saturation, uint8_t value) { -float r = 0, g = 0, b = 0; -struct HSV { - float H; - float S; - float V; -} hsv; - -hsv.H=hue; -hsv.S=(float)saturation/100.0; -hsv.V=(float)value/100.0; - -if (hsv.S == 0) { - r = hsv.V; - g = hsv.V; - b = hsv.V; - } else { - int i; - float f, p, q, t; - - if (hsv.H == 360) - hsv.H = 0; - else - hsv.H = hsv.H / 60; - - i = (int)trunc(hsv.H); - f = hsv.H - i; - - p = hsv.V * (1.0 - hsv.S); - q = hsv.V * (1.0 - (hsv.S * f)); - t = hsv.V * (1.0 - (hsv.S * (1.0 - f))); - - switch (i) - { - case 0: - r = hsv.V; - g = t; - b = p; - break; - - case 1: - r = q; - g = hsv.V; - b = p; - break; - - case 2: - r = p; - g = hsv.V; - b = t; - break; - - case 3: - r = p; - g = q; - b = hsv.V; - break; - - case 4: - r = t; - g = p; - b = hsv.V; - break; - - default: - r = hsv.V; - g = p; - b = q; - break; - } - - } - - uint8_t ir,ig,ib; - ir=r*255; - ig=g*255; - ib=b*255; - - uint32_t rgb=(ir<<16)|(ig<<8)|ib; - return rgb; -} -#endif - - - - -char *isvar(char *lp, uint8_t *vtype,struct T_INDEX *tind,float *fp,char *sp,JsonObject *jo) { - uint16_t count,len=0; - uint8_t nres=0; - char vname[32]; - float fvar=0; - tind->index=0; - tind->bits.data=0; - - if (isdigit(*lp) || (*lp=='-' && isdigit(*(lp+1))) || *lp=='.') { - - if (fp) { - if (*lp=='0' && *(lp+1)=='x') { - lp+=2; - *fp=strtol(lp,0,16); - } else { - *fp=CharToFloat(lp); - } - } - if (*lp=='-') lp++; - while (isdigit(*lp) || *lp=='.') { - if (*lp==0 || *lp==SCRIPT_EOL) break; - lp++; - } - tind->bits.constant=1; - tind->bits.is_string=0; - *vtype=NUM_RES; - return lp; - } - if (*lp=='"') { - lp++; - while (*lp!='"') { - if (*lp==0 || *lp==SCRIPT_EOL) break; - uint8_t iob=*lp; - if (iob=='\\') { - lp++; - if (*lp=='t') { - iob='\t'; - } else if (*lp=='n') { - iob='\n'; - } else if (*lp=='r') { - iob='\r'; - } else if (*lp=='\\') { - iob='\\'; - } else { - lp--; - } - if (sp) *sp++=iob; - } else { - if (sp) *sp++=iob; - } - lp++; - } - if (sp) *sp=0; - *vtype=STR_RES; - tind->bits.constant=1; - tind->bits.is_string=1; - return lp+1; - } - - if (*lp=='-') { - - nres=1; - lp++; - } - - const char *term="\n\r ])=+-/*%>index=VAR_NV; - glob_script_mem.var_not_found=1; - return lp; - } - - struct T_INDEX *vtp=glob_script_mem.type; - char dvnam[32]; - strcpy (dvnam,vname); - uint8_t olen=len; - last_findex=-1; - char *ja=strchr(dvnam,'['); - if (ja) { - *ja=0; - ja++; - olen=strlen(dvnam); - } - for (count=0; countindex=count; - if (vtp[count].bits.is_string==0) { - *vtype=NTYPE|index; - if (vtp[count].bits.is_filter) { - if (ja) { - lp+=olen+1; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - last_findex=fvar; - fvar=Get_MFVal(index,fvar); - len=1; - } else { - fvar=Get_MFilter(index); - } - } else { - fvar=glob_script_mem.fvars[index]; - } - if (nres) fvar=-fvar; - if (fp) *fp=fvar; - } else { - *vtype=STYPE|index; - if (sp) strlcpy(sp,glob_script_mem.glob_snp+(index*glob_script_mem.max_ssize),SCRIPT_MAXSSIZE); - } - return lp+len; - } - } - } - - if (jo) { - - const char* str_value; - uint8_t aindex; - String vn; - char *ja=strchr(vname,'['); - if (ja) { - - *ja=0; - ja++; - - float fvar; - GetNumericResult(ja,OPER_EQU,&fvar,0); - aindex=fvar; - if (aindex<1 || aindex>6) aindex=1; - aindex--; - } - if (jo->success()) { - char *subtype=strchr(vname,'#'); - char *subtype2; - if (subtype) { - *subtype=0; - subtype++; - subtype2=strchr(subtype,'#'); - if (subtype2) { - *subtype2=0; - *subtype2++; - } - } - vn=vname; - str_value = (*jo)[vn]; - if ((*jo)[vn].success()) { - if (subtype) { - JsonObject &jobj1=(*jo)[vn]; - if (jobj1.success()) { - vn=subtype; - jo=&jobj1; - str_value = (*jo)[vn]; - if ((*jo)[vn].success()) { - - if (subtype2) { - JsonObject &jobj2=(*jo)[vn]; - if ((*jo)[vn].success()) { - vn=subtype2; - jo=&jobj2; - str_value = (*jo)[vn]; - if ((*jo)[vn].success()) { - goto skip; - } else { - goto chknext; - } - } else { - goto chknext; - } - } - - goto skip; - } - } else { - goto chknext; - } - } - skip: - if (ja) { - - str_value = (*jo)[vn][aindex]; - } - if (str_value && *str_value) { - if ((*jo).is(vn)) { - if (!strncmp(str_value,"ON",2)) { - if (fp) *fp=1; - goto nexit; - } else if (!strncmp(str_value,"OFF",3)) { - if (fp) *fp=0; - goto nexit; - } else { - *vtype=STR_RES; - tind->bits.constant=1; - tind->bits.is_string=1; - if (sp) strlcpy(sp,str_value,SCRIPT_MAXSSIZE); - return lp+len; - } - - } else { - if (fp) { - if (!strncmp(vn.c_str(),"Epoch",5)) { - *fp=atoi(str_value)-(uint32_t)EPOCH_OFFSET; - } else { - *fp=CharToFloat((char*)str_value); - } - } - nexit: - *vtype=NUM_RES; - tind->bits.constant=1; - tind->bits.is_string=0; - return lp+len; - } - } - } - } - } - -chknext: - switch (vname[0]) { - case 'a': -#ifdef USE_ANGLE_FUNC - if (!strncmp(vname,"acos(",5)) { - lp+=5; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - fvar=acosf(fvar); - lp++; - len=0; - goto exit; - } -#endif - break; - - case 'b': - if (!strncmp(vname,"boot",4)) { - if (rules_flag.system_boot) { - rules_flag.system_boot=0; - fvar=1; - } - goto exit; - } -#ifdef USE_BUTTON_EVENT - if (!strncmp(vname,"bt[",3)) { - - GetNumericResult(vname+3,OPER_EQU,&fvar,0); - uint32_t index=fvar; - if (index<1 || index>MAX_KEYS) index=1; - fvar=script_button[index-1]; - script_button[index-1]|=0x80; - len++; - goto exit; - } -#endif - break; - case 'c': - if (!strncmp(vname,"chg[",4)) { - - struct T_INDEX ind; - uint8_t vtype; - isvar(vname+4,&vtype,&ind,0,0,0); - if (!ind.bits.constant) { - uint8_t index=glob_script_mem.type[ind.index].index; - if (glob_script_mem.fvars[index]!=glob_script_mem.s_fvars[index]) { - - glob_script_mem.s_fvars[index]=glob_script_mem.fvars[index]; - fvar=1; - len++; - goto exit; - } else { - fvar=0; - len++; - goto exit; - } - } - } - break; - case 'd': - if (!strncmp(vname,"day",3)) { - fvar=RtcTime.day_of_month; - goto exit; - } - break; - case 'e': - if (!strncmp(vname,"epoch",5)) { - fvar=UtcTime()-(uint32_t)EPOCH_OFFSET; - goto exit; - } - break; -#ifdef USE_SCRIPT_FATFS - case 'f': - if (!strncmp(vname,"fo(",3)) { - lp+=3; - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t mode=fvar; - fvar=-1; - for (uint8_t cnt=0;cnt=SFS_MAX) ind=SFS_MAX-1; - glob_script_mem.files[ind].close(); - glob_script_mem.file_flags[ind].is_open=0; - fvar=0; - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"ff(",3)) { - lp+=3; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t ind=fvar; - if (ind>=SFS_MAX) ind=SFS_MAX-1; - glob_script_mem.files[ind].flush(); - fvar=0; - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"fw(",3)) { - lp+=3; - char str[SCRIPT_MAXSSIZE]; - lp=ForceStringVar(lp,str); - while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t ind=fvar; - if (ind>=SFS_MAX) ind=SFS_MAX-1; - if (glob_script_mem.file_flags[ind].is_open) { - fvar=glob_script_mem.files[ind].print(str); - } else { - fvar=0; - } - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"fr(",3)) { - lp+=3; - struct T_INDEX ind; - uint8_t vtype; - uint8_t sindex=0; - lp=isvar(lp,&vtype,&ind,0,0,0); - if (vtype!=VAR_NV) { - - if ((vtype&STYPE)==0) { - - fvar=0; - goto exit; - } else { - - sindex=glob_script_mem.type[ind.index].index; - } - } else { - - fvar=0; - goto exit; - } - while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - uint8_t find=fvar; - if (find>=SFS_MAX) find=SFS_MAX-1; - uint8_t index=0; - char str[glob_script_mem.max_ssize+1]; - char *cp=str; - if (glob_script_mem.file_flags[find].is_open) { - if (glob_script_mem.file_flags[find].is_dir) { - while (true) { - File entry=glob_script_mem.files[find].openNextFile(); - if (entry) { - if (!reject((char*)entry.name())) { - strcpy(str,entry.name()); - entry.close(); - break; - } - } else { - *cp=0; - break; - } - entry.close(); - } - index=strlen(str); - } else { - while (glob_script_mem.files[find].available()) { - uint8_t buf[1]; - glob_script_mem.files[find].read(buf,1); - if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { - break; - } else { - *cp++=buf[0]; - index++; - if (index>=glob_script_mem.max_ssize-1) break; - } - } - *cp=0; - } - } else { - strcpy(str,"file error"); - } - lp++; - strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); - fvar=index; - len=0; - goto exit; - } - if (!strncmp(vname,"fd(",3)) { - lp+=3; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - SD.remove(str); - lp++; - len=0; - goto exit; - } -#ifdef USE_SCRIPT_FATFS_EXT - if (!strncmp(vname,"fe(",3)) { - lp+=3; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - - File ef=SD.open(str); - if (ef) { - uint16_t fsiz=ef.size(); - if (fsiz<2048) { - char *script=(char*)calloc(fsiz+16,1); - if (script) { - ef.read((uint8_t*)script,fsiz); - execute_script(script); - free(script); - fvar=1; - } - } - ef.close(); - } - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"fmd(",4)) { - lp+=4; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=SD.mkdir(str); - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"frd(",4)) { - lp+=4; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=SD.rmdir(str); - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"fx(",3)) { - lp+=3; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - if (SD.exists(str)) fvar=1; - else fvar=0; - lp++; - len=0; - goto exit; - } -#endif - if (!strncmp(vname,"fl1(",4) || !strncmp(vname,"fl2(",4) ) { - uint8_t lknum=*(lp+2)&3; - lp+=4; - char str[glob_script_mem.max_ssize+1]; - lp=GetStringResult(lp,OPER_EQU,str,0); - if (lknum<1 || lknum>2) lknum=1; - strlcpy(glob_script_mem.flink[lknum-1],str,14); - lp++; - fvar=0; - len=0; - goto exit; - } - if (!strncmp(vname,"fsm",3)) { - fvar=glob_script_mem.script_sd_found; - - goto exit; - } - break; - -#endif - case 'g': - if (!strncmp(vname,"gtmp",4)) { - fvar=global_temperature; - goto exit; - } - if (!strncmp(vname,"ghum",4)) { - fvar=global_humidity; - goto exit; - } - if (!strncmp(vname,"gprs",4)) { - fvar=global_pressure; - goto exit; - } - if (!strncmp(vname,"gtopic",6)) { - if (sp) strlcpy(sp,SettingsText(SET_MQTT_GRP_TOPIC),glob_script_mem.max_ssize); - goto strexit; - } - break; - case 'h': - if (!strncmp(vname,"hours",5)) { - fvar=RtcTime.hour; - goto exit; - } - if (!strncmp(vname,"heap",4)) { - fvar=ESP.getFreeHeap(); - goto exit; - } - if (!strncmp(vname,"hn(",3)) { - lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0); - if (fvar<0 || fvar>255) fvar=0; - lp++; - len=0; - if (sp) { - sprintf(sp,"%02x",(uint8_t)fvar); - } - goto strexit; - } - if (!strncmp(vname,"hx(",3)) { - lp=GetNumericResult(lp+3,OPER_EQU,&fvar,0); - lp++; - len=0; - if (sp) { - sprintf(sp,"%08x",(uint32_t)fvar); - } - goto strexit; - } -#ifdef USE_LIGHT - - if (!strncmp(vname,"hsvrgb(",7)) { - lp=GetNumericResult(lp+7,OPER_EQU,&fvar,0); - if (fvar<0 || fvar>360) fvar=0; - SCRIPT_SKIP_SPACES - - float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - if (fvar2<0 || fvar2>100) fvar2=0; - SCRIPT_SKIP_SPACES - - float fvar3; - lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); - if (fvar3<0 || fvar3>100) fvar3=0; - - fvar=HSVToRGB(fvar,fvar2,fvar3); - - lp++; - len=0; - goto exit; - } - -#endif - break; - case 'i': - if (!strncmp(vname,"int(",4)) { - lp=GetNumericResult(lp+4,OPER_EQU,&fvar,0); - fvar=floor(fvar); - lp++; - len=0; - goto exit; - } - break; - case 'l': - if (!strncmp(vname,"loglvl",6)) { - fvar=glob_script_mem.script_loglevel; - tind->index=SCRIPT_LOGLEVEL; - exit_settable: - if (fp) *fp=fvar; - *vtype=NTYPE; - tind->bits.settable=1; - tind->bits.is_string=0; - return lp+len; - } - break; - case 'm': - if (!strncmp(vname,"med(",4)) { - float fvar1; - lp=GetNumericResult(lp+4,OPER_EQU,&fvar1,0); - SCRIPT_SKIP_SPACES - - float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - fvar=DoMedian5(fvar1,fvar2); - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"micros",6)) { - fvar=micros(); - goto exit; - } - if (!strncmp(vname,"millis",6)) { - fvar=millis(); - goto exit; - } - if (!strncmp(vname,"mins",4)) { - fvar=RtcTime.minute; - goto exit; - } - if (!strncmp(vname,"month",5)) { - fvar=RtcTime.month; - goto exit; - } - if (!strncmp(vname,"mqttc",5)) { - if (rules_flag.mqtt_connected) { - rules_flag.mqtt_connected=0; - fvar=1; - } - goto exit; - } - if (!strncmp(vname,"mqttd",5)) { - if (rules_flag.mqtt_disconnected) { - rules_flag.mqtt_disconnected=0; - fvar=1; - } - goto exit; - } - if (!strncmp(vname,"mqtts",5)) { - fvar=!global_state.mqtt_down; - goto exit; - } - break; - case 'p': - if (!strncmp(vname,"pin[",4)) { - - GetNumericResult(vname+4,OPER_EQU,&fvar,0); - fvar=digitalRead((uint8_t)fvar); - - len++; - goto exit; - } - if (!strncmp(vname,"pn[",3)) { - GetNumericResult(vname+3,OPER_EQU,&fvar,0); - fvar=pin[(uint8_t)fvar]; - - len++; - goto exit; - } - if (!strncmp(vname,"pd[",3)) { - GetNumericResult(vname+3,OPER_EQU,&fvar,0); - uint8_t gpiopin=fvar; - for (uint8_t i=0;iMAX_COUNTERS) index=1; - fvar=RtcSettings.pulse_counter[index-1]; - len+=1; - goto exit; - } - break; - - case 'r': - if (!strncmp(vname,"ram",3)) { - fvar=glob_script_mem.script_mem_size+(glob_script_mem.script_size)+(PMEM_SIZE); - goto exit; - } - break; - case 's': - if (!strncmp(vname,"secs",4)) { - fvar=RtcTime.second; - goto exit; - } - if (!strncmp(vname,"sw[",3)) { - - GetNumericResult(vname+3,OPER_EQU,&fvar,0); - fvar=SwitchLastState((uint32_t)fvar); - - len++; - goto exit; - } - if (!strncmp(vname,"stack",5)) { - fvar=GetStack(); - goto exit; - } - if (!strncmp(vname,"slen",4)) { - fvar=strlen(glob_script_mem.script_ram); - goto exit; - } - if (!strncmp(vname,"sl(",3)) { - lp+=3; - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - lp++; - len=0; - fvar=strlen(str); - goto exit; - } - if (!strncmp(vname,"sb(",3)) { - lp+=3; - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - SCRIPT_SKIP_SPACES - float fvar1; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); - SCRIPT_SKIP_SPACES - float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - lp++; - len=0; - if (fvar1<0) { - fvar1=strlen(str)+fvar1; - } - memcpy(sp,&str[(uint8_t)fvar1],(uint8_t)fvar2); - sp[(uint8_t)fvar2] = '\0'; - goto strexit; - } - if (!strncmp(vname,"st(",3)) { - lp+=3; - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - while (*lp==' ') lp++; - char token[2]; - token[0]=*lp++; - token[1]=0; - while (*lp==' ') lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - - lp++; - len=0; - if (sp) { - - char *st=strtok(str,token); - if (!st) { - *sp=0; - } else { - for (uint8_t cnt=1; cnt<=fvar; cnt++) { - if (cnt==fvar) { - strcpy(sp,st); - break; - } - st=strtok(NULL,token); - if (!st) { - *sp=0; - break; - } - } - } - } - goto strexit; - } - if (!strncmp(vname,"s(",2)) { - lp+=2; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - char str[glob_script_mem.max_ssize+1]; - dtostrfd(fvar,glob_script_mem.script_dprec,str); - if (sp) strlcpy(sp,str,glob_script_mem.max_ssize); - lp++; - len=0; - goto strexit; - } -#if defined(USE_TIMERS) && defined(USE_SUNRISE) - if (!strncmp(vname,"sunrise",7)) { - fvar=SunMinutes(0); - goto exit; - } - if (!strncmp(vname,"sunset",6)) { - fvar=SunMinutes(1); - goto exit; - } -#endif - -#ifdef USE_SHUTTER - if (!strncmp(vname,"sht[",4)) { - GetNumericResult(vname+4,OPER_EQU,&fvar,0); - uint8_t index=fvar; - if (index<=shutters_present) { - fvar=Settings.shutter_position[index-1]; - } else { - fvar=-1; - } - len+=1; - goto exit; - } -#endif -#ifdef USE_ANGLE_FUNC - if (!strncmp(vname,"sin(",4)) { - lp+=4; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - fvar=sinf(fvar); - lp++; - len=0; - goto exit; - } - if (!strncmp(vname,"sqrt(",5)) { - lp+=5; - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - fvar=sqrtf(fvar); - lp++; - len=0; - goto exit; - } -#endif -#ifdef USE_SML_SCRIPT_CMD - if (!strncmp(vname,"sml(",4)) { - lp+=4; - float fvar1; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,0); - SCRIPT_SKIP_SPACES - float fvar2; - lp=GetNumericResult(lp,OPER_EQU,&fvar2,0); - SCRIPT_SKIP_SPACES - if (fvar2==0) { - float fvar3; - lp=GetNumericResult(lp,OPER_EQU,&fvar3,0); - fvar=SML_SetBaud(fvar1,fvar3); - } else { - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - fvar=SML_Write(fvar1,str); - } - lp++; - fvar=0; - len=0; - goto exit; - } -#endif - break; - case 't': - if (!strncmp(vname,"time",4)) { - fvar=MinutesPastMidnight(); - goto exit; - } - if (!strncmp(vname,"tper",4)) { - fvar=Settings.tele_period; - tind->index=SCRIPT_TELEPERIOD; - goto exit_settable; - } - if (!strncmp(vname,"tinit",5)) { - if (rules_flag.time_init) { - rules_flag.time_init=0; - fvar=1; - } - goto exit; - } - if (!strncmp(vname,"tset",4)) { - if (rules_flag.time_set) { - rules_flag.time_set=0; - fvar=1; - } - goto exit; - } - if (!strncmp(vname,"tstamp",6)) { - if (sp) strlcpy(sp,GetDateAndTime(DT_LOCAL).c_str(),glob_script_mem.max_ssize); - goto strexit; - } - if (!strncmp(vname,"topic",5)) { - if (sp) strlcpy(sp,SettingsText(SET_MQTT_TOPIC),glob_script_mem.max_ssize); - goto strexit; - } -#ifdef USE_DISPLAY -#ifdef USE_TOUCH_BUTTONS - if (!strncmp(vname,"tbut[",5)) { - GetNumericResult(vname+5,OPER_EQU,&fvar,0); - uint8_t index=fvar; - if (index<1 || index>MAXBUTTONS) index=1; - index--; - if (buttons[index]) { - fvar=buttons[index]->vpower&0x80; - } else { - fvar=-1; - } - len+=1; - goto exit; - } - -#endif -#endif - break; - case 'u': - if (!strncmp(vname,"uptime",6)) { - fvar=MinutesUptime(); - goto exit; - } - if (!strncmp(vname,"upsecs",6)) { - fvar=uptime; - goto exit; - } - if (!strncmp(vname,"upd[",4)) { - - struct T_INDEX ind; - uint8_t vtype; - isvar(vname+4,&vtype,&ind,0,0,0); - if (!ind.bits.constant) { - if (!ind.bits.changed) { - fvar=0; - len++; - goto exit; - } else { - glob_script_mem.type[ind.index].bits.changed=0; - fvar=1; - len++; - goto exit; - } - } - goto notfound; - } - break; - - case 'w': - if (!strncmp(vname,"wday",4)) { - fvar=RtcTime.day_of_week; - goto exit; - } - if (!strncmp(vname,"wific",5)) { - if (rules_flag.wifi_connected) { - rules_flag.wifi_connected=0; - fvar=1; - } - goto exit; - } - if (!strncmp(vname,"wifid",5)) { - if (rules_flag.wifi_disconnected) { - rules_flag.wifi_disconnected=0; - fvar=1; - } - goto exit; - } - if (!strncmp(vname,"wifis",5)) { - fvar=!global_state.wifi_down; - goto exit; - } - break; - case 'y': - if (!strncmp(vname,"year",4)) { - fvar=RtcTime.year; - goto exit; - } - break; - default: - break; - } - -notfound: - if (fp) *fp=0; - *vtype=VAR_NV; - tind->index=VAR_NV; - glob_script_mem.var_not_found=1; - return lp; - -exit: - if (fp) *fp=fvar; - *vtype=NUM_RES; - tind->bits.constant=1; - tind->bits.is_string=0; - return lp+len; - -strexit: - *vtype=STYPE; - tind->bits.constant=1; - tind->bits.is_string=1; - return lp+len; -} - - - -char *getop(char *lp, uint8_t *operand) { - switch (*lp) { - case '=': - if (*(lp+1)=='=') { - *operand=OPER_EQUEQU; - return lp+2; - } else { - *operand=OPER_EQU; - return lp+1; - } - break; - case '+': - if (*(lp+1)=='=') { - *operand=OPER_PLSEQU; - return lp+2; - } else { - *operand=OPER_PLS; - return lp+1; - } - break; - case '-': - if (*(lp+1)=='=') { - *operand=OPER_MINEQU; - return lp+2; - } else { - *operand=OPER_MIN; - return lp+1; - } - break; - case '*': - if (*(lp+1)=='=') { - *operand=OPER_MULEQU; - return lp+2; - } else { - *operand=OPER_MUL; - return lp+1; - } - break; - case '/': - if (*(lp+1)=='=') { - *operand=OPER_DIVEQU; - return lp+2; - } else { - *operand=OPER_DIV; - return lp+1; - } - break; - case '!': - if (*(lp+1)=='=') { - *operand=OPER_NOTEQU; - return lp+2; - } - break; - case '>': - if (*(lp+1)=='=') { - *operand=OPER_GRTEQU; - return lp+2; - } else { - *operand=OPER_GRT; - return lp+1; - - } - break; - case '<': - if (*(lp+1)=='=') { - *operand=OPER_LOWEQU; - return lp+2; - } else { - *operand=OPER_LOW; - return lp+1; - } - break; - case '%': - if (*(lp+1)=='=') { - *operand=OPER_PERCEQU; - return lp+2; - } else { - *operand=OPER_PERC; - return lp+1; - } - break; - case '^': - if (*(lp+1)=='=') { - *operand=OPER_XOREQU; - return lp+2; - } else { - *operand=OPER_XOR; - return lp+1; - } - break; - case '&': - if (*(lp+1)=='=') { - *operand=OPER_ANDEQU; - return lp+2; - } else { - *operand=OPER_AND; - return lp+1; - } - break; - case '|': - if (*(lp+1)=='=') { - *operand=OPER_OREQU; - return lp+2; - } else { - *operand=OPER_OR; - return lp+1; - } - break; - } - *operand=0; - return lp; -} - - -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) - - -extern "C" { -#include - extern cont_t g_cont; -} -uint16_t GetStack(void) { - register uint32_t *sp asm("a1"); - return (4 * (sp - g_cont.stack)); -} - -#else -extern "C" { -#include - extern cont_t* g_pcont; -} -uint16_t GetStack(void) { - register uint32_t *sp asm("a1"); - return (4 * (sp - g_pcont->stack)); -} -#endif - -char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo) { - uint8_t operand=0; - uint8_t vtype; - char *slp; - struct T_INDEX ind; - char str[SCRIPT_MAXSSIZE],str1[SCRIPT_MAXSSIZE]; - while (1) { - lp=isvar(lp,&vtype,&ind,0,str1,jo); - if (vtype!=STR_RES && !(vtype&STYPE)) { - - glob_script_mem.glob_error=1; - return lp; - } - switch (lastop) { - case OPER_EQU: - strlcpy(str,str1,sizeof(str)); - break; - case OPER_PLS: - strncat(str,str1,sizeof(str)); - break; - } - slp=lp; - lp=getop(lp,&operand); - switch (operand) { - case OPER_EQUEQU: - case OPER_NOTEQU: - case OPER_LOW: - case OPER_LOWEQU: - case OPER_GRT: - case OPER_GRTEQU: - lp=slp; - strcpy(cp,str); - return lp; - break; - default: - break; - } - lastop=operand; - if (!operand) { - strcpy(cp,str); - return lp; - } - } -} - -char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo) { -uint8_t operand=0; -float fvar1,fvar; -char *slp; -uint8_t vtype; -struct T_INDEX ind; - while (1) { - - if (*lp=='(') { - lp++; - lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo); - lp++; - - } else { - lp=isvar(lp,&vtype,&ind,&fvar1,0,jo); - if (vtype!=NUM_RES && vtype&STYPE) { - - glob_script_mem.glob_error=1; - } - } - switch (lastop) { - case OPER_EQU: - fvar=fvar1; - break; - case OPER_PLS: - fvar+=fvar1; - break; - case OPER_MIN: - fvar-=fvar1; - break; - case OPER_MUL: - fvar*=fvar1; - break; - case OPER_DIV: - fvar/=fvar1; - break; - case OPER_PERC: - fvar=fmodf(fvar,fvar1); - break; - case OPER_XOR: - fvar=(uint32_t)fvar^(uint32_t)fvar1; - break; - case OPER_AND: - fvar=(uint32_t)fvar&(uint32_t)fvar1; - break; - case OPER_OR: - fvar=(uint32_t)fvar|(uint32_t)fvar1; - break; - default: - break; - - } - slp=lp; - lp=getop(lp,&operand); - switch (operand) { - case OPER_EQUEQU: - case OPER_NOTEQU: - case OPER_LOW: - case OPER_LOWEQU: - case OPER_GRT: - case OPER_GRTEQU: - lp=slp; - *fp=fvar; - return lp; - break; - default: - break; - } - lastop=operand; - if (!operand) { - *fp=fvar; - return lp; - } - } -} - - -char *ForceStringVar(char *lp,char *dstr) { - float fvar; - char *slp=lp; - glob_script_mem.glob_error=0; - lp=GetStringResult(lp,OPER_EQU,dstr,0); - if (glob_script_mem.glob_error) { - - lp=GetNumericResult(slp,OPER_EQU,&fvar,0); - dtostrfd(fvar,6,dstr); - glob_script_mem.glob_error=0; - } - return lp; -} - - -void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize) { - char *cp; - uint16_t count; - uint8_t vtype; - uint8_t dprec=glob_script_mem.script_dprec; - float fvar; - cp=srcbuf; - struct T_INDEX ind; - char string[SCRIPT_MAXSSIZE]; - dstsize-=2; - for (count=0;count=sizeof(str)) len=len>=sizeof(str); - strlcpy(str,cp,len); - toSLog(str); -} - -void toLogEOL(const char *s1,const char *str) { - if (!str) return; - uint8_t index=0; - char *cp=log_data; - strcpy(cp,s1); - cp+=strlen(s1); - while (*str) { - if (*str==SCRIPT_EOL) break; - *cp++=*str++; - } - *cp=0; - AddLog(LOG_LEVEL_INFO); -} - - -void toSLog(const char *str) { - if (!str) return; -#if SCRIPT_DEBUG>0 - while (*str) { - Serial.write(*str); - str++; - } -#endif -} - -char *Evaluate_expression(char *lp,uint8_t and_or, uint8_t *result,JsonObject *jo) { - float fvar,*dfvar,fvar1; - uint8_t numeric; - struct T_INDEX ind; - uint8_t vtype=0,lastop; - uint8_t res=0; - char *llp=lp; - char *slp; - - SCRIPT_SKIP_SPACES - if (*lp=='(') { - uint8_t res=0; - uint8_t xand_or=0; - lp++; - -loop: - SCRIPT_SKIP_SPACES - lp=Evaluate_expression(lp,xand_or,&res,jo); - if (*lp==')') { - lp++; - goto exit0; - } - - SCRIPT_SKIP_SPACES - if (!strncmp(lp,"or",2)) { - lp+=2; - xand_or=1; - goto loop; - } else if (!strncmp(lp,"and",3)) { - lp+=3; - xand_or=2; - goto loop; - } -exit0: - if (!and_or) { - *result=res; - } else if (and_or==1) { - *result|=res; - } else { - *result&=res; - } - goto exit10; - } - - llp=lp; - - dfvar=&fvar; - glob_script_mem.glob_error=0; - slp=lp; - numeric=1; - lp=GetNumericResult(lp,OPER_EQU,dfvar,0); - if (glob_script_mem.glob_error==1) { - - char cmpstr[SCRIPT_MAXSSIZE]; - lp=slp; - numeric=0; - - lp=isvar(lp,&vtype,&ind,0,cmpstr,0); - lp=getop(lp,&lastop); - - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,jo); - if (lastop==OPER_EQUEQU || lastop==OPER_NOTEQU) { - res=strcmp(cmpstr,str); - if (lastop==OPER_EQUEQU) res=!res; - goto exit; - } - - } else { - - - lp=getop(lp,&lastop); - lp=GetNumericResult(lp,OPER_EQU,&fvar1,jo); - switch (lastop) { - case OPER_EQUEQU: - res=(*dfvar==fvar1); - break; - case OPER_NOTEQU: - res=(*dfvar!=fvar1); - break; - case OPER_LOW: - res=(*dfvarfvar1); - break; - case OPER_GRTEQU: - res=(*dfvar>=fvar1); - break; - default: - - break; - } - -exit: - if (!and_or) { - *result=res; - } else if (and_or==1) { - *result|=res; - } else { - *result&=res; - } - } - - -exit10: -#if SCRIPT_DEBUG>0 - char tbuff[128]; - sprintf(tbuff,"p1=%d,p2=%d,cmpres=%d,and_or=%d line: ",(int32_t)*dfvar,(int32_t)fvar1,*result,and_or); - toLogEOL(tbuff,llp); -#endif - return lp; -} - - - -#define IF_NEST 8 - -int16_t Run_Scripter(const char *type, int8_t tlen, char *js) { - - if (tasm_cmd_activ && tlen>0) return 0; - - uint8_t vtype=0,sindex,xflg,floop=0,globvindex,fromscriptcmd=0; - int8_t globaindex; - struct T_INDEX ind; - uint8_t operand,lastop,numeric=1,if_state[IF_NEST],if_exe[IF_NEST],if_result[IF_NEST],and_or,ifstck=0; - if_state[ifstck]=0; - if_result[ifstck]=0; - if_exe[ifstck]=1; - char cmpstr[SCRIPT_MAXSSIZE]; - uint8_t check=0; - if (tlen<0) { - tlen=abs(tlen); - check=1; - } - - float *dfvar,*cv_count,cv_max,cv_inc; - char *cv_ptr; - float fvar=0,fvar1,sysvar,swvar; - uint8_t section=0,sysv_type=0,swflg=0; - - if (!glob_script_mem.scriptptr) { - return -99; - } - - DynamicJsonBuffer jsonBuffer; - JsonObject &jobj=jsonBuffer.parseObject(js); - JsonObject *jo; - if (js) jo=&jobj; - else jo=0; - - char *lp=glob_script_mem.scriptptr; - - while (1) { - - - startline: - SCRIPT_SKIP_SPACES - - SCRIPT_SKIP_EOL - - if (*lp==';') goto next_line; - if (!*lp) break; - - if (section) { - - if (*lp=='>') { - return 0; - } - if (*lp=='#') { - return 0; - } - glob_script_mem.var_not_found=0; - - -#ifdef IFTHEN_DEBUG - char tbuff[128]; - sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]); - toLogEOL(tbuff,lp); -#endif - - - - - if (!strncmp(lp,"if",2)) { - lp+=2; - if (ifstck=2) { - lp+=5; - if (ifstck>0) { - if_state[ifstck]=0; - ifstck--; - } - goto next_line; - } else if (!strncmp(lp,"or",2) && if_state[ifstck]==1) { - lp+=2; - and_or=1; - } else if (!strncmp(lp,"and",3) && if_state[ifstck]==1) { - lp+=3; - and_or=2; - } - - if (*lp=='{' && if_state[ifstck]==1) { - lp+=1; - if_state[ifstck]=2; - if (if_exe[ifstck-1]) if_exe[ifstck]=if_result[ifstck]; - } else if (*lp=='{' && if_state[ifstck]==3) { - lp+=1; - - } else if (*lp=='}' && if_state[ifstck]>=2) { - lp++; - char *slp=lp; - uint8_t iselse=0; - for (uint8_t count=0; count<8;count++) { - if (*lp=='}') { - - break; - } - if (!strncmp(lp,"else",4)) { - - if_state[ifstck]=3; - if (if_exe[ifstck-1]) if_exe[ifstck]=!if_result[ifstck]; - lp+=4; - iselse=1; - SCRIPT_SKIP_SPACES - if (*lp=='{') lp++; - break; - } - lp++; - } - if (!iselse) { - lp=slp; - - if (ifstck>0) { - if_state[ifstck]=0; - ifstck--; - } - goto next_line; - } - } - - if (!strncmp(lp,"for",3)) { - - - lp+=3; - SCRIPT_SKIP_SPACES - lp=isvar(lp,&vtype,&ind,0,0,0); - if ((vtype!=VAR_NV) && (vtype&STYPE)==0) { - - uint8_t index=glob_script_mem.type[ind.index].index; - cv_count=&glob_script_mem.fvars[index]; - SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,cv_count,0); - SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&cv_max,0); - SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&cv_inc,0); - - cv_ptr=lp; - floop=1; - } else { - - toLogEOL("for error",lp); - } - } else if (!strncmp(lp,"next",4) && floop>0) { - - *cv_count+=cv_inc; - if (*cv_count<=cv_max) { - lp=cv_ptr; - } else { - lp+=4; - floop=0; - } - } - - if (!strncmp(lp,"switch",6)) { - lp+=6; - SCRIPT_SKIP_SPACES - char *slp=lp; - lp=GetNumericResult(lp,OPER_EQU,&swvar,0); - if (glob_script_mem.glob_error==1) { - - lp=slp; - - lp=isvar(lp,&vtype,&ind,0,cmpstr,0); - swflg=0x81; - } else { - swflg=1; - } - } else if (!strncmp(lp,"case",4) && swflg>0) { - lp+=4; - SCRIPT_SKIP_SPACES - float cvar; - if (!(swflg&0x80)) { - lp=GetNumericResult(lp,OPER_EQU,&cvar,0); - if (swvar!=cvar) { - swflg=2; - } else { - swflg=1; - } - } else { - char str[SCRIPT_MAXSSIZE]; - lp=GetStringResult(lp,OPER_EQU,str,0); - if (!strcmp(cmpstr,str)) { - swflg=0x81; - } else { - swflg=0x82; - } - } - } else if (!strncmp(lp,"ends",4) && swflg>0) { - lp+=4; - swflg=0; - } - if ((swflg&3)==2) goto next_line; - - SCRIPT_SKIP_SPACES - - if (*lp==SCRIPT_EOL) { - goto next_line; - } - - - if (!if_exe[ifstck] && if_state[ifstck]!=1) goto next_line; - -#ifdef IFTHEN_DEBUG - sprintf(tbuff,"stack=%d,exe=%d,state=%d,cmpres=%d execute line: ",ifstck,if_exe[ifstck],if_state[ifstck],if_result[ifstck]); - toLogEOL(tbuff,lp); -#endif - - if (!strncmp(lp,"break",5)) { - if (floop) { - - floop=0; - } else { - section=0; - } - break; - } else if (!strncmp(lp,"dp",2) && isdigit(*(lp+2))) { - lp+=2; - - glob_script_mem.script_dprec=atoi(lp); - goto next_line; - } else if (!strncmp(lp,"delay(",6)) { - lp+=5; - - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - delay(fvar); - goto next_line; - } else if (!strncmp(lp,"spinm(",6)) { - lp+=6; - - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t pinnr=fvar; - SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t mode=fvar; - pinMode(pinnr,mode&3); - goto next_line; - } else if (!strncmp(lp,"spin(",5)) { - lp+=5; - - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t pinnr=fvar; - SCRIPT_SKIP_SPACES - lp=GetNumericResult(lp,OPER_EQU,&fvar,0); - int8_t mode=fvar; - digitalWrite(pinnr,mode&1); - goto next_line; - } else if (!strncmp(lp,"svars(",5)) { - lp+=5; - - Scripter_save_pvars(); - goto next_line; - } -#ifdef USE_LIGHT -#ifdef USE_WS2812 - else if (!strncmp(lp,"ws2812(",7)) { - lp+=7; - lp=isvar(lp,&vtype,&ind,0,0,0); - if (vtype!=VAR_NV) { - - uint8_t index=glob_script_mem.type[ind.index].index; - if ((vtype&STYPE)==0) { - - if (glob_script_mem.type[index].bits.is_filter) { - uint8_t len=0; - float *fa=Get_MFAddr(index,&len); - - if (fa && len) ws2812_set_array(fa,len); - } - } - } - goto next_line; - } -#endif -#endif - - else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) { - - uint8_t sflag=0,pflg=0,svmqtt,swll; - if (*lp=='p') { - pflg=1; - lp+=5; - } - else { - if (*lp=='-') sflag=1; - if (*lp=='+') sflag=2; - lp+=2; - } - char *slp=lp; - SCRIPT_SKIP_SPACES - #define SCRIPT_CMDMEM 512 - char *cmdmem=(char*)malloc(SCRIPT_CMDMEM); - if (cmdmem) { - char *cmd=cmdmem; - uint16_t count; - for (count=0; count=0) { - Set_MFVal(glob_script_mem.type[globvindex].index,globaindex,*dfvar); - } else { - Set_MFilter(glob_script_mem.type[globvindex].index,*dfvar); - } - } - - if (sysv_type) { - switch (sysv_type) { - case SCRIPT_LOGLEVEL: - glob_script_mem.script_loglevel=*dfvar; - break; - case SCRIPT_TELEPERIOD: - if (*dfvar<10) *dfvar=10; - if (*dfvar>300) *dfvar=300; - Settings.tele_period=*dfvar; - break; - } - sysv_type=0; - } - } else { - - numeric=0; - sindex=index; - - char str[SCRIPT_MAXSSIZE]; - lp=getop(lp,&lastop); - char *slp=lp; - glob_script_mem.glob_error=0; - lp=GetStringResult(lp,OPER_EQU,str,jo); - if (!js && glob_script_mem.glob_error) { - - lp=GetNumericResult(slp,OPER_EQU,&fvar,0); - dtostrfd(fvar,6,str); - glob_script_mem.glob_error=0; - } - - if (!glob_script_mem.var_not_found) { - - glob_script_mem.type[globvindex].bits.changed=1; - if (lastop==OPER_EQU) { - strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); - } else if (lastop==OPER_PLSEQU) { - strncat(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),str,glob_script_mem.max_ssize); - } - } - } - - } - SCRIPT_SKIP_SPACES - if (*lp=='{' && if_state[ifstck]==3) { - lp+=1; - - } - goto next_line; - } - } else { - - if (*lp=='>' && tlen==1) { - - lp++; - section=1; - fromscriptcmd=1; - goto startline; - } - if (!strncmp(lp,type,tlen)) { - - section=1; - glob_script_mem.section_ptr=lp; - if (check) { - return 99; - } - - char *ctype=(char*)type; - if (*ctype=='#') { - - ctype+=tlen; - if (*ctype=='(' && *(lp+tlen)=='(') { - float fparam; - numeric=1; - glob_script_mem.glob_error=0; - GetNumericResult((char*)ctype,OPER_EQU,&fparam,0); - if (glob_script_mem.glob_error==1) { - - numeric=0; - - GetStringResult((char*)ctype+1,OPER_EQU,cmpstr,0); - } - lp+=tlen; - if (*lp=='(') { - - lp++; - lp=isvar(lp,&vtype,&ind,0,0,0); - if (vtype!=VAR_NV) { - - uint8_t index=glob_script_mem.type[ind.index].index; - if ((vtype&STYPE)==0) { - - dfvar=&glob_script_mem.fvars[index]; - if (numeric) { - *dfvar=fparam; - } else { - - *dfvar=CharToFloat(cmpstr); - } - } else { - - sindex=index; - if (!numeric) { - strlcpy(glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize),cmpstr,glob_script_mem.max_ssize); - } else { - - dtostrfd(fparam,6,glob_script_mem.glob_snp+(sindex*glob_script_mem.max_ssize)); - } - } - } - } - } else { - lp+=tlen; - if (*ctype=='(' || (*lp!=SCRIPT_EOL && *lp!='?')) { - - section=0; - } - } - } - } - } - - next_line: - if (*lp==SCRIPT_EOL) { - lp++; - } else { - lp = strchr(lp, SCRIPT_EOL); - if (!lp) { - if (section) { - return 0; - } else { - return -1; - } - } - lp++; - } - } - return -1; -} - -uint8_t script_xsns_index = 0; - - -void ScripterEvery100ms(void) { - - if (Settings.rule_enabled && (uptime > 4)) { - mqtt_data[0] = '\0'; - uint16_t script_tele_period_save = tele_period; - tele_period = 2; - XsnsNextCall(FUNC_JSON_APPEND, script_xsns_index); - tele_period = script_tele_period_save; - if (strlen(mqtt_data)) { - mqtt_data[0] = '{'; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data); - Run_Scripter(">T",2, mqtt_data); - } - } - if (fast_script==99) Run_Scripter(">F",2,0); -} - - - - -void Scripter_save_pvars(void) { - int16_t mlen=0; - float *fp=(float*)glob_script_mem.script_pram; - mlen+=sizeof(float); - struct T_INDEX *vtp=glob_script_mem.type; - for (uint8_t count=0; countPMEM_SIZE) { - vtp[count].bits.is_permanent=0; - return; - } - *fp++=glob_script_mem.fvars[index]; - } - } - char *cp=(char*)fp; - for (uint8_t count=0; countPMEM_SIZE) { - vtp[count].bits.is_permanent=0; - return; - } - strcpy(cp,sp); - cp+=slen+1; - } - } -} - - -#ifdef USE_WEBSERVER - -#define WEB_HANDLE_SCRIPT "s10" - -const char S_CONFIGURE_SCRIPT[] PROGMEM = D_CONFIGURE_SCRIPT; - -const char HTTP_BTN_MENU_RULES[] PROGMEM = - "

"; - - -const char HTTP_FORM_SCRIPT[] PROGMEM = - "
 " D_SCRIPT " " - "
"; - -const char HTTP_FORM_SCRIPT1[] PROGMEM = - "
" - "" D_SCRIPT_ENABLE "
" - "
" - ""; - -const char HTTP_SCRIPT_FORM_END[] PROGMEM = - "
" - "" - "
"; - -#ifdef USE_SCRIPT_FATFS -const char HTTP_FORM_SCRIPT1c[] PROGMEM = - ""; -#ifdef SDCARD_DIR -const char HTTP_FORM_SCRIPT1d[] PROGMEM = - ""; -#else -const char HTTP_FORM_SCRIPT1d[] PROGMEM = - ""; -#endif - -#ifdef SDCARD_DIR -const char S_SCRIPT_FILE_UPLOAD[] PROGMEM = D_SDCARD_DIR; -#else -const char S_SCRIPT_FILE_UPLOAD[] PROGMEM = D_SDCARD_UPLOAD; -#endif - -const char HTTP_FORM_FILE_UPLOAD[] PROGMEM = -"
" -"
 %s" " "; -const char HTTP_FORM_FILE_UPG[] PROGMEM = -"
" -"

" -"
"; - -const char HTTP_FORM_FILE_UPGb[] PROGMEM = -"
" -"
" -""; - -const char HTTP_FORM_SDC_DIRa[] PROGMEM = -"
"; -const char HTTP_FORM_SDC_DIRb[] PROGMEM = - "
%s    %d
"; -const char HTTP_FORM_SDC_DIRd[] PROGMEM = -"
%s
"; -const char HTTP_FORM_SDC_DIRc[] PROGMEM = -"
"; -const char HTTP_FORM_SDC_HREF[] PROGMEM = -"http://%s/upl?download=%s/%s"; -#endif - - - -#ifdef USE_SCRIPT_FATFS - -#if USE_LONG_FILE_NAMES>0 -#undef REJCMPL -#define REJCMPL 6 -#else -#undef REJCMPL -#define REJCMPL 8 -#endif - -uint8_t reject(char *name) { - - if (*name=='_') return 1; - if (*name=='.') return 1; - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 - if (!strncasecmp(name,"SPOTLI~1",REJCMPL)) return 1; - if (!strncasecmp(name,"TRASHE~1",REJCMPL)) return 1; - if (!strncasecmp(name,"FSEVEN~1",REJCMPL)) return 1; - if (!strncasecmp(name,"SYSTEM~1",REJCMPL)) return 1; -#else - if (!strcasecmp(name,"SPOTLI~1")) return 1; - if (!strcasecmp(name,"TRASHE~1")) return 1; - if (!strcasecmp(name,"FSEVEN~1")) return 1; - if (!strcasecmp(name,"SYSTEM~1")) return 1; -#endif - return 0; -} - -void ListDir(char *path, uint8_t depth) { - char name[32]; - char npath[128]; - char format[12]; - sprintf(format,"%%-%ds",24-depth); - - File dir=SD.open(path); - if (dir) { - dir.rewindDirectory(); - if (strlen(path)>1) { - snprintf_P(npath,sizeof(npath),PSTR("http://%s/upl?download=%s"),WiFi.localIP().toString().c_str(),path); - for (uint8_t cnt=strlen(npath)-1;cnt>0;cnt--) { - if (npath[cnt]=='/') { - if (npath[cnt-1]=='=') npath[cnt+1]=0; - else npath[cnt]=0; - break; - } - } - WSContentSend_P(HTTP_FORM_SDC_DIRd,npath,path,".."); - } - while (true) { - File entry=dir.openNextFile(); - if (!entry) { - break; - } - char *pp=path; - if (!*(pp+1)) pp++; - char *cp=name; - - if (reject((char*)entry.name())) goto fclose; - - for (uint8_t cnt=0;cnt1) { - strcat(path,"/"); - } - strcat(path,entry.name()); - ListDir(path,depth+4); - path[plen]=0; - } else { - snprintf_P(npath,sizeof(npath),HTTP_FORM_SDC_HREF,WiFi.localIP().toString().c_str(),pp,entry.name()); - WSContentSend_P(HTTP_FORM_SDC_DIRb,npath,entry.name(),name,entry.size()); - } - fclose: - entry.close(); - } - dir.close(); - } -} - -char path[48]; - -void Script_FileUploadConfiguration(void) -{ - uint8_t depth=0; - strcpy(path,"/"); - - if (!HttpCheckPriviledgedAccess()) { return; } - - if (WebServer->hasArg("download")) { - String stmp = WebServer->arg("download"); - char *cp=(char*)stmp.c_str(); - if (DownloadFile(cp)) { - - strcpy(path,cp); - } - } - - WSContentStart_P(S_SCRIPT_FILE_UPLOAD); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_FILE_UPLOAD,D_SDCARD_DIR); - WSContentSend_P(HTTP_FORM_FILE_UPG, D_SCRIPT_UPLOAD); -#ifdef SDCARD_DIR - WSContentSend_P(HTTP_FORM_SDC_DIRa); - if (glob_script_mem.script_sd_found) { - ListDir(path,depth); - } - WSContentSend_P(HTTP_FORM_SDC_DIRc); -#endif - WSContentSend_P(HTTP_FORM_FILE_UPGb); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); - Web.upload_error = 0; -} - -File upload_file; - -void ScriptFileUploadSuccess(void) { - WSContentStart_P(S_INFORMATION); - WSContentSendStyle(); - WSContentSend_P(PSTR("
" D_UPLOAD " " D_SUCCESSFUL "
"), WebColor(COL_TEXT_SUCCESS)); - WSContentSend_P(PSTR("

")); - WSContentSend_P(PSTR("

"),"/upl",D_UPL_DONE); - - WSContentStop(); -} - - - -void script_upload(void) { - - - - HTTPUpload& upload = WebServer->upload(); - if (upload.status == UPLOAD_FILE_START) { - char npath[48]; - sprintf(npath,"%s/%s",path,upload.filename.c_str()); - SD.remove(npath); - upload_file=SD.open(npath,FILE_WRITE); - if (!upload_file) Web.upload_error=1; - } else if(upload.status == UPLOAD_FILE_WRITE) { - if (upload_file) upload_file.write(upload.buf,upload.currentSize); - } else if(upload.status == UPLOAD_FILE_END) { - if (upload_file) upload_file.close(); - if (Web.upload_error) { - AddLog_P(LOG_LEVEL_INFO, PSTR("HTP: upload error")); - } - } else { - Web.upload_error=1; - WebServer->send(500, "text/plain", "500: couldn't create file"); - } -} - -uint8_t DownloadFile(char *file) { - File download_file; - WiFiClient download_Client; - - if (!SD.exists(file)) { - AddLog_P(LOG_LEVEL_INFO,PSTR("file not found")); - return 0; - } - - download_file=SD.open(file,FILE_READ); - if (!download_file) { - AddLog_P(LOG_LEVEL_INFO,PSTR("could not open file")); - return 0; - } - - if (download_file.isDirectory()) { - download_file.close(); - return 1; - } - - uint32_t flen=download_file.size(); - - download_Client = WebServer->client(); - WebServer->setContentLength(flen); - - char attachment[100]; - char *cp; - for (uint8_t cnt=strlen(file); cnt>=0; cnt--) { - if (file[cnt]=='/') { - cp=&file[cnt+1]; - break; - } - } - snprintf_P(attachment, sizeof(attachment), PSTR("attachment; filename=%s"),cp); - WebServer->sendHeader(F("Content-Disposition"), attachment); - WSSend(200, CT_STREAM, ""); - - uint8_t buff[512]; - uint16_t bread; - - - uint8_t cnt=0; - while (download_file.available()) { - bread=download_file.read(buff,sizeof(buff)); - uint16_t bw=download_Client.write((const char*)buff,bread); - if (!bw) break; - cnt++; - if (cnt>7) { - cnt=0; - if (glob_script_mem.script_loglevel&0x80) { - - loop(); - } - } - } - download_file.close(); - download_Client.stop(); - return 0; -} - -#endif - - -void HandleScriptTextareaConfiguration(void) { - if (!HttpCheckPriviledgedAccess()) { return; } - - if (WebServer->hasArg("save")) { - ScriptSaveSettings(); - HandleConfiguration(); - return; - } -} - -void HandleScriptConfiguration(void) { - - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_SCRIPT); - -#ifdef USE_SCRIPT_FATFS - if (WebServer->hasArg("d1")) { - DownloadFile(glob_script_mem.flink[0]); - } - if (WebServer->hasArg("d2")) { - DownloadFile(glob_script_mem.flink[1]); - } - if (WebServer->hasArg("upl")) { - Script_FileUploadConfiguration(); - } -#endif - - WSContentStart_P(S_CONFIGURE_SCRIPT); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_SCRIPT); - - -#ifdef xSCRIPT_STRIP_COMMENTS - uint16_t ssize=glob_script_mem.script_size; - if (bitRead(Settings.rule_enabled, 1)) ssize*=2; - WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",ssize); -#else - WSContentSend_P(HTTP_FORM_SCRIPT1,1,1,bitRead(Settings.rule_enabled,0) ? " checked" : "",glob_script_mem.script_size); -#endif - - - if (glob_script_mem.script_ram[0]) { - _WSContentSend(glob_script_mem.script_ram); - } - WSContentSend_P(HTTP_FORM_SCRIPT1b); - -#ifdef USE_SCRIPT_FATFS - if (glob_script_mem.script_sd_found) { - WSContentSend_P(HTTP_FORM_SCRIPT1d); - if (glob_script_mem.flink[0][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,1,glob_script_mem.flink[0]); - if (glob_script_mem.flink[1][0]) WSContentSend_P(HTTP_FORM_SCRIPT1c,2,glob_script_mem.flink[1]); - } -#endif - - WSContentSend_P(HTTP_SCRIPT_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); - } - - -void ScriptSaveSettings(void) { - - if (WebServer->hasArg("c1")) { - bitWrite(Settings.rule_enabled,0,1); - } else { - bitWrite(Settings.rule_enabled,0,0); - } - - - String str = WebServer->arg("t1"); - - if (*str.c_str()) { - - str.replace("\r\n","\n"); - str.replace("\r","\n"); - -#ifdef xSCRIPT_STRIP_COMMENTS - if (bitRead(Settings.rule_enabled, 1)) { - char *sp=(char*)str.c_str(); - char *sp1=sp; - char *dp=sp; - uint8_t flg=0; - while (*sp) { - while (*sp==' ') sp++; - sp1=sp; - sp=strchr(sp,'\n'); - if (!sp) { - flg=1; - } else { - *sp=0; - } - if (*sp1!=';') { - uint8_t slen=strlen(sp1); - if (slen) { - strcpy(dp,sp1); - dp+=slen; - *dp++='\n'; - } - } - if (flg) { - *dp=0; - break; - } - sp++; - } - } -#endif - - strlcpy(glob_script_mem.script_ram,str.c_str(), glob_script_mem.script_size); - -#ifdef USE_24C256 -#ifndef USE_SCRIPT_FATFS - if (glob_script_mem.flags&1) { - EEP_WRITE(0,EEP_SCRIPT_SIZE,glob_script_mem.script_ram); - } -#endif -#endif - -#ifdef USE_SCRIPT_FATFS - if (glob_script_mem.flags&1) { - SD.remove(FAT_SCRIPT_NAME); - File file=SD.open(FAT_SCRIPT_NAME,FILE_WRITE); - file.write(glob_script_mem.script_ram,FAT_SCRIPT_SIZE); - file.close(); - } -#endif - - } - - if (glob_script_mem.script_mem) { - Scripter_save_pvars(); - free(glob_script_mem.script_mem); - glob_script_mem.script_mem=0; - glob_script_mem.script_mem_size=0; - } - - if (bitRead(Settings.rule_enabled, 0)) { - int16_t res=Init_Scripter(); - if (res) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("script init error: %d"), res); - return; - } - Run_Scripter(">B",2,0); - fast_script=Run_Scripter(">F",-2,0); - } -} - -#endif - - -#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) - - -#define HUE_DEV_MVNUM 5 -#define HUE_DEV_NSIZE 16 -struct HUE_SCRIPT { - char name[HUE_DEV_NSIZE]; - uint8_t type; - uint8_t index[HUE_DEV_MVNUM]; - uint8_t vindex[HUE_DEV_MVNUM]; -} hue_script[32]; - - -const char SCRIPT_HUE_LIGHTS_STATUS_JSON1[] PROGMEM = - "{\"state\":" - "{\"on\":{state}," - "{light_status}" - "\"alert\":\"none\"," - "\"effect\":\"none\"," - "\"reachable\":true}" - ",\"type\":\"{type}\"," - "\"name\":\"{j1\"," - "\"modelid\":\"{m1}\"," - "\"uniqueid\":\"{j2\"," - "\"swversion\":\"5.50.1.19085\"}"; -# 3624 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" -const char SCRIPT_HUE_LIGHTS_STATUS_JSON2[] PROGMEM = -"{\"state\":{" -"\"presence\":{state}," -"\"lastupdated\":\"2017-10-01T12:37:30\"" -"}," -"\"swupdate\":{" -"\"state\":\"noupdates\"," -"\"lastinstall\": null" -"}," -"\"config\":{" -"\"on\":true," -"\"battery\":100," -"\"reachable\":true," -"\"alert\":\"none\"," -"\"ledindication\":false," -"\"usertest\":false," -"\"sensitivity\":2," -"\"sensitivitymax\":2," -"\"pending\":[]" -"}," -"\"name\":\"{j1\"," -"\"type\":\"ZLLPresence\"," -"\"modelid\":\"SML001\"," -"\"manufacturername\":\"Philips\"," -"\"swversion\":\"6.1.0.18912\"," -"\"uniqueid\":\"{j2\"" -"}"; -# 3705 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" -void Script_HueStatus(String *response, uint16_t hue_devs) { - - if (hue_script[hue_devs].type=='p') { - *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON2); - response->replace("{j1",hue_script[hue_devs].name); - response->replace("{j2", GetHueDeviceId(hue_devs)); - uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; - response->replace("{state}", (pwr ? "true" : "false")); - return; - } - - *response+=FPSTR(SCRIPT_HUE_LIGHTS_STATUS_JSON1); - uint8_t pwr=glob_script_mem.fvars[hue_script[hue_devs].index[0]-1]; - response->replace("{state}", (pwr ? "true" : "false")); - String light_status = ""; - if (hue_script[hue_devs].index[1]>0) { - - light_status += "\"bri\":"; - uint32_t bri=glob_script_mem.fvars[hue_script[hue_devs].index[1]-1]; - if (bri > 254) bri = 254; - if (bri < 1) bri = 1; - light_status += String(bri); - light_status += ","; - } - if (hue_script[hue_devs].index[2]>0) { - - uint32_t hue=glob_script_mem.fvars[hue_script[hue_devs].index[2]-1]; - - light_status += "\"hue\":"; - light_status += String(hue); - light_status += ","; - } - if (hue_script[hue_devs].index[3]>0) { - - uint32_t sat=glob_script_mem.fvars[hue_script[hue_devs].index[3]-1] ; - if (sat > 254) sat = 254; - if (sat < 1) sat = 1; - light_status += "\"sat\":"; - light_status += String(sat); - light_status += ","; - } - if (hue_script[hue_devs].index[4]>0) { - - uint32_t ct=glob_script_mem.fvars[hue_script[hue_devs].index[4]-1]; - light_status += "\"ct\":"; - light_status += String(ct); - light_status += ","; - } - - float temp; - switch (hue_script[hue_devs].type) { - case 'C': - response->replace("{type}","Color Ligh"); - response->replace("{m1","LST001"); - break; - case 'D': - response->replace("{type}","Dimmable Light"); - response->replace("{m1","LWB004"); - break; - case 'T': - response->replace("{type}","Color Temperature Light"); - response->replace("{m1","LTW011"); - break; - case 'E': - response->replace("{type}","Extended color light"); - response->replace("{m1","LCT007"); - break; - case 'S': - response->replace("{type}","On/Off light"); - response->replace("{m1","LCT007"); - break; - default: - response->replace("{type}","color light"); - response->replace("{m1","LST001"); - break; - } - - response->replace("{light_status}", light_status); - response->replace("{j1",hue_script[hue_devs].name); - response->replace("{j2", GetHueDeviceId(hue_devs)); - -} - -void Script_Check_Hue(String *response) { - if (!bitRead(Settings.rule_enabled, 0)) return; - - uint8_t hue_script_found=Run_Scripter(">H",-2,0); - if (hue_script_found!=99) return; - - char line[128]; - char tmp[128]; - uint8_t hue_devs=0; - uint8_t vindex=0; - char *cp; - char *lp=glob_script_mem.section_ptr+2; - while (lp) { - SCRIPT_SKIP_SPACES - while (*lp==SCRIPT_EOL) { - lp++; - } - if (!*lp || *lp=='#' || *lp=='>') { - break; - } - if (*lp!=';') { - - memcpy(line,lp,sizeof(line)); - line[sizeof(line)-1]=0; - cp=line; - for (uint32_t i=0; i0) *response+=",\""; - } - *response+=String(EncodeLightId(hue_devs+devices_present+1))+"\":"; - Script_HueStatus(response,hue_devs); - } - - hue_devs++; - } - if (*lp==SCRIPT_EOL) { - lp++; - } else { - lp = strchr(lp, SCRIPT_EOL); - if (!lp) break; - lp++; - } - } -#if 0 - if (response) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Hue: %d"), hue_devs); - toLog(">>>>"); - toLog(response->c_str()); - toLog(response->c_str()+LOGSZ); - } -#endif -} - -const char sHUE_LIGHT_RESPONSE_JSON[] PROGMEM = - "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; - -const char sHUE_SENSOR_RESPONSE_JSON[] PROGMEM = - "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; - -const char sHUE_ERROR_JSON[] PROGMEM = - "[{\"error\":{\"type\":901,\"address\":\"/\",\"description\":\"Internal Error\"}}]"; - - - -void Script_Handle_Hue(String *path) { - String response; - int code = 200; - uint16_t tmp = 0; - uint16_t hue = 0; - uint8_t sat = 0; - uint8_t bri = 254; - uint16_t ct = 0; - bool resp = false; - - uint8_t device = DecodeLightId(atoi(path->c_str())); - uint8_t index = device-devices_present-1; - - if (WebServer->args()) { - response = "["; - - StaticJsonBuffer<400> jsonBuffer; - JsonObject &hue_json = jsonBuffer.parseObject(WebServer->arg((WebServer->args())-1)); - if (hue_json.containsKey("on")) { - - response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(EncodeLightId(device))); - response.replace("{cm", "on"); - - bool on = hue_json["on"]; - switch(on) - { - case false : glob_script_mem.fvars[hue_script[index].index[0]-1]=0; - response.replace("{re", "false"); - break; - case true : glob_script_mem.fvars[hue_script[index].index[0]-1]=1; - response.replace("{re", "true"); - break; - } - glob_script_mem.type[hue_script[index].vindex[0]].bits.changed=1; - resp = true; - } - if (hue_json.containsKey("bri")) { - tmp = hue_json["bri"]; - bri=tmp; - if (254 <= bri) { bri = 255; } - if (resp) { response += ","; } - response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(EncodeLightId(device))); - response.replace("{cm", "bri"); - response.replace("{re", String(tmp)); - glob_script_mem.fvars[hue_script[index].index[1]-1]=bri; - glob_script_mem.type[hue_script[index].vindex[1]].bits.changed=1; - resp = true; - } - if (hue_json.containsKey("xy")) { - float x, y; - x = hue_json["xy"][0]; - y = hue_json["xy"][1]; - const String &x_str = hue_json["xy"][0]; - const String &y_str = hue_json["xy"][1]; - uint8_t rr,gg,bb; - LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); - LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); - if (resp) { response += ","; } - response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(device)); - response.replace("{cm", "xy"); - response.replace("{re", "[" + x_str + "," + y_str + "]"); - glob_script_mem.fvars[hue_script[index].index[2]-1]=hue; - glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1; - glob_script_mem.fvars[hue_script[index].index[3]-1]=sat; - glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1; - resp = true; - } - - if (hue_json.containsKey("hue")) { - tmp = hue_json["hue"]; - - - hue=tmp; - if (resp) { response += ","; } - response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(EncodeLightId(device))); - response.replace("{cm", "hue"); - response.replace("{re", String(tmp)); - glob_script_mem.fvars[hue_script[index].index[2]-1]=hue; - glob_script_mem.type[hue_script[index].vindex[2]].bits.changed=1; - resp = true; - } - if (hue_json.containsKey("sat")) { - tmp = hue_json["sat"]; - sat=tmp; - if (254 <= sat) { sat = 255; } - if (resp) { response += ","; } - response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(EncodeLightId(device))); - response.replace("{cm", "sat"); - response.replace("{re", String(tmp)); - glob_script_mem.fvars[hue_script[index].index[3]-1]=sat; - glob_script_mem.type[hue_script[index].vindex[3]].bits.changed=1; - resp = true; - } - if (hue_json.containsKey("ct")) { - ct = hue_json["ct"]; - if (resp) { response += ","; } - response += FPSTR(sHUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(EncodeLightId(device))); - response.replace("{cm", "ct"); - response.replace("{re", String(ct)); - glob_script_mem.fvars[hue_script[index].index[4]-1]=ct; - glob_script_mem.type[hue_script[index].vindex[4]].bits.changed=1; - resp = true; - } - response += "]"; - - } else { - response = FPSTR(sHUE_ERROR_JSON); - } - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); - WSSend(code, CT_JSON, response); - if (resp) { - Run_Scripter(">E",2,0); - } -} -#endif - - -#ifdef USE_SCRIPT_SUB_COMMAND -bool Script_SubCmd(void) { - if (!bitRead(Settings.rule_enabled, 0)) return false; - - if (tasm_cmd_activ) return false; - - char command[CMDSZ]; - strlcpy(command,XdrvMailbox.topic,CMDSZ); - uint32_t pl=XdrvMailbox.payload; - char pld[64]; - strlcpy(pld,XdrvMailbox.data,sizeof(pld)); - - char cmdbuff[128]; - char *cp=cmdbuff; - *cp++='#'; - strcpy(cp,XdrvMailbox.topic); - uint8_t tlen=strlen(XdrvMailbox.topic); - cp+=tlen; - if (XdrvMailbox.index > 0) { - *cp++=XdrvMailbox.index|0x30; - tlen++; - } - if ((XdrvMailbox.payload>0) || (XdrvMailbox.data_len>0)) { - *cp++='('; - strncpy(cp,XdrvMailbox.data,XdrvMailbox.data_len); - cp+=XdrvMailbox.data_len; - *cp++=')'; - *cp=0; - } - - uint32_t res=Run_Scripter(cmdbuff,tlen+1,0); - - if (res) return false; - else { - if (pl>=0) { - Response_P(S_JSON_COMMAND_NVALUE, command, pl); - } else { - Response_P(S_JSON_COMMAND_SVALUE, command, pld); - } - } - return true; -} -#endif - -void execute_script(char *script) { - char *svd_sp=glob_script_mem.scriptptr; - strcat(script,"\n#"); - glob_script_mem.scriptptr=script; - Run_Scripter(">",1,0); - glob_script_mem.scriptptr=svd_sp; -} -#define D_CMND_SCRIPT "Script" -#define D_CMND_SUBSCRIBE "Subscribe" -#define D_CMND_UNSUBSCRIBE "Unsubscribe" - -enum ScriptCommands { CMND_SCRIPT,CMND_SUBSCRIBE, CMND_UNSUBSCRIBE }; -const char kScriptCommands[] PROGMEM = D_CMND_SCRIPT "|" D_CMND_SUBSCRIBE "|" D_CMND_UNSUBSCRIBE; - -bool ScriptCommand(void) { - char command[CMDSZ]; - bool serviced = true; - uint8_t index = XdrvMailbox.index; - - if (tasm_cmd_activ) return false; - - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kScriptCommands); - if (-1 == command_code) { - serviced = false; - } - else if ((CMND_SCRIPT == command_code) && (index > 0)) { - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { - switch (XdrvMailbox.payload) { - case 0: - case 1: - bitWrite(Settings.rule_enabled, index -1, XdrvMailbox.payload); - break; -#ifdef xSCRIPT_STRIP_COMMENTS - case 2: - bitWrite(Settings.rule_enabled, 1,0); - break; - case 3: - bitWrite(Settings.rule_enabled, 1,1); - break; -#endif - } - } else { - if ('>' == XdrvMailbox.data[0]) { - - snprintf_P (mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%s\"}"),command,XdrvMailbox.data); - if (bitRead(Settings.rule_enabled, 0)) { - for (uint8_t count=0; count> 1; -} - -void dateTime(uint16_t* date, uint16_t* time) { - - *date = xFAT_DATE(RtcTime.year,RtcTime.month, RtcTime.day_of_month); - - *time = xFAT_TIME(RtcTime.hour,RtcTime.minute,RtcTime.second); -} - -#endif - - - -#ifdef SUPPORT_MQTT_EVENT -# 4199 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" -bool ScriptMqttData(void) -{ - bool serviced = false; - - toLog(XdrvMailbox.data); - if (XdrvMailbox.data_len < 1 || XdrvMailbox.data_len > 256) { - return false; - } - String sTopic = XdrvMailbox.topic; - String sData = XdrvMailbox.data; - - MQTT_Subscription event_item; - - for (uint32_t index = 0; index < subscriptions.size(); index++) { - event_item = subscriptions.get(index); - - - if (sTopic.startsWith(event_item.Topic)) { - - serviced = true; - String value; - String lkey; - if (event_item.Key.length() == 0) { - value = sData; - } else { - StaticJsonBuffer<400> jsonBuf; - JsonObject& jsonData = jsonBuf.parseObject(sData); - String key1 = event_item.Key; - String key2; - if (!jsonData.success()) break; - int dot; - if ((dot = key1.indexOf('.')) > 0) { - key2 = key1.substring(dot+1); - key1 = key1.substring(0, dot); - lkey=key2; - if (!jsonData[key1][key2].success()) break; - value = (const char *)jsonData[key1][key2]; - } else { - if (!jsonData[key1].success()) break; - value = (const char *)jsonData[key1]; - lkey=key1; - } - } - value.trim(); - char sbuffer[128]; - - if (!strncmp(lkey.c_str(),"Epoch",5)) { - uint32_t ep=atoi(value.c_str())-(uint32_t)EPOCH_OFFSET; - snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=%d\n"), event_item.Event.c_str(),ep); - } else { - snprintf_P(sbuffer, sizeof(sbuffer), PSTR(">%s=\"%s\"\n"), event_item.Event.c_str(), value.c_str()); - } - - execute_script(sbuffer); - } - } - return serviced; -} -# 4274 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" -String ScriptSubscribe(const char *data, int data_len) -{ - MQTT_Subscription subscription_item; - String events; - if (data_len > 0) { - char parameters[data_len+1]; - memcpy(parameters, data, data_len); - parameters[data_len] = '\0'; - String event_name, topic, key; - - char * pos = strtok(parameters, ","); - if (pos) { - event_name = Trim(pos); - pos = strtok(nullptr, ","); - if (pos) { - topic = Trim(pos); - pos = strtok(nullptr, ","); - if (pos) { - key = Trim(pos); - } - } - } - - - if (event_name.length() > 0 && topic.length() > 0) { - - for (uint32_t index=0; index < subscriptions.size(); index++) { - if (subscriptions.get(index).Event.equals(event_name)) { - - String stopic = subscriptions.get(index).Topic + "/#"; - MqttUnsubscribe(stopic.c_str()); - subscriptions.remove(index); - break; - } - } - - if (!topic.endsWith("#")) { - if (topic.endsWith("/")) { - topic.concat("#"); - } else { - topic.concat("/#"); - } - } - - - subscription_item.Event = event_name; - subscription_item.Topic = topic.substring(0, topic.length() - 2); - subscription_item.Key = key; - subscriptions.add(subscription_item); - - MqttSubscribe(topic.c_str()); - events.concat(event_name + "," + topic - + (key.length()>0 ? "," : "") - + key); - } else { - events = D_JSON_WRONG_PARAMETERS; - } - } else { - - for (uint32_t index=0; index < subscriptions.size(); index++) { - subscription_item = subscriptions.get(index); - events.concat(subscription_item.Event + "," + subscription_item.Topic - + (subscription_item.Key.length()>0 ? "," : "") - + subscription_item.Key + "; "); - } - } - return events; -} -# 4354 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_10_scripter.ino" -String ScriptUnsubscribe(const char * data, int data_len) -{ - MQTT_Subscription subscription_item; - String events; - if (data_len > 0) { - for (uint32_t index = 0; index < subscriptions.size(); index++) { - subscription_item = subscriptions.get(index); - if (subscription_item.Event.equalsIgnoreCase(data)) { - String stopic = subscription_item.Topic + "/#"; - MqttUnsubscribe(stopic.c_str()); - events = subscription_item.Event; - subscriptions.remove(index); - break; - } - } - } else { - - String stopic; - while (subscriptions.size() > 0) { - events.concat(subscriptions.get(0).Event + "; "); - stopic = subscriptions.get(0).Topic + "/#"; - MqttUnsubscribe(stopic.c_str()); - subscriptions.remove(0); - } - } - return events; -} -#endif - - - -#ifdef USE_SCRIPT_WEB_DISPLAY - -void Script_Check_HTML_Setvars(void) { - - if (!HttpCheckPriviledgedAccess()) { return; } - - if (WebServer->hasArg("sv")) { - String stmp = WebServer->arg("sv"); - char cmdbuf[64]; - memset(cmdbuf,0,sizeof(cmdbuf)); - char *cp=cmdbuf; - *cp++='>'; - strncpy(cp,stmp.c_str(),sizeof(cmdbuf)-1); - char *cp1=strchr(cp,'_'); - if (!cp1) return; - *cp1=0; - char vname[32]; - strncpy(vname,cp,sizeof(vname)); - *cp1='='; - cp1++; - - struct T_INDEX ind; - uint8_t vtype; - isvar(vname,&vtype,&ind,0,0,0); - if (vtype!=NUM_RES && vtype&STYPE) { - - uint8_t tlen=strlen(cp1); - memmove(cp1+1,cp1,tlen); - *cp1='\"'; - *(cp1+tlen+1)='\"'; - } - - - execute_script(cmdbuf); - Run_Scripter(">E",2,0); - } -} - - -const char SCRIPT_MSG_BUTTONa[] PROGMEM = - ""; - -const char SCRIPT_MSG_BUTTONa_TBL[] PROGMEM = - ""; - -const char SCRIPT_MSG_BUTTONb[] PROGMEM = - ""; - -const char SCRIPT_MSG_BUT_START[] PROGMEM = - "
"; -const char SCRIPT_MSG_BUT_START_TBL[] PROGMEM = - ""; - -const char SCRIPT_MSG_BUT_STOP[] PROGMEM = - ""; -const char SCRIPT_MSG_BUT_STOP_TBL[] PROGMEM = - "
"; - -const char SCRIPT_MSG_SLIDER[] PROGMEM = - "
%s
%s%s
" - "
"; - -const char SCRIPT_MSG_CHKBOX[] PROGMEM = - "
"; - -const char SCRIPT_MSG_TEXTINP[] PROGMEM = - "
"; - -const char SCRIPT_MSG_NUMINP[] PROGMEM = - "
"; - - -void ScriptGetVarname(char *nbuf,char *sp, uint32_t blen) { -uint32_t cnt; - for (cnt=0;cntW",-2,0); - if (web_script==99) { - char line[128]; - char tmp[128]; - uint8_t optflg=0; - char *lp=glob_script_mem.section_ptr+2; - while (lp) { - while (*lp==SCRIPT_EOL) { - lp++; - } - if (!*lp || *lp=='#' || *lp=='>') { - break; - } - if (*lp!=';') { - - memcpy(line,lp,sizeof(line)); - line[sizeof(line)-1]=0; - char *cp=line; - for (uint32_t i=0; i0) { - cp="checked='checked'"; - uval=0; - } else { - cp=""; - uval=1; - } - WSContentSend_PD(SCRIPT_MSG_CHKBOX,label,(char*)cp,uval,vname); - - } else if (!strncmp(lin,"bu(",3)) { - char *lp=lin+3; - uint8_t bcnt=0; - char *found=lin; - while (bcnt<4) { - found=strstr(found,"bu("); - if (!found) break; - found+=3; - bcnt++; - } - uint8_t proz=100/bcnt; - if (!optflg && bcnt>1) proz-=2; - if (optflg) WSContentSend_PD(SCRIPT_MSG_BUT_START_TBL); - else WSContentSend_PD(SCRIPT_MSG_BUT_START); - for (uint32_t cnt=0;cnt0) { - cp=ontxt; - uval=0; - } else { - cp=offtxt; - uval=1; - } - if (bcnt>1 && cnt==bcnt-1) { - if (!optflg) proz+=2; - } - if (!optflg) { - WSContentSend_PD(SCRIPT_MSG_BUTTONa,proz,uval,vname,cp); - } else { - WSContentSend_PD(SCRIPT_MSG_BUTTONa_TBL,proz,uval,vname,cp); - } - if (bcnt>1 && cnt%s
"),tmp); - } else { - WSContentSend_PD(PSTR("{s}%s{e}"),tmp); - } - } - } - if (*lp==SCRIPT_EOL) { - lp++; - } else { - lp = strchr(lp, SCRIPT_EOL); - if (!lp) break; - lp++; - } - } - } -} -#endif - - -#ifdef USE_SENDMAIL -void script_send_email_body(BearSSL::WiFiClientSecure_light *client) { -uint8_t msect=Run_Scripter(">m",-2,0); - if (msect==99) { - char line[128]; - char tmp[128]; - char *lp=glob_script_mem.section_ptr+2; - while (lp) { - while (*lp==SCRIPT_EOL) { - lp++; - } - if (!*lp || *lp=='#' || *lp=='>') { - break; - } - if (*lp!=';') { - - memcpy(line,lp,sizeof(line)); - line[sizeof(line)-1]=0; - char *cp=line; - for (uint32_t i=0; iprintln(tmp); - } - if (*lp==SCRIPT_EOL) { - lp++; - } else { - lp = strchr(lp, SCRIPT_EOL); - if (!lp) break; - lp++; - } - } - } else { - client->println("*"); - } -} -#endif - -#ifdef USE_SCRIPT_JSON_EXPORT -void ScriptJsonAppend(void) { - uint8_t web_script=Run_Scripter(">J",-2,0); - if (web_script==99) { - char line[128]; - char tmp[128]; - char *lp=glob_script_mem.section_ptr+2; - while (lp) { - while (*lp==SCRIPT_EOL) { - lp++; - } - if (!*lp || *lp=='#' || *lp=='>') { - break; - } - if (*lp!=';') { - - memcpy(line,lp,sizeof(line)); - line[sizeof(line)-1]=0; - char *cp=line; - for (uint32_t i=0; iB",2,0); - fast_script=Run_Scripter(">F",-2,0); -#if defined(USE_SCRIPT_HUE) && defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) - Script_Check_Hue(0); -#endif - } - break; - case FUNC_EVERY_100_MSECOND: - ScripterEvery100ms(); - break; - case FUNC_EVERY_SECOND: - ScriptEverySecond(); - break; - case FUNC_COMMAND: - result = ScriptCommand(); - break; - case FUNC_SET_POWER: -#ifdef SCRIPT_POWER_SECTION - if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">P",2,0); -#else - if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,0); -#endif - break; - case FUNC_RULES_PROCESS: - if (bitRead(Settings.rule_enabled, 0)) Run_Scripter(">E",2,mqtt_data); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_ADD_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_RULES); - break; - case FUNC_WEB_ADD_HANDLER: - WebServer->on("/" WEB_HANDLE_SCRIPT, HandleScriptConfiguration); - WebServer->on("/ta",HTTP_POST, HandleScriptTextareaConfiguration); - -#ifdef USE_SCRIPT_FATFS - WebServer->on("/u3", HTTP_POST,[]() { WebServer->sendHeader("Location","/u3");WebServer->send(303);},script_upload); - WebServer->on("/u3", HTTP_GET,ScriptFileUploadSuccess); - WebServer->on("/upl", HTTP_GET,Script_FileUploadConfiguration); -#endif - break; -#endif - case FUNC_SAVE_BEFORE_RESTART: - if (bitRead(Settings.rule_enabled, 0)) { - Run_Scripter(">R",2,0); - Scripter_save_pvars(); - } - break; -#ifdef SUPPORT_MQTT_EVENT - case FUNC_MQTT_DATA: - if (bitRead(Settings.rule_enabled, 0)) { - result = ScriptMqttData(); - } - break; -#endif -#ifdef USE_SCRIPT_WEB_DISPLAY - case FUNC_WEB_SENSOR: - if (bitRead(Settings.rule_enabled, 0)) { - ScriptWebShow(); - } - break; -#endif - -#ifdef USE_SCRIPT_JSON_EXPORT - case FUNC_JSON_APPEND: - if (bitRead(Settings.rule_enabled, 0)) { - ScriptJsonAppend(); - } - break; -#endif - -#ifdef USE_BUTTON_EVENT - case FUNC_BUTTON_PRESSED: - if (bitRead(Settings.rule_enabled, 0)) { - if ((script_button[XdrvMailbox.index]&1)!=(XdrvMailbox.payload&1)) { - script_button[XdrvMailbox.index]=XdrvMailbox.payload; - Run_Scripter(">b",2,0); - } - } - break; -#endif - - } - return result; -} - - - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_11_knx.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_11_knx.ino" -#ifdef USE_KNX -# 51 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_11_knx.ino" -#define XDRV_11 11 - -#include - -address_t KNX_physs_addr; -address_t KNX_addr; - -#define KNX_Empty 255 - -#define TOGGLE_INHIBIT_TIME 15 - -float last_temp; -float last_hum; -uint8_t toggle_inhibit; - -typedef struct __device_parameters -{ - uint8_t type; - - - - - bool show; - - bool last_state; - - callback_id_t CB_id; - - - - - -} device_parameters_t; - - -device_parameters_t device_param[] = { - { 1, false, false, KNX_Empty }, - { 2, false, false, KNX_Empty }, - { 3, false, false, KNX_Empty }, - { 4, false, false, KNX_Empty }, - { 5, false, false, KNX_Empty }, - { 6, false, false, KNX_Empty }, - { 7, false, false, KNX_Empty }, - { 8, false, false, KNX_Empty }, - { 9, false, false, KNX_Empty }, - { 10, false, false, KNX_Empty }, - { 11, false, false, KNX_Empty }, - { 12, false, false, KNX_Empty }, - { 13, false, false, KNX_Empty }, - { 14, false, false, KNX_Empty }, - { 15, false, false, KNX_Empty }, - { 16, false, false, KNX_Empty }, - { KNX_TEMPERATURE, false, false, KNX_Empty }, - { KNX_HUMIDITY , false, false, KNX_Empty }, - { KNX_ENERGY_VOLTAGE , false, false, KNX_Empty }, - { KNX_ENERGY_CURRENT , false, false, KNX_Empty }, - { KNX_ENERGY_POWER , false, false, KNX_Empty }, - { KNX_ENERGY_POWERFACTOR , false, false, KNX_Empty }, - { KNX_ENERGY_DAILY , false, false, KNX_Empty }, - { KNX_ENERGY_START , false, false, KNX_Empty }, - { KNX_ENERGY_TOTAL , false, false, KNX_Empty }, - { KNX_SLOT1 , false, false, KNX_Empty }, - { KNX_SLOT2 , false, false, KNX_Empty }, - { KNX_SLOT3 , false, false, KNX_Empty }, - { KNX_SLOT4 , false, false, KNX_Empty }, - { KNX_SLOT5 , false, false, KNX_Empty }, - { KNX_Empty, false, false, KNX_Empty} -}; - - -const char * device_param_ga[] = { - D_TIMER_OUTPUT " 1", - D_TIMER_OUTPUT " 2", - D_TIMER_OUTPUT " 3", - D_TIMER_OUTPUT " 4", - D_TIMER_OUTPUT " 5", - D_TIMER_OUTPUT " 6", - D_TIMER_OUTPUT " 7", - D_TIMER_OUTPUT " 8", - D_SENSOR_BUTTON " 1", - D_SENSOR_BUTTON " 2", - D_SENSOR_BUTTON " 3", - D_SENSOR_BUTTON " 4", - D_SENSOR_BUTTON " 5", - D_SENSOR_BUTTON " 6", - D_SENSOR_BUTTON " 7", - D_SENSOR_BUTTON " 8", - D_TEMPERATURE , - D_HUMIDITY , - D_VOLTAGE , - D_CURRENT , - D_POWERUSAGE , - D_POWER_FACTOR , - D_ENERGY_TODAY , - D_ENERGY_YESTERDAY , - D_ENERGY_TOTAL , - D_KNX_TX_SLOT " 1", - D_KNX_TX_SLOT " 2", - D_KNX_TX_SLOT " 3", - D_KNX_TX_SLOT " 4", - D_KNX_TX_SLOT " 5", - nullptr -}; - - -const char *device_param_cb[] = { - D_TIMER_OUTPUT " 1", - D_TIMER_OUTPUT " 2", - D_TIMER_OUTPUT " 3", - D_TIMER_OUTPUT " 4", - D_TIMER_OUTPUT " 5", - D_TIMER_OUTPUT " 6", - D_TIMER_OUTPUT " 7", - D_TIMER_OUTPUT " 8", - D_TIMER_OUTPUT " 1 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 2 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 3 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 4 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 5 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 6 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 7 " D_BUTTON_TOGGLE, - D_TIMER_OUTPUT " 8 " D_BUTTON_TOGGLE, - D_REPLY " " D_TEMPERATURE, - D_REPLY " " D_HUMIDITY, - D_REPLY " " D_VOLTAGE , - D_REPLY " " D_CURRENT , - D_REPLY " " D_POWERUSAGE , - D_REPLY " " D_POWER_FACTOR , - D_REPLY " " D_ENERGY_TODAY , - D_REPLY " " D_ENERGY_YESTERDAY , - D_REPLY " " D_ENERGY_TOTAL , - D_KNX_RX_SLOT " 1", - D_KNX_RX_SLOT " 2", - D_KNX_RX_SLOT " 3", - D_KNX_RX_SLOT " 4", - D_KNX_RX_SLOT " 5", - nullptr -}; - - -#define D_PRFX_KNX "Knx" -#define D_CMND_KNXTXCMND "Tx_Cmnd" -#define D_CMND_KNXTXVAL "Tx_Val" -#define D_CMND_KNX_ENABLED "_Enabled" -#define D_CMND_KNX_ENHANCED "_Enhanced" -#define D_CMND_KNX_PA "_PA" -#define D_CMND_KNX_GA "_GA" -#define D_CMND_KNX_CB "_CB" - -const char kKnxCommands[] PROGMEM = D_PRFX_KNX "|" - D_CMND_KNXTXCMND "|" D_CMND_KNXTXVAL "|" D_CMND_KNX_ENABLED "|" D_CMND_KNX_ENHANCED "|" D_CMND_KNX_PA "|" D_CMND_KNX_GA "|" D_CMND_KNX_CB ; - -void (* const KnxCommand[])(void) PROGMEM = { - &CmndKnxTxCmnd, &CmndKnxTxVal, &CmndKnxEnabled, &CmndKnxEnhanced, &CmndKnxPa, &CmndKnxGa, &CmndKnxCb }; - -uint8_t KNX_GA_Search( uint8_t param, uint8_t start = 0 ) -{ - for (uint32_t i = start; i < Settings.knx_GA_registered; ++i) - { - if ( Settings.knx_GA_param[i] == param ) - { - if ( Settings.knx_GA_addr[i] != 0 ) - { - if ( i >= start ) { return i; } - } - } - } - return KNX_Empty; -} - - -uint8_t KNX_CB_Search( uint8_t param, uint8_t start = 0 ) -{ - for (uint32_t i = start; i < Settings.knx_CB_registered; ++i) - { - if ( Settings.knx_CB_param[i] == param ) - { - if ( Settings.knx_CB_addr[i] != 0 ) - { - if ( i >= start ) { return i; } - } - } - } - return KNX_Empty; -} - - -void KNX_ADD_GA( uint8_t GAop, uint8_t GA_FNUM, uint8_t GA_AREA, uint8_t GA_FDEF ) -{ - - if ( Settings.knx_GA_registered >= MAX_KNX_GA ) { return; } - if ( GA_FNUM == 0 && GA_AREA == 0 && GA_FDEF == 0 ) { return; } - - - Settings.knx_GA_param[Settings.knx_GA_registered] = GAop; - KNX_addr.ga.area = GA_FNUM; - KNX_addr.ga.line = GA_AREA; - KNX_addr.ga.member = GA_FDEF; - Settings.knx_GA_addr[Settings.knx_GA_registered] = KNX_addr.value; - - Settings.knx_GA_registered++; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " GA #%d: %s " D_TO " %d/%d/%d"), - Settings.knx_GA_registered, - device_param_ga[GAop-1], - GA_FNUM, GA_AREA, GA_FDEF ); -} - - -void KNX_DEL_GA( uint8_t GAnum ) -{ - - uint8_t dest_offset = 0; - uint8_t src_offset = 0; - uint8_t len = 0; - - - Settings.knx_GA_param[GAnum-1] = 0; - - if (GAnum == 1) - { - - src_offset = 1; - - - - len = (Settings.knx_GA_registered - 1); - } - else if (GAnum == Settings.knx_GA_registered) - { - - } - else - { - - - - - dest_offset = GAnum -1 ; - src_offset = dest_offset + 1; - len = (Settings.knx_GA_registered - GAnum); - } - - if (len > 0) - { - memmove(Settings.knx_GA_param + dest_offset, Settings.knx_GA_param + src_offset, len * sizeof(uint8_t)); - memmove(Settings.knx_GA_addr + dest_offset, Settings.knx_GA_addr + src_offset, len * sizeof(uint16_t)); - } - - Settings.knx_GA_registered--; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " GA #%d"), - GAnum ); -} - - -void KNX_ADD_CB( uint8_t CBop, uint8_t CB_FNUM, uint8_t CB_AREA, uint8_t CB_FDEF ) -{ - - if ( Settings.knx_CB_registered >= MAX_KNX_CB ) { return; } - if ( CB_FNUM == 0 && CB_AREA == 0 && CB_FDEF == 0 ) { return; } - - - if ( device_param[CBop-1].CB_id == KNX_Empty ) - { - - device_param[CBop-1].CB_id = knx.callback_register("", KNX_CB_Action, &device_param[CBop-1]); - - - - - } - - Settings.knx_CB_param[Settings.knx_CB_registered] = CBop; - KNX_addr.ga.area = CB_FNUM; - KNX_addr.ga.line = CB_AREA; - KNX_addr.ga.member = CB_FDEF; - Settings.knx_CB_addr[Settings.knx_CB_registered] = KNX_addr.value; - - knx.callback_assign( device_param[CBop-1].CB_id, KNX_addr ); - - Settings.knx_CB_registered++; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ADD " CB #%d: %d/%d/%d " D_TO " %s"), - Settings.knx_CB_registered, - CB_FNUM, CB_AREA, CB_FDEF, - device_param_cb[CBop-1] ); -} - - -void KNX_DEL_CB( uint8_t CBnum ) -{ - uint8_t oldparam = Settings.knx_CB_param[CBnum-1]; - uint8_t dest_offset = 0; - uint8_t src_offset = 0; - uint8_t len = 0; - - - knx.callback_unassign(CBnum-1); - Settings.knx_CB_param[CBnum-1] = 0; - - if (CBnum == 1) - { - - src_offset = 1; - - - - len = (Settings.knx_CB_registered - 1); - } - else if (CBnum == Settings.knx_CB_registered) - { - - } - else - { - - - - - dest_offset = CBnum -1 ; - src_offset = dest_offset + 1; - len = (Settings.knx_CB_registered - CBnum); - } - - if (len > 0) - { - memmove(Settings.knx_CB_param + dest_offset, Settings.knx_CB_param + src_offset, len * sizeof(uint8_t)); - memmove(Settings.knx_CB_addr + dest_offset, Settings.knx_CB_addr + src_offset, len * sizeof(uint16_t)); - } - - Settings.knx_CB_registered--; - - - if ( KNX_CB_Search( oldparam ) == KNX_Empty ) { - knx.callback_deregister( device_param[oldparam-1].CB_id ); - device_param[oldparam-1].CB_id = KNX_Empty; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " CB #%d"), CBnum ); -} - - -bool KNX_CONFIG_NOT_MATCH(void) -{ - - for (uint32_t i = 0; i < KNX_MAX_device_param; ++i) - { - if ( !device_param[i].show ) { - - - - if ( KNX_GA_Search(i+1) != KNX_Empty ) { return true; } - - if ( i < 8 ) - { - if ( KNX_CB_Search(i+1) != KNX_Empty ) { return true; } - if ( KNX_CB_Search(i+9) != KNX_Empty ) { return true; } - } - - if ( i > 15 ) - { - if ( KNX_CB_Search(i+1) != KNX_Empty ) { return true; } - } - } - } - - - for (uint32_t i = 0; i < Settings.knx_GA_registered; ++i) - { - if ( Settings.knx_GA_param[i] != 0 ) - { - if ( Settings.knx_GA_addr[i] == 0 ) - { - return true; - } - } - } - for (uint32_t i = 0; i < Settings.knx_CB_registered; ++i) - { - if ( Settings.knx_CB_param[i] != 0 ) - { - if ( Settings.knx_CB_addr[i] == 0 ) - { - return true; - } - } - } - - return false; -} - - -void KNXStart(void) -{ - knx.start(nullptr); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_START)); -} - - -void KNX_INIT(void) -{ - - if (Settings.knx_GA_registered > MAX_KNX_GA) { Settings.knx_GA_registered = MAX_KNX_GA; } - if (Settings.knx_CB_registered > MAX_KNX_CB) { Settings.knx_CB_registered = MAX_KNX_CB; } - - - KNX_physs_addr.value = Settings.knx_physsical_addr; - knx.physical_address_set( KNX_physs_addr ); -# 472 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_11_knx.ino" - for (uint32_t i = 0; i < devices_present; ++i) - { - device_param[i].show = true; - } - for (uint32_t i = GPIO_SWT1; i < GPIO_SWT4 + 1; ++i) - { - if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1 + 8].show = true; } - } - for (uint32_t i = GPIO_KEY1; i < GPIO_KEY4 + 1; ++i) - { - if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1 + 8].show = true; } - } - for (uint32_t i = GPIO_SWT1_NP; i < GPIO_SWT4_NP + 1; ++i) - { - if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_SWT1_NP + 8].show = true; } - } - for (uint32_t i = GPIO_KEY1_NP; i < GPIO_KEY4_NP + 1; ++i) - { - if (GetUsedInModule(i, my_module.io)) { device_param[i - GPIO_KEY1_NP + 8].show = true; } - } - if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } - if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } - if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } -#ifdef USE_DS18x20 - if (GetUsedInModule(GPIO_DSB, my_module.io)) { device_param[KNX_TEMPERATURE-1].show = true; } -#endif - if (GetUsedInModule(GPIO_DHT11, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } - if (GetUsedInModule(GPIO_DHT22, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } - if (GetUsedInModule(GPIO_SI7021, my_module.io)) { device_param[KNX_HUMIDITY-1].show = true; } - -#if defined(USE_ENERGY_SENSOR) - - if ( energy_flg != ENERGY_NONE ) { - device_param[KNX_ENERGY_POWER-1].show = true; - device_param[KNX_ENERGY_DAILY-1].show = true; - device_param[KNX_ENERGY_START-1].show = true; - device_param[KNX_ENERGY_TOTAL-1].show = true; - device_param[KNX_ENERGY_VOLTAGE-1].show = true; - device_param[KNX_ENERGY_CURRENT-1].show = true; - device_param[KNX_ENERGY_POWERFACTOR-1].show = true; - } -#endif - -#ifdef USE_RULES - device_param[KNX_SLOT1-1].show = true; - device_param[KNX_SLOT2-1].show = true; - device_param[KNX_SLOT3-1].show = true; - device_param[KNX_SLOT4-1].show = true; - device_param[KNX_SLOT5-1].show = true; -#endif - - - if (KNX_CONFIG_NOT_MATCH()) { - Settings.knx_GA_registered = 0; - Settings.knx_CB_registered = 0; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_DELETE " " D_KNX_PARAMETERS)); - } - - - - - uint8_t j; - for (uint32_t i = 0; i < Settings.knx_CB_registered; ++i) - { - j = Settings.knx_CB_param[i]; - if ( j > 0 ) - { - device_param[j-1].CB_id = knx.callback_register("", KNX_CB_Action, &device_param[j-1]); - - - - KNX_addr.value = Settings.knx_CB_addr[i]; - knx.callback_assign( device_param[j-1].CB_id, KNX_addr ); - } - } -} - - -void KNX_CB_Action(message_t const &msg, void *arg) -{ - device_parameters_t *chan = (device_parameters_t *)arg; - if (!(Settings.flag.knx_enabled)) { return; } - - char tempchar[33]; - - if (msg.data_len == 1) { - - tempchar[0] = msg.data[0]; - tempchar[1] = '\0'; - } else { - - float tempvar = knx.data_to_2byte_float(msg.data); - dtostrfd(tempvar,2,tempchar); - } - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX D_RECEIVED_FROM " %d.%d.%d " D_COMMAND " %s: %s " D_TO " %s"), - msg.received_on.ga.area, msg.received_on.ga.line, msg.received_on.ga.member, - (msg.ct == KNX_CT_WRITE) ? D_KNX_COMMAND_WRITE : (msg.ct == KNX_CT_READ) ? D_KNX_COMMAND_READ : D_KNX_COMMAND_OTHER, - tempchar, - device_param_cb[(chan->type)-1]); - - switch (msg.ct) - { - case KNX_CT_WRITE: - if (chan->type < 9) - { - ExecuteCommandPower(chan->type, msg.data[0], SRC_KNX); - } - else if (chan->type < 17) - { - if (!toggle_inhibit) { - ExecuteCommandPower((chan->type) -8, POWER_TOGGLE, SRC_KNX); - if (Settings.flag.knx_enable_enhancement) { - toggle_inhibit = TOGGLE_INHIBIT_TIME; - } - } - } -#ifdef USE_RULES - else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) - { - if (!toggle_inhibit) { - char command[25]; - if (msg.data_len == 1) { - - snprintf_P(command, sizeof(command), PSTR("event KNXRX_CMND%d=%d"), ((chan->type) - KNX_SLOT1 + 1 ), msg.data[0]); - } else { - - snprintf_P(command, sizeof(command), PSTR("event KNXRX_VAL%d=%s"), ((chan->type) - KNX_SLOT1 + 1 ), tempchar); - } - ExecuteCommand(command, SRC_KNX); - if (Settings.flag.knx_enable_enhancement) { - toggle_inhibit = TOGGLE_INHIBIT_TIME; - } - } - } -#endif - break; - - case KNX_CT_READ: - if (chan->type < 9) - { - knx.answer_1bit(msg.received_on, chan->last_state); - if (Settings.flag.knx_enable_enhancement) { - knx.answer_1bit(msg.received_on, chan->last_state); - knx.answer_1bit(msg.received_on, chan->last_state); - } - } - else if (chan->type == KNX_TEMPERATURE) - { - knx.answer_2byte_float(msg.received_on, last_temp); - if (Settings.flag.knx_enable_enhancement) { - knx.answer_2byte_float(msg.received_on, last_temp); - knx.answer_2byte_float(msg.received_on, last_temp); - } - } - else if (chan->type == KNX_HUMIDITY) - { - knx.answer_2byte_float(msg.received_on, last_hum); - if (Settings.flag.knx_enable_enhancement) { - knx.answer_2byte_float(msg.received_on, last_hum); - knx.answer_2byte_float(msg.received_on, last_hum); - } - } -#ifdef USE_RULES - else if ((chan->type >= KNX_SLOT1) && (chan->type <= KNX_SLOT5)) - { - if (!toggle_inhibit) { - char command[25]; - snprintf_P(command, sizeof(command), PSTR("event KNXRX_REQ%d"), ((chan->type) - KNX_SLOT1 + 1 ) ); - ExecuteCommand(command, SRC_KNX); - if (Settings.flag.knx_enable_enhancement) { - toggle_inhibit = TOGGLE_INHIBIT_TIME; - } - } - } -#endif - break; - } -} - - -void KnxUpdatePowerState(uint8_t device, power_t state) -{ - if (!(Settings.flag.knx_enabled)) { return; } - - device_param[device -1].last_state = bitRead(state, device -1); - - - uint8_t i = KNX_GA_Search(device); - while ( i != KNX_Empty ) { - KNX_addr.value = Settings.knx_GA_addr[i]; - knx.write_1bit(KNX_addr, device_param[device -1].last_state); - if (Settings.flag.knx_enable_enhancement) { - knx.write_1bit(KNX_addr, device_param[device -1].last_state); - knx.write_1bit(KNX_addr, device_param[device -1].last_state); - } - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), - device_param_ga[device -1], device_param[device -1].last_state, - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - - i = KNX_GA_Search(device, i + 1); - } -} - - -void KnxSendButtonPower(void) -{ - if (!(Settings.flag.knx_enabled)) { return; } - - uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; - uint32_t device = XdrvMailbox.payload & 0xFF; - uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; -# 693 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_11_knx.ino" - uint8_t i = KNX_GA_Search(device + 8); - while ( i != KNX_Empty ) { - KNX_addr.value = Settings.knx_GA_addr[i]; - knx.write_1bit(KNX_addr, !(state == 0)); - if (Settings.flag.knx_enable_enhancement) { - knx.write_1bit(KNX_addr, !(state == 0)); - knx.write_1bit(KNX_addr, !(state == 0)); - } - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), - device_param_ga[device + 7], !(state == 0), - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - - i = KNX_GA_Search(device + 8, i + 1); - } - -} - - -void KnxSensor(uint8_t sensor_type, float value) -{ - if (sensor_type == KNX_TEMPERATURE) - { - last_temp = value; - } else if (sensor_type == KNX_HUMIDITY) - { - last_hum = value; - } - - if (!(Settings.flag.knx_enabled)) { return; } - - uint8_t i = KNX_GA_Search(sensor_type); - while ( i != KNX_Empty ) { - KNX_addr.value = Settings.knx_GA_addr[i]; - knx.write_2byte_float(KNX_addr, value); - if (Settings.flag.knx_enable_enhancement) { - knx.write_2byte_float(KNX_addr, value); - knx.write_2byte_float(KNX_addr, value); - } - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s " D_SENT_TO " %d.%d.%d "), - device_param_ga[sensor_type -1], - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - - i = KNX_GA_Search(sensor_type, i+1); - } -} - - - - - - -#ifdef USE_WEBSERVER -#ifdef USE_KNX_WEB_MENU -const char S_CONFIGURE_KNX[] PROGMEM = D_CONFIGURE_KNX; - -const char HTTP_BTN_MENU_KNX[] PROGMEM = - "

"; - -const char HTTP_FORM_KNX[] PROGMEM = - "
" - " " D_KNX_PARAMETERS " " - "
" - "
" - "" D_KNX_PHYSICAL_ADDRESS " " - " . " - " . " - "" - "

" D_KNX_PHYSICAL_ADDRESS_NOTE "

" - "" D_KNX_ENABLE "" D_KNX_ENHANCEMENT "

" - - "
" - "" D_KNX_GROUP_ADDRESS_TO_WRITE "
" - - " / " - " / " - " "; - -const char HTTP_FORM_KNX_ADD_BTN[] PROGMEM = - "

" - ""; - -const char HTTP_FORM_KNX_ADD_TABLE_ROW[] PROGMEM = - "" - ""; - -const char HTTP_FORM_KNX3[] PROGMEM = - "
%s -> %d / %d / %d

" - "
" - "" D_KNX_GROUP_ADDRESS_TO_READ "
"; - -const char HTTP_FORM_KNX4[] PROGMEM = - "-> -> ")); - WSContentSend_P(HTTP_FORM_KNX_GA, "GA_FNUM", "GA_AREA", "GA_FDEF"); - WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "GAwarning", (Settings.knx_GA_registered < MAX_KNX_GA) ? "" : "disabled", 1); - for (uint32_t i = 0; i < Settings.knx_GA_registered ; ++i) - { - if ( Settings.knx_GA_param[i] ) - { - KNX_addr.value = Settings.knx_GA_addr[i]; - WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW, device_param_ga[Settings.knx_GA_param[i]-1], KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, i +1); - } - } - - WSContentSend_P(HTTP_FORM_KNX3); - WSContentSend_P(HTTP_FORM_KNX_GA, "CB_FNUM", "CB_AREA", "CB_FDEF"); - WSContentSend_P(HTTP_FORM_KNX4); - - uint8_t j; - for (uint32_t i = 0; i < KNX_MAX_device_param ; i++) - { - - if ( (i > 8) && (i < 16) ) { j=i-8; } else { j=i; } - if ( i == 8 ) { j = 0; } - if ( device_param[j].show ) - { - WSContentSend_P(HTTP_FORM_KNX_OPT, device_param[i].type, device_param_cb[i]); - } - } - WSContentSend_P(PSTR(" ")); - WSContentSend_P(HTTP_FORM_KNX_ADD_BTN, "CBwarning", (Settings.knx_CB_registered < MAX_KNX_CB) ? "" : "disabled", 2); - - for (uint32_t i = 0; i < Settings.knx_CB_registered ; ++i) - { - if ( Settings.knx_CB_param[i] ) - { - KNX_addr.value = Settings.knx_CB_addr[i]; - WSContentSend_P(HTTP_FORM_KNX_ADD_TABLE_ROW2, KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, device_param_cb[Settings.knx_CB_param[i]-1], i +1); - } - } - WSContentSend_P(PSTR("
")); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); - } - -} - - -void KNX_Save_Settings(void) -{ - String stmp; - address_t KNX_addr; - - Settings.flag.knx_enabled = WebServer->hasArg("b1"); - Settings.flag.knx_enable_enhancement = WebServer->hasArg("b2"); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_ENABLED ": %d, " D_KNX_ENHANCEMENT ": %d"), - Settings.flag.knx_enabled, Settings.flag.knx_enable_enhancement ); - - stmp = WebServer->arg("area"); - KNX_addr.pa.area = stmp.toInt(); - stmp = WebServer->arg("line"); - KNX_addr.pa.line = stmp.toInt(); - stmp = WebServer->arg("member"); - KNX_addr.pa.member = stmp.toInt(); - Settings.knx_physsical_addr = KNX_addr.value; - knx.physical_address_set( KNX_addr ); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX D_KNX_PHYSICAL_ADDRESS ": %d.%d.%d "), - KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA: %d"), - Settings.knx_GA_registered ); - for (uint32_t i = 0; i < Settings.knx_GA_registered ; ++i) - { - KNX_addr.value = Settings.knx_GA_addr[i]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "GA #%d: %s " D_TO " %d/%d/%d"), - i+1, device_param_ga[Settings.knx_GA_param[i]-1], - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); - - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB: %d"), - Settings.knx_CB_registered ); - for (uint32_t i = 0; i < Settings.knx_CB_registered ; ++i) - { - KNX_addr.value = Settings.knx_CB_addr[i]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_KNX "CB #%d: %d/%d/%d " D_TO " %s"), - i+1, - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member, - device_param_cb[Settings.knx_CB_param[i]-1] ); - } -} - -#endif -#endif - - - - - -void CmndKnxTxCmnd(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled) { - - - - uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); - while ( i != KNX_Empty ) { - KNX_addr.value = Settings.knx_GA_addr[i]; - knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); - if (Settings.flag.knx_enable_enhancement) { - knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); - knx.write_1bit(KNX_addr, !(XdrvMailbox.payload == 0)); - } - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %d " D_SENT_TO " %d.%d.%d"), - device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], !(XdrvMailbox.payload == 0), - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - - i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); - } - ResponseCmndIdxChar (XdrvMailbox.data ); - } -} - -void CmndKnxTxVal(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNXTX_CMNDS) && (XdrvMailbox.data_len > 0) && Settings.flag.knx_enabled) { - - - - uint8_t i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1); - while ( i != KNX_Empty ) { - KNX_addr.value = Settings.knx_GA_addr[i]; - - float tempvar = CharToFloat(XdrvMailbox.data); - dtostrfd(tempvar,2,XdrvMailbox.data); - - knx.write_2byte_float(KNX_addr, tempvar); - if (Settings.flag.knx_enable_enhancement) { - knx.write_2byte_float(KNX_addr, tempvar); - knx.write_2byte_float(KNX_addr, tempvar); - } - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_KNX "%s = %s " D_SENT_TO " %d.%d.%d"), - device_param_ga[XdrvMailbox.index + KNX_SLOT1 -2], XdrvMailbox.data, - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member); - - i = KNX_GA_Search(XdrvMailbox.index + KNX_SLOT1 -1, i + 1); - } - ResponseCmndIdxChar (XdrvMailbox.data ); - } -} - -void CmndKnxEnabled(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - Settings.flag.knx_enabled = XdrvMailbox.payload; - } - ResponseCmndChar (GetStateText(Settings.flag.knx_enabled) ); -} - -void CmndKnxEnhanced(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { - Settings.flag.knx_enable_enhancement = XdrvMailbox.payload; - } - ResponseCmndChar (GetStateText(Settings.flag.knx_enable_enhancement) ); -} - -void CmndKnxPa(void) -{ - if (XdrvMailbox.data_len) { - if (strstr(XdrvMailbox.data, ".") != nullptr) { - char sub_string[XdrvMailbox.data_len]; - - int pa_area = atoi(subStr(sub_string, XdrvMailbox.data, ".", 1)); - int pa_line = atoi(subStr(sub_string, XdrvMailbox.data, ".", 2)); - int pa_member = atoi(subStr(sub_string, XdrvMailbox.data, ".", 3)); - - if ( ((pa_area == 0) && (pa_line == 0) && (pa_member == 0)) - || (pa_area > 15) || (pa_line > 15) || (pa_member > 255) ) { - Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); - return; - } - - KNX_addr.pa.area = pa_area; - KNX_addr.pa.line = pa_line; - KNX_addr.pa.member = pa_member; - Settings.knx_physsical_addr = KNX_addr.value; - } - } - KNX_addr.value = Settings.knx_physsical_addr; - Response_P (PSTR("{\"%s\":\"%d.%d.%d\"}"), - XdrvMailbox.command, KNX_addr.pa.area, KNX_addr.pa.line, KNX_addr.pa.member ); -} - -void CmndKnxGa(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_GA)) { - if (XdrvMailbox.data_len) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { - char sub_string[XdrvMailbox.data_len]; - - int ga_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); - int ga_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - int ga_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - int ga_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); - - if ( ((ga_area == 0) && (ga_line == 0) && (ga_member == 0)) - || (ga_area > 31) || (ga_line > 7) || (ga_member > 255) - || (ga_option < 0) || ((ga_option > KNX_MAX_device_param ) && (ga_option != KNX_Empty)) - || (!device_param[ga_option-1].show) ) { - Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); - return; - } - - KNX_addr.ga.area = ga_area; - KNX_addr.ga.line = ga_line; - KNX_addr.ga.member = ga_member; - - if ( XdrvMailbox.index > Settings.knx_GA_registered ) { - Settings.knx_GA_registered ++; - XdrvMailbox.index = Settings.knx_GA_registered; - } - - Settings.knx_GA_addr[XdrvMailbox.index -1] = KNX_addr.value; - Settings.knx_GA_param[XdrvMailbox.index -1] = ga_option; - } else { - if ( (XdrvMailbox.payload <= Settings.knx_GA_registered) && (XdrvMailbox.payload > 0) ) { - XdrvMailbox.index = XdrvMailbox.payload; - } else { - Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); - return; - } - } - if ( XdrvMailbox.index <= Settings.knx_GA_registered ) { - KNX_addr.value = Settings.knx_GA_addr[XdrvMailbox.index -1]; - Response_P (PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), - XdrvMailbox.command, XdrvMailbox.index, device_param_ga[Settings.knx_GA_param[XdrvMailbox.index-1]-1], - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); - } - } else { - ResponseCmndNumber (Settings.knx_GA_registered ); - } - } -} - -void CmndKnxCb(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_KNX_CB)) { - if (XdrvMailbox.data_len) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { - char sub_string[XdrvMailbox.data_len]; - - int cb_option = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); - int cb_area = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - int cb_line = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - int cb_member = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); - - if ( ((cb_area == 0) && (cb_line == 0) && (cb_member == 0)) - || (cb_area > 31) || (cb_line > 7) || (cb_member > 255) - || (cb_option < 0) || ((cb_option > KNX_MAX_device_param ) && (cb_option != KNX_Empty)) - || (!device_param[cb_option-1].show) ) { - Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); - return; - } - - KNX_addr.ga.area = cb_area; - KNX_addr.ga.line = cb_line; - KNX_addr.ga.member = cb_member; - - if ( XdrvMailbox.index > Settings.knx_CB_registered ) { - Settings.knx_CB_registered ++; - XdrvMailbox.index = Settings.knx_CB_registered; - } - - Settings.knx_CB_addr[XdrvMailbox.index -1] = KNX_addr.value; - Settings.knx_CB_param[XdrvMailbox.index -1] = cb_option; - } else { - if ( (XdrvMailbox.payload <= Settings.knx_CB_registered) && (XdrvMailbox.payload > 0) ) { - XdrvMailbox.index = XdrvMailbox.payload; - } else { - Response_P (PSTR("{\"%s\":\"" D_ERROR "\"}"), XdrvMailbox.command ); - return; - } - } - if ( XdrvMailbox.index <= Settings.knx_CB_registered ) { - KNX_addr.value = Settings.knx_CB_addr[XdrvMailbox.index -1]; - Response_P (PSTR("{\"%s%d\":\"%s, %d/%d/%d\"}"), - XdrvMailbox.command, XdrvMailbox.index, device_param_cb[Settings.knx_CB_param[XdrvMailbox.index-1]-1], - KNX_addr.ga.area, KNX_addr.ga.line, KNX_addr.ga.member ); - } - } else { - ResponseCmndNumber (Settings.knx_CB_registered ); - } - } -} - - - - - -bool Xdrv11(uint8_t function) -{ - bool result = false; - switch (function) { - case FUNC_LOOP: - if (!global_state.wifi_down) { knx.loop(); } - break; - case FUNC_EVERY_50_MSECOND: - if (toggle_inhibit) { - toggle_inhibit--; - } - break; - case FUNC_ANY_KEY: - KnxSendButtonPower(); - break; -#ifdef USE_WEBSERVER -#ifdef USE_KNX_WEB_MENU - case FUNC_WEB_ADD_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_KNX); - break; - case FUNC_WEB_ADD_HANDLER: - WebServer->on("/kn", HandleKNXConfiguration); - break; -#endif -#endif - case FUNC_COMMAND: - result = DecodeCommand(kKnxCommands, KnxCommand); - break; - case FUNC_PRE_INIT: - KNX_INIT(); - break; - - - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_12_home_assistant.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_12_home_assistant.ino" -#ifdef USE_HOME_ASSISTANT - -#define XDRV_12 12 - - -const char kHAssJsonSensorTypes[] PROGMEM = - D_JSON_TEMPERATURE "|" D_JSON_PRESSURE "|" D_JSON_PRESSUREATSEALEVEL "|" - D_JSON_APPARENT_POWERUSAGE "|Battery|" D_JSON_CURRENT "|" D_JSON_DISTANCE "|" D_JSON_FREQUENCY "|" D_JSON_HUMIDITY "|" D_JSON_ILLUMINANCE "|" - D_JSON_MOISTURE "|PB0.3|PB0.5|PB1|PB2.5|PB5|PB10|PM1|PM2.5|PM10|" D_JSON_POWERFACTOR "|" D_JSON_POWERUSAGE "|" - D_JSON_REACTIVE_POWERUSAGE "|" D_JSON_TODAY "|" D_JSON_TOTAL "|" D_JSON_VOLTAGE "|" D_JSON_WEIGHT "|" D_JSON_YESTERDAY; -const char kHAssJsonSensorUnits[] PROGMEM = - "|||" - "W|%|A|Cm|Hz|%|LX|" - "%|ppd|ppd|ppd|ppd|ppd|ppd|µg/m³|µg/m³|µg/m³||W|" - "W|KWh|KWh|V|Kg|KWh"; -const char kHAssJsonSensorDevCla[] PROGMEM = - "dev_cla\":\"temperature|dev_cla\":\"pressure|dev_cla\":\"pressure|" - "dev_cla\":\"power|dev_cla\":\"battery|ic\":\"mdi:alpha-a-circle-outline|ic\":\"mdi:leak|ic\":\"mdi:current-ac|dev_cla\":\"humidity|dev_cla\":\"illuminance|" - "ic\":\"mdi:cup-water|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|" - "ic\":\"mdi:air-filter|ic\":\"mdi:air-filter|ic\":\"mdi:air-filter|ic\":\"mdi:alpha-f-circle-outline|dev_cla\":\"power|" - "dev_cla\":\"power|dev_cla\":\"power|dev_cla\":\"power|ic\":\"mdi:alpha-v-circle-outline|ic\":\"mdi:scale|dev_cla\":\"power"; - - -const char HASS_DISCOVER_SENSOR[] PROGMEM = - ",\"unit_of_meas\":\"%s\",\"%s\"," - "\"frc_upd\":true," - "\"val_tpl\":\"{{value_json['%s']['%s']"; - -const char HASS_DISCOVER_BASE[] PROGMEM = - "{\"name\":\"%s\"," - "\"stat_t\":\"%s\"," - "\"avty_t\":\"%s\"," - "\"pl_avail\":\"" D_ONLINE "\"," - "\"pl_not_avail\":\"" D_OFFLINE "\""; - -const char HASS_DISCOVER_RELAY[] PROGMEM = - ",\"cmd_t\":\"%s\"," - "\"val_tpl\":\"{{value_json.%s}}\"," - "\"pl_off\":\"%s\"," - "\"pl_on\":\"%s\""; - -const char HASS_DISCOVER_BUTTON_TOGGLE[] PROGMEM = - ",\"value_template\":\"{%%if is_state(entity_id,\\\"off\\\")-%%}ON{%%-endif%%}\"," - "\"off_delay\":1"; - -const char HASS_DISCOVER_BUTTON_SWITCH_ONOFF[] PROGMEM = - ",\"value_template\":\"{{value_json.%s}}\"," - "\"frc_upd\":true," - "\"pl_on\":\"%s\"," - "\"pl_off\":\"%s\""; - -const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM = - ",\"bri_cmd_t\":\"%s\"," - "\"bri_stat_t\":\"%s\"," - "\"bri_scl\":100," - "\"on_cmd_type\":\"%s\"," - "\"bri_val_tpl\":\"{{value_json." D_CMND_DIMMER "}}\""; - -const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM = - ",\"rgb_cmd_t\":\"%s2\"," - "\"rgb_stat_t\":\"%s\"," - "\"rgb_val_tpl\":\"{{value_json." D_CMND_COLOR ".split(',')[0:3]|join(',')}}\""; - -const char HASS_DISCOVER_LIGHT_WHITE[] PROGMEM = - ",\"whit_val_cmd_t\":\"%s\"," - "\"whit_val_stat_t\":\"%s\"," - "\"white_value_scale\":100," - "\"whit_val_tpl\":\"{{value_json.Channel[3]}}\""; - -const char HASS_DISCOVER_LIGHT_CT[] PROGMEM = - ",\"clr_temp_cmd_t\":\"%s\"," - "\"clr_temp_stat_t\":\"%s\"," - "\"clr_temp_val_tpl\":\"{{value_json." D_CMND_COLORTEMPERATURE "}}\""; - -const char HASS_DISCOVER_LIGHT_SCHEME[] PROGMEM = - ",\"fx_cmd_t\":\"%s\"," - "\"fx_stat_t\":\"%s\"," - "\"fx_val_tpl\":\"{{value_json." D_CMND_SCHEME "}}\"," - "\"fx_list\":[\"0\",\"1\",\"2\",\"3\",\"4\"]"; - -const char HASS_DISCOVER_SENSOR_HASS_STATUS[] PROGMEM = - ",\"json_attributes_topic\":\"%s\"," - "\"unit_of_meas\":\" \"," - "\"val_tpl\":\"{{value_json['" D_JSON_RSSI "']}}\"," - "\"ic\":\"mdi:information-outline\""; - -const char HASS_DISCOVER_DEVICE_INFO[] PROGMEM = - ",\"uniq_id\":\"%s\"," - "\"device\":{\"identifiers\":[\"%06X\"]," - "\"connections\":[[\"mac\",\"%s\"]]," - "\"name\":\"%s\"," - "\"model\":\"%s\"," - "\"sw_version\":\"%s%s\"," - "\"manufacturer\":\"Tasmota\"}"; - -const char HASS_DISCOVER_DEVICE_INFO_SHORT[] PROGMEM = - ",\"uniq_id\":\"%s\"," - "\"device\":{\"identifiers\":[\"%06X\"]," - "\"connections\":[[\"mac\",\"%s\"]]}"; - -uint8_t hass_init_step = 0; -uint8_t hass_mode = 0; -int hass_tele_period = 0; - -void TryResponseAppend_P(const char *format, ...) -{ - va_list args; - va_start(args, format); - char dummy[2]; - int dlen = vsnprintf_P(dummy, 1, format, args); - - int mlen = strlen(mqtt_data); - int slen = sizeof(mqtt_data) - 1 - mlen; - if (dlen >= slen) - { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: MQTT discovery failed due to too long topic or friendly name. " - "Please shorten topic and friendly name. Failed to format(%u/%u):"), - dlen, slen); - va_start(args, format); - vsnprintf_P(log_data, sizeof(log_data), format, args); - AddLog(LOG_LEVEL_ERROR); - } - else - { - va_start(args, format); - vsnprintf_P(mqtt_data + mlen, slen, format, args); - } - va_end(args); -} - -void HAssAnnounceRelayLight(void) -{ - char stopic[TOPSZ]; - char stemp1[TOPSZ]; - char stemp2[TOPSZ]; - char stemp3[TOPSZ]; - char unique_id[30]; - bool is_light = false; - bool is_topic_light = false; - - for (uint32_t i = 1; i <= MAX_RELAYS; i++) - { - is_light = ((i == devices_present) && (light_type)); - is_topic_light = Settings.flag.hass_light || is_light; - - mqtt_data[0] = '\0'; - - - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP.getChipId(), (is_topic_light) ? "RL" : "LI", i); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), - (is_topic_light) ? "switch" : "light", unique_id); - MqttPublish(stopic, true); - - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP.getChipId(), (is_topic_light) ? "LI" : "RL", i); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/%s/%s/config"), - (is_topic_light) ? "light" : "switch", unique_id); - - if (Settings.flag.hass_discovery && (i <= devices_present)) - { - char name[33 + 2]; - char value_template[33]; - char prefix[TOPSZ]; - char *command_topic = stemp1; - char *state_topic = stemp2; - char *availability_topic = stemp3; - - if (i > MAX_FRIENDLYNAMES) - { - snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i); - } - else - { - snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 + i - 1)); - } - GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); - GetTopic_P(command_topic, CMND, mqtt_topic, value_template); - GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE); - GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - - Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); - TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2)); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); - -#ifdef USE_LIGHT - if (is_light) - { - char *brightness_command_topic = stemp1; - - GetTopic_P(brightness_command_topic, CMND, mqtt_topic, D_CMND_DIMMER); - strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_DIMMER, brightness_command_topic, state_topic, stemp3); - - if (Light.subtype >= LST_RGB) - { - char *rgb_command_topic = stemp1; - - GetTopic_P(rgb_command_topic, CMND, mqtt_topic, D_CMND_COLOR); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_COLOR, rgb_command_topic, state_topic); - - char *effect_command_topic = stemp1; - GetTopic_P(effect_command_topic, CMND, mqtt_topic, D_CMND_SCHEME); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_SCHEME, effect_command_topic, state_topic); - } - if (LST_RGBW == Light.subtype) - { - char *white_temp_command_topic = stemp1; - - GetTopic_P(white_temp_command_topic, CMND, mqtt_topic, D_CMND_WHITE); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_WHITE, white_temp_command_topic, state_topic); - } - if ((LST_COLDWARM == Light.subtype) || (LST_RGBCW == Light.subtype)) - { - char *color_temp_command_topic = stemp1; - - GetTopic_P(color_temp_command_topic, CMND, mqtt_topic, D_CMND_COLORTEMPERATURE); - TryResponseAppend_P(HASS_DISCOVER_LIGHT_CT, color_temp_command_topic, state_topic); - } - } -#endif - TryResponseAppend_P(PSTR("}")); - } - MqttPublish(stopic, true); - } -} - -void HAssAnnounceButtonSwitch(uint8_t device, char *topic, uint8_t present, uint8_t key, uint8_t toggle) -{ - - - char stopic[TOPSZ]; - char stemp1[TOPSZ]; - char stemp2[TOPSZ]; - char unique_id[30]; - - mqtt_data[0] = '\0'; - - - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%d"), ESP.getChipId(), key ? "SW" : "BTN", device + 1); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/binary_sensor/%s/config"), unique_id); - - if (Settings.flag.hass_discovery && present) - { - char name[33 + 6]; - char value_template[33]; - char prefix[TOPSZ]; - char *state_topic = stemp1; - char *availability_topic = stemp2; - char jsoname[8]; - - snprintf_P(name, sizeof(name), PSTR("%s %s%d"), SettingsText(SET_FRIENDLYNAME1), key ? "Switch" : "Button", device + 1); - snprintf_P(jsoname, sizeof(jsoname), PSTR("%s%d"), key ? "SWITCH" : "BUTTON", device + 1); - GetPowerDevice(value_template, device + 1, sizeof(value_template), - key + Settings.flag.device_index_enable); - GetTopic_P(state_topic, STAT, mqtt_topic, (PSTR("/'%s'"), jsoname)); - GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - - Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); - - if (toggle) { - if (!key) { - TryResponseAppend_P(HASS_DISCOVER_BUTTON_TOGGLE); - } else { - TryResponseAppend_P(",\"value_template\":\"{%%if is_state(entity_id,\\\"on\\\")-%%}OFF{%%-else-%%}ON{%%-endif%%}\""); - } - } else { - TryResponseAppend_P(HASS_DISCOVER_BUTTON_SWITCH_ONOFF, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2), SettingsText(SET_STATE_TXT1)); - } - TryResponseAppend_P(PSTR("}")); - } - MqttPublish(stopic, true); -} - -void HAssAnnounceSwitches(void) -{ - char sw_topic[TOPSZ]; - - - char *tmp = SettingsText(SET_MQTT_SWITCH_TOPIC); - Format(sw_topic, tmp, sizeof(sw_topic)); - if (!strcmp_P(sw_topic, "0") || strlen(sw_topic) == 0) - { - for (uint32_t switch_index = 0; switch_index < MAX_SWITCHES; switch_index++) - { - uint8_t switch_present = 0; - uint8_t toggle = 1; - - if (pin[GPIO_SWT1 + switch_index] < 99) - { - switch_present = 1; - } - - - if (Settings.switchmode[switch_index] == FOLLOW || Settings.switchmode[switch_index] == FOLLOW_INV || - Settings.flag3.button_switch_force_local || - !strcmp(mqtt_topic, sw_topic) || !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), sw_topic)) - { - toggle = 0; - } - HAssAnnounceButtonSwitch(switch_index, sw_topic, switch_present, 1, toggle); - } - } -} - -void HAssAnnounceButtons(void) -{ - char key_topic[TOPSZ]; - - - char *tmp = SettingsText(SET_MQTT_BUTTON_TOPIC); - Format(key_topic, tmp, sizeof(key_topic)); - if (!strcmp_P(key_topic, "0") || strlen(key_topic) == 0) - { - for (uint32_t button_index = 0; button_index < MAX_KEYS; button_index++) - { - uint8_t button_present = 0; - uint8_t toggle = 1; - - if (!button_index && ((SONOFF_DUAL == my_module_type) || (CH4 == my_module_type))) - { - button_present = 1; - } - else - { - if (pin[GPIO_KEY1 + button_index] < 99) - { - button_present = 1; - } - } - - - if (Settings.flag3.button_switch_force_local || - !strcmp(mqtt_topic, key_topic) || !strcmp(SettingsText(SET_MQTT_GRP_TOPIC), key_topic)) - { - toggle = 0; - } - HAssAnnounceButtonSwitch(button_index, key_topic, button_present, 0, toggle); - } - } -} - -void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char *MultiSubName, uint8_t subqty, uint8_t subidx) -{ - char stopic[TOPSZ]; - char stemp1[TOPSZ]; - char stemp2[TOPSZ]; - char unique_id[30]; - - mqtt_data[0] = '\0'; - - char subname[20]; - NoAlNumToUnderscore(subname, MultiSubName); - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_%s_%s"), ESP.getChipId(), sensorname, subname); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id);; - - if (Settings.flag.hass_discovery) - { - char name[33 + 42]; - char prefix[TOPSZ]; - char *state_topic = stemp1; - char *availability_topic = stemp2; - - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); - GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR)); - snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_FRIENDLYNAME1), sensorname, MultiSubName); - GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - - Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP.getChipId(), WiFi.macAddress().c_str()); - - char jname[32]; - int sensor_index = GetCommandCode(jname, sizeof(jname), subsensortype, kHAssJsonSensorTypes); - if (sensor_index > -1) { - char param1[20]; - GetTextIndexed(param1, sizeof(param1), sensor_index, kHAssJsonSensorUnits); - switch (sensor_index) { - case 0: - snprintf_P(param1, sizeof(param1), PSTR("°%c"),TempUnit()); - break; - case 1: - case 2: - snprintf_P(param1, sizeof(param1), PSTR("%s"), PressureUnit().c_str()); - break; - } - char param2[50]; - GetTextIndexed(param2, sizeof(param2), sensor_index, kHAssJsonSensorDevCla); - TryResponseAppend_P(HASS_DISCOVER_SENSOR, param1, param2, sensorname, subsensortype); - if (subidx) { - TryResponseAppend_P(PSTR("[%d]"), subqty -1); - } - } else { - TryResponseAppend_P(HASS_DISCOVER_SENSOR, " ", "ic\":\"mdi:eye", sensorname, subsensortype); - } - TryResponseAppend_P(PSTR("}}\"}")); - } - MqttPublish(stopic, true); -} - -void HAssAnnounceSensors(void) -{ - uint8_t hass_xsns_index = 0; - bool is_sensor = true; - uint8_t subqty = 0; - do - { - mqtt_data[0] = '\0'; - int tele_period_save = tele_period; - tele_period = 2; - XsnsNextCall(FUNC_JSON_APPEND, hass_xsns_index); - tele_period = tele_period_save; - - char sensordata[512]; - strlcpy(sensordata, mqtt_data, sizeof(sensordata)); - - if (strlen(sensordata)) - { - sensordata[0] = '{'; - snprintf_P(sensordata, sizeof(sensordata), PSTR("%s}"), sensordata); - - - - StaticJsonBuffer<500> jsonBuffer; - JsonObject &root = jsonBuffer.parseObject(sensordata); - if (!root.success()) - { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: jsonBuffer failed to parse '%s'"), sensordata); - continue; - } - for (auto sensor : root) - { - const char *sensorname = sensor.key; - JsonObject &sensors = sensor.value.as(); - if (!sensors.success()) - { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("HASS: JsonObject failed to parse '%s'"), sensordata); - continue; - } - for (auto subsensor : sensors) - { - - if (subsensor.value.is()) { - JsonArray& subsensors = subsensor.value.as(); - subqty = subsensors.size(); - char MultiSubName[20]; - for (int i = 1; i <= subqty; i++) { - snprintf_P(MultiSubName, sizeof(MultiSubName), PSTR("%s %d"), subsensor.key, i); - HAssAnnounceSensor(sensorname, subsensor.key, MultiSubName, i, 1); - } - } else { HAssAnnounceSensor(sensorname, subsensor.key, subsensor.key, 0, 0);} - } - } - } - yield(); - } while (hass_xsns_index != 0); -} - -void HAssAnnounceStatusSensor(void) -{ - char stopic[TOPSZ]; - char stemp1[TOPSZ]; - char stemp2[TOPSZ]; - char unique_id[30]; - - - mqtt_data[0] = '\0'; - - - snprintf_P(unique_id, sizeof(unique_id), PSTR("%06X_status"), ESP.getChipId()); - snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/sensor/%s/config"), unique_id); - - if (Settings.flag.hass_discovery) - { - char name[33 + 7]; - char prefix[TOPSZ]; - char *state_topic = stemp1; - char *availability_topic = stemp2; - - snprintf_P(name, sizeof(name), PSTR("%s status"), SettingsText(SET_FRIENDLYNAME1)); - GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE)); - GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - - Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); - TryResponseAppend_P(HASS_DISCOVER_SENSOR_HASS_STATUS, state_topic); - TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP.getChipId(), WiFi.macAddress().c_str(), - SettingsText(SET_FRIENDLYNAME1), ModuleName().c_str(), my_version, my_image); - - TryResponseAppend_P(PSTR("}")); - } - MqttPublish(stopic, true); -} - -void HAssPublishStatus(void) -{ - Response_P(PSTR("{\"" D_JSON_VERSION "\":\"%s%s\",\"" D_JSON_BUILDDATETIME "\":\"%s\"," - "\"" D_JSON_COREVERSION "\":\"" ARDUINO_ESP8266_RELEASE "\",\"" D_JSON_SDKVERSION "\":\"%s\"," - "\"" D_CMND_MODULE "\":\"%s\",\"" D_JSON_RESTARTREASON "\":\"%s\",\"" D_JSON_UPTIME "\":\"%s\"," - "\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d," - "\"" D_JSON_BOOTCOUNT "\":%d,\"" D_JSON_SAVECOUNT "\":%d,\"" D_CMND_IPADDRESS "\":\"%s\"," - "\"" D_JSON_RSSI "\":\"%d\",\"LoadAvg\":%lu}"), - my_version, my_image, GetBuildDateAndTime().c_str(), ESP.getSdkVersion(), ModuleName().c_str(), - GetResetReason().c_str(), GetUptime().c_str(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(), - Settings.bootcount, Settings.save_flag, WiFi.localIP().toString().c_str(), - WifiGetRssiAsQuality(WiFi.RSSI()), loop_load_avg); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE)); -} - -void HAssDiscovery(void) -{ - - if (Settings.flag.hass_discovery) - { - Settings.flag.mqtt_response = 0; - Settings.flag.decimal_text = 1; - Settings.flag3.hass_tele_on_power = 1; - Settings.light_scheme = 0; - } - - if (Settings.flag.hass_discovery || (1 == hass_mode)) - { - - HAssAnnounceRelayLight(); - - - HAssAnnounceButtons(); - - - HAssAnnounceSwitches(); - - - HAssAnnounceSensors(); - - - HAssAnnounceStatusSensor(); - } -} - -void HAssDiscover(void) -{ - hass_mode = 1; - hass_init_step = 1; -} - -void HAssAnyKey(void) -{ - if (!Settings.flag.hass_discovery) - { - return; - } - - uint32_t key = (XdrvMailbox.payload >> 16) & 0xFF; - uint32_t device = XdrvMailbox.payload & 0xFF; - uint32_t state = (XdrvMailbox.payload >> 8) & 0xFF; - - char scommand[CMDSZ]; - snprintf_P(scommand, sizeof(scommand), PSTR("%s%d"), (key) ? "SWITCH" : "BUTTON", device); - char stopic[TOPSZ]; - - GetTopic_P(stopic, STAT, mqtt_topic, scommand); - Response_P(S_JSON_COMMAND_SVALUE, PSTR(D_RSLT_STATE), GetStateText(state)); - MqttPublish(stopic); -} - - - - - -bool Xdrv12(uint8_t function) -{ - bool result = false; - - if (Settings.flag.mqtt_enabled) - { - switch (function) - { - case FUNC_EVERY_SECOND: - if (hass_init_step) - { - hass_init_step--; - if (!hass_init_step) - { - HAssDiscovery(); - } - } - else if (Settings.flag.hass_discovery && Settings.tele_period) - { - hass_tele_period++; - if (hass_tele_period >= Settings.tele_period) - { - hass_tele_period = 0; - - mqtt_data[0] = '\0'; - HAssPublishStatus(); - } - } - break; - case FUNC_ANY_KEY: - HAssAnyKey(); - break; - case FUNC_MQTT_INIT: - hass_mode = 0; - hass_init_step = 2; - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_13_display.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_13_display.ino" -#if defined(USE_I2C) || defined(USE_SPI) -#ifdef USE_DISPLAY - -#define XDRV_13 13 - -#include -#include - -Renderer *renderer; - -enum ColorType { COLOR_BW, COLOR_COLOR }; - -#ifndef MAXBUTTONS -#define MAXBUTTONS 16 -#endif - -#ifdef USE_TOUCH_BUTTONS -VButton *buttons[MAXBUTTONS]; -#endif - - - -uint16_t fg_color = 1; -uint16_t bg_color = 0; -uint8_t color_type = COLOR_BW; -uint8_t auto_draw=1; - -const uint8_t DISPLAY_MAX_DRIVERS = 16; -const uint8_t DISPLAY_MAX_COLS = 44; -const uint8_t DISPLAY_MAX_ROWS = 32; - -const uint8_t DISPLAY_LOG_ROWS = 32; - -#define D_PRFX_DISPLAY "Display" -#define D_CMND_DISP_ADDRESS "Address" -#define D_CMND_DISP_COLS "Cols" -#define D_CMND_DISP_DIMMER "Dimmer" -#define D_CMND_DISP_MODE "Mode" -#define D_CMND_DISP_MODEL "Model" -#define D_CMND_DISP_REFRESH "Refresh" -#define D_CMND_DISP_ROWS "Rows" -#define D_CMND_DISP_SIZE "Size" -#define D_CMND_DISP_FONT "Font" -#define D_CMND_DISP_ROTATE "Rotate" -#define D_CMND_DISP_TEXT "Text" -#define D_CMND_DISP_WIDTH "Width" -#define D_CMND_DISP_HEIGHT "Height" - -enum XdspFunctions { FUNC_DISPLAY_INIT_DRIVER, FUNC_DISPLAY_INIT, FUNC_DISPLAY_EVERY_50_MSECOND, FUNC_DISPLAY_EVERY_SECOND, - FUNC_DISPLAY_MODEL, FUNC_DISPLAY_MODE, FUNC_DISPLAY_POWER, - FUNC_DISPLAY_CLEAR, FUNC_DISPLAY_DRAW_FRAME, - FUNC_DISPLAY_DRAW_HLINE, FUNC_DISPLAY_DRAW_VLINE, FUNC_DISPLAY_DRAW_LINE, - FUNC_DISPLAY_DRAW_CIRCLE, FUNC_DISPLAY_FILL_CIRCLE, - FUNC_DISPLAY_DRAW_RECTANGLE, FUNC_DISPLAY_FILL_RECTANGLE, - FUNC_DISPLAY_TEXT_SIZE, FUNC_DISPLAY_FONT_SIZE, FUNC_DISPLAY_ROTATION, FUNC_DISPLAY_DRAW_STRING, FUNC_DISPLAY_ONOFF }; - -enum DisplayInitModes { DISPLAY_INIT_MODE, DISPLAY_INIT_PARTIAL, DISPLAY_INIT_FULL }; - -const char kDisplayCommands[] PROGMEM = D_PRFX_DISPLAY "|" - "|" D_CMND_DISP_MODEL "|" D_CMND_DISP_WIDTH "|" D_CMND_DISP_HEIGHT "|" D_CMND_DISP_MODE "|" D_CMND_DISP_REFRESH "|" - D_CMND_DISP_DIMMER "|" D_CMND_DISP_COLS "|" D_CMND_DISP_ROWS "|" D_CMND_DISP_SIZE "|" D_CMND_DISP_FONT "|" - D_CMND_DISP_ROTATE "|" D_CMND_DISP_TEXT "|" D_CMND_DISP_ADDRESS ; - -void (* const DisplayCommand[])(void) PROGMEM = { - &CmndDisplay, &CmndDisplayModel, &CmndDisplayWidth, &CmndDisplayHeight, &CmndDisplayMode, &CmndDisplayRefresh, - &CmndDisplayDimmer, &CmndDisplayColumns, &CmndDisplayRows, &CmndDisplaySize, &CmndDisplayFont, - &CmndDisplayRotate, &CmndDisplayText, &CmndDisplayAddress }; - -char *dsp_str; - -uint16_t dsp_x; -uint16_t dsp_y; -uint16_t dsp_x2; -uint16_t dsp_y2; -uint16_t dsp_rad; -uint16_t dsp_color; -int16_t dsp_len; -int16_t disp_xpos = 0; -int16_t disp_ypos = 0; - -uint8_t disp_power = 0; -uint8_t disp_device = 0; -uint8_t disp_refresh = 1; -uint8_t disp_autodraw = 1; -uint8_t dsp_init; -uint8_t dsp_font; -uint8_t dsp_flag; -uint8_t dsp_on; - -#ifdef USE_DISPLAY_MODES1TO5 - -char **disp_log_buffer; -char **disp_screen_buffer; -char disp_temp[2]; -char disp_pres[5]; - -uint8_t disp_log_buffer_cols = 0; -uint8_t disp_log_buffer_idx = 0; -uint8_t disp_log_buffer_ptr = 0; -uint8_t disp_screen_buffer_cols = 0; -uint8_t disp_screen_buffer_rows = 0; -bool disp_subscribed = false; - -#endif - - - -void DisplayInit(uint8_t mode) -{ - if (renderer) { - renderer->DisplayInit(mode, Settings.display_size, Settings.display_rotate, Settings.display_font); - } - else { - dsp_init = mode; - XdspCall(FUNC_DISPLAY_INIT); - } -} - -void DisplayClear(void) -{ - XdspCall(FUNC_DISPLAY_CLEAR); -} - -void DisplayDrawHLine(uint16_t x, uint16_t y, int16_t len, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_len = len; - dsp_color = color; - XdspCall(FUNC_DISPLAY_DRAW_HLINE); -} - -void DisplayDrawVLine(uint16_t x, uint16_t y, int16_t len, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_len = len; - dsp_color = color; - XdspCall(FUNC_DISPLAY_DRAW_VLINE); -} - -void DisplayDrawLine(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_x2 = x2; - dsp_y2 = y2; - dsp_color = color; - XdspCall(FUNC_DISPLAY_DRAW_LINE); -} - -void DisplayDrawCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_rad = rad; - dsp_color = color; - XdspCall(FUNC_DISPLAY_DRAW_CIRCLE); -} - -void DisplayDrawFilledCircle(uint16_t x, uint16_t y, uint16_t rad, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_rad = rad; - dsp_color = color; - XdspCall(FUNC_DISPLAY_FILL_CIRCLE); -} - -void DisplayDrawRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_x2 = x2; - dsp_y2 = y2; - dsp_color = color; - XdspCall(FUNC_DISPLAY_DRAW_RECTANGLE); -} - -void DisplayDrawFilledRectangle(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2, uint16_t color) -{ - dsp_x = x; - dsp_y = y; - dsp_x2 = x2; - dsp_y2 = y2; - dsp_color = color; - XdspCall(FUNC_DISPLAY_FILL_RECTANGLE); -} - -void DisplayDrawFrame(void) -{ - XdspCall(FUNC_DISPLAY_DRAW_FRAME); -} - -void DisplaySetSize(uint8_t size) -{ - Settings.display_size = size &3; - XdspCall(FUNC_DISPLAY_TEXT_SIZE); -} - -void DisplaySetFont(uint8_t font) -{ - Settings.display_font = font &3; - XdspCall(FUNC_DISPLAY_FONT_SIZE); -} - -void DisplaySetRotation(uint8_t rotation) -{ - Settings.display_rotate = rotation &3; - XdspCall(FUNC_DISPLAY_ROTATION); -} - -void DisplayDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) -{ - dsp_x = x; - dsp_y = y; - dsp_str = str; - dsp_color = color; - dsp_flag = flag; - XdspCall(FUNC_DISPLAY_DRAW_STRING); -} - -void DisplayOnOff(uint8_t on) -{ - dsp_on = on; - XdspCall(FUNC_DISPLAY_ONOFF); -} - - - - -uint8_t fatoiv(char *cp,float *res) { - uint8_t index=0; - *res=CharToFloat(cp); - while (*cp) { - if ((*cp>='0' && *cp<='9') || (*cp=='-') || (*cp=='.')) { - cp++; - index++; - } else { - break; - } - } - return index; -} - - -uint8_t atoiv(char *cp, int16_t *res) -{ - uint8_t index = 0; - *res = atoi(cp); - while (*cp) { - if ((*cp>='0' && *cp<='9') || (*cp=='-')) { - cp++; - index++; - } else { - break; - } - } - return index; -} - - -uint8_t atoiV(char *cp, uint16_t *res) -{ - uint8_t index = 0; - *res = atoi(cp); - while (*cp) { - if (*cp>='0' && *cp<='9') { - cp++; - index++; - } else { - break; - } - } - return index; -} - - -void alignright(char *string) { - uint16_t slen=strlen(string); - uint16_t len=slen; - while (len) { - - if (string[len-1]!=' ') { - break; - } - len--; - } - uint16_t diff=slen-len; - if (diff>0) { - - memmove(&string[diff],string,len); - memset(string,' ',diff); - } -} - -char *get_string(char *buff,uint8_t len,char *cp) { -uint8_t index=0; - while (*cp!=':') { - buff[index]=*cp++; - index++; - if (index>=len) break; - } - buff[index]=0; - cp++; - return cp; -} - -#define ESCAPE_CHAR '~' - - -uint32_t decode_te(char *line) { - uint32_t skip = 0; - char sbuf[3],*cp; - while (*line) { - if (*line==ESCAPE_CHAR) { - cp=line+1; - if (*cp!=0 && *cp==ESCAPE_CHAR) { - - memmove(cp,cp+1,strlen(cp)); - skip++; - } else { - - if (strlen(cp)<2) { - - return skip; - } - - sbuf[0]=*(cp); - sbuf[1]=*(cp+1); - sbuf[2]=0; - *line=strtol(sbuf,0,16); - - memmove(cp,cp+2,strlen(cp)-1); - skip += 2; - } - } - line++; - } - return skip; -} - - - -#define DISPLAY_BUFFER_COLS 128 - -void DisplayText(void) -{ - uint8_t lpos; - uint8_t escape = 0; - uint8_t var; - int16_t lin = 0; - int16_t col = 0; - int16_t fill = 0; - int16_t temp; - int16_t temp1; - float ftemp; - - char linebuf[DISPLAY_BUFFER_COLS]; - char *dp = linebuf; - char *cp = XdrvMailbox.data; - - memset(linebuf, ' ', sizeof(linebuf)); - linebuf[sizeof(linebuf)-1] = 0; - *dp = 0; - - while (*cp) { - if (!escape) { - - if (*cp == '[') { - escape = 1; - cp++; - - if ((uint32_t)dp - (uint32_t)linebuf) { - if (!fill) { *dp = 0; } - if (col > 0 && lin > 0) { - - if (!renderer) DisplayDrawStringAt(col, lin, linebuf, fg_color, 1); - else renderer->DrawStringAt(col, lin, linebuf, fg_color, 1); - } else { - - if (!renderer) DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); - else renderer->DrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); - } - memset(linebuf, ' ', sizeof(linebuf)); - linebuf[sizeof(linebuf)-1] = 0; - dp = linebuf; - } - } else { - - if (dp < (linebuf + DISPLAY_BUFFER_COLS)) { *dp++ = *cp++; } - } - } else { - - if (*cp == ']') { - escape = 0; - cp++; - } else { - - switch (*cp++) { - case 'z': - - if (!renderer) DisplayClear(); - else renderer->fillScreen(bg_color); - disp_xpos = 0; - disp_ypos = 0; - col = 0; - lin = 0; - break; - case 'i': - - DisplayInit(DISPLAY_INIT_PARTIAL); - break; - case 'I': - - DisplayInit(DISPLAY_INIT_FULL); - break; - case 'o': - if (!renderer) { - DisplayOnOff(0); - } else { - renderer->DisplayOnff(0); - } - break; - case 'O': - if (!renderer) { - DisplayOnOff(1); - } else { - renderer->DisplayOnff(1); - } - break; - case 'x': - - var = atoiv(cp, &disp_xpos); - cp += var; - break; - case 'y': - - var = atoiv(cp, &disp_ypos); - cp += var; - break; - case 'l': - - var = atoiv(cp, &lin); - cp += var; - - break; - case 'c': - - var = atoiv(cp, &col); - cp += var; - - break; - case 'C': - - if (*cp=='i') { - - cp++; - var = atoiv(cp, &temp); - if (renderer) ftemp=renderer->GetColorFromIndex(temp); - } else { - - var = fatoiv(cp,&ftemp); - } - fg_color=ftemp; - cp += var; - if (renderer) renderer->setTextColor(fg_color,bg_color); - break; - case 'B': - - if (*cp=='i') { - - cp++; - var = atoiv(cp, &temp); - if (renderer) ftemp=renderer->GetColorFromIndex(temp); - } else { - var = fatoiv(cp,&ftemp); - } - bg_color=ftemp; - cp += var; - if (renderer) renderer->setTextColor(fg_color,bg_color); - break; - case 'p': - - var = atoiv(cp, &fill); - cp += var; - linebuf[fill] = 0; - break; -#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) - case 'P': - { char *ep=strchr(cp,':'); - if (ep) { - *ep=0; - ep++; - Draw_RGB_Bitmap(cp,disp_xpos,disp_ypos); - cp=ep; - } - } - break; -#endif - case 'h': - - var = atoiv(cp, &temp); - cp += var; - if (temp < 0) { - if (renderer) renderer->writeFastHLine(disp_xpos + temp, disp_ypos, -temp, fg_color); - else DisplayDrawHLine(disp_xpos + temp, disp_ypos, -temp, fg_color); - } else { - if (renderer) renderer->writeFastHLine(disp_xpos, disp_ypos, temp, fg_color); - else DisplayDrawHLine(disp_xpos, disp_ypos, temp, fg_color); - } - disp_xpos += temp; - break; - case 'v': - - var = atoiv(cp, &temp); - cp += var; - if (temp < 0) { - if (renderer) renderer->writeFastVLine(disp_xpos, disp_ypos + temp, -temp, fg_color); - else DisplayDrawVLine(disp_xpos, disp_ypos + temp, -temp, fg_color); - } else { - if (renderer) renderer->writeFastVLine(disp_xpos, disp_ypos, temp, fg_color); - else DisplayDrawVLine(disp_xpos, disp_ypos, temp, fg_color); - } - disp_ypos += temp; - break; - case 'L': - - var = atoiv(cp, &temp); - cp += var; - cp++; - var = atoiv(cp, &temp1); - cp += var; - if (renderer) renderer->writeLine(disp_xpos, disp_ypos, temp, temp1, fg_color); - else DisplayDrawLine(disp_xpos, disp_ypos, temp, temp1, fg_color); - disp_xpos += temp; - disp_ypos += temp1; - break; - case 'k': - - var = atoiv(cp, &temp); - cp += var; - if (renderer) renderer->drawCircle(disp_xpos, disp_ypos, temp, fg_color); - else DisplayDrawCircle(disp_xpos, disp_ypos, temp, fg_color); - break; - case 'K': - - var = atoiv(cp, &temp); - cp += var; - if (renderer) renderer->fillCircle(disp_xpos, disp_ypos, temp, fg_color); - else DisplayDrawFilledCircle(disp_xpos, disp_ypos, temp, fg_color); - break; - case 'r': - - var = atoiv(cp, &temp); - cp += var; - cp++; - var = atoiv(cp, &temp1); - cp += var; - if (renderer) renderer->drawRect(disp_xpos, disp_ypos, temp, temp1, fg_color); - else DisplayDrawRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); - break; - case 'R': - - var = atoiv(cp, &temp); - cp += var; - cp++; - var = atoiv(cp, &temp1); - cp += var; - if (renderer) renderer->fillRect(disp_xpos, disp_ypos, temp, temp1, fg_color); - else DisplayDrawFilledRectangle(disp_xpos, disp_ypos, temp, temp1, fg_color); - break; - case 'u': - - { int16_t rad; - var = atoiv(cp, &temp); - cp += var; - cp++; - var = atoiv(cp, &temp1); - cp += var; - cp++; - var = atoiv(cp, &rad); - cp += var; - if (renderer) renderer->drawRoundRect(disp_xpos, disp_ypos, temp, temp1, rad, fg_color); - - } - break; - case 'U': - - { int16_t rad; - var = atoiv(cp, &temp); - cp += var; - cp++; - var = atoiv(cp, &temp1); - cp += var; - cp++; - var = atoiv(cp, &rad); - cp += var; - if (renderer) renderer->fillRoundRect(disp_xpos, disp_ypos, temp, temp1, rad, fg_color); - - } - break; - - case 't': - if (*cp=='S') { - cp++; - if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) { - snprintf_P(dp, 9, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - dp += 8; - } - } else { - if (dp < (linebuf + DISPLAY_BUFFER_COLS) -5) { - snprintf_P(dp, 6, PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute); - dp += 5; - } - } - break; - case 'T': - if (dp < (linebuf + DISPLAY_BUFFER_COLS) -8) { - snprintf_P(dp, 9, PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year%2000); - dp += 8; - } - break; - case 'd': - - if (renderer) renderer->Updateframe(); - else DisplayDrawFrame(); - break; - case 'D': - - auto_draw=*cp&3; - if (renderer) renderer->setDrawMode(auto_draw>>1); - cp += 1; - break; - case 's': - - if (renderer) renderer->setTextSize(*cp&7); - else DisplaySetSize(*cp&3); - cp += 1; - break; - case 'f': - - if (renderer) renderer->setTextFont(*cp&7); - else DisplaySetFont(*cp&7); - cp += 1; - break; - case 'a': - - if (renderer) renderer->setRotation(*cp&3); - else DisplaySetRotation(*cp&3); - cp+=1; - break; - -#ifdef USE_GRAPH - case 'G': - - if (*cp=='d') { - cp++; - var=atoiv(cp,&temp); - cp+=var; - cp++; - var=atoiv(cp,&temp1); - cp+=var; - RedrawGraph(temp,temp1); - break; - } -#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) - if (*cp=='s') { - cp++; - var=atoiv(cp,&temp); - cp+=var; - cp++; - - char bbuff[128]; - cp=get_string(bbuff,sizeof(bbuff),cp); - Save_graph(temp,bbuff); - break; - } - if (*cp=='r') { - cp++; - var=atoiv(cp,&temp); - cp+=var; - cp++; - - char bbuff[128]; - cp=get_string(bbuff,sizeof(bbuff),cp); - Restore_graph(temp,bbuff); - break; - } -#endif - { int16_t num,gxp,gyp,gxs,gys,dec,icol; - float ymin,ymax; - var=atoiv(cp,&num); - cp+=var; - cp++; - var=atoiv(cp,&gxp); - cp+=var; - cp++; - var=atoiv(cp,&gyp); - cp+=var; - cp++; - var=atoiv(cp,&gxs); - cp+=var; - cp++; - var=atoiv(cp,&gys); - cp+=var; - cp++; - var=atoiv(cp,&dec); - cp+=var; - cp++; - var=fatoiv(cp,&ymin); - cp+=var; - cp++; - var=fatoiv(cp,&ymax); - cp+=var; - if (color_type==COLOR_COLOR) { - - cp++; - var=atoiv(cp,&icol); - cp+=var; - } else { - icol=0; - } - DefineGraph(num,gxp,gyp,gxs,gys,dec,ymin,ymax,icol); - } - break; - case 'g': - { float temp; - int16_t num; - var=atoiv(cp,&num); - cp+=var; - cp++; - var=fatoiv(cp,&temp); - cp+=var; - AddValue(num,temp); - } - break; -#endif - -#ifdef USE_AWATCH - case 'w': - var = atoiv(cp, &temp); - cp += var; - DrawAClock(temp); - break; -#endif - -#ifdef USE_TOUCH_BUTTONS - case 'b': - { int16_t num,gxp,gyp,gxs,gys,outline,fill,textcolor,textsize; - var=atoiv(cp,&num); - cp+=var; - cp++; - uint8_t bflags=num>>8; - num=num%MAXBUTTONS; - var=atoiv(cp,&gxp); - cp+=var; - cp++; - var=atoiv(cp,&gyp); - cp+=var; - cp++; - var=atoiv(cp,&gxs); - cp+=var; - cp++; - var=atoiv(cp,&gys); - cp+=var; - cp++; - var=atoiv(cp,&outline); - cp+=var; - cp++; - var=atoiv(cp,&fill); - cp+=var; - cp++; - var=atoiv(cp,&textcolor); - cp+=var; - cp++; - var=atoiv(cp,&textsize); - cp+=var; - cp++; - - char bbuff[32]; - cp=get_string(bbuff,sizeof(bbuff),cp); - - if (buttons[num]) { - delete buttons[num]; - } - if (renderer) { - buttons[num]= new VButton(); - if (buttons[num]) { - buttons[num]->vpower=bflags; - buttons[num]->initButtonUL(renderer,gxp,gyp,gxs,gys,renderer->GetColorFromIndex(outline),\ - renderer->GetColorFromIndex(fill),renderer->GetColorFromIndex(textcolor),bbuff,textsize); - if (!bflags) { - - buttons[num]->xdrawButton(bitRead(power,num)); - } else { - - buttons[num]->vpower&=0x7f; - buttons[num]->xdrawButton(buttons[num]->vpower&0x80); - } - } - } - } - break; -#endif - default: - - Response_P(PSTR("Unknown Escape")); - goto exit; - break; - } - } - } - } - exit: - - dp -= decode_te(linebuf); - if ((uint32_t)dp - (uint32_t)linebuf) { - if (!fill) { - *dp = 0; - } else { - linebuf[abs(fill)] = 0; - } - if (fill<0) { - - alignright(linebuf); - } - if (col > 0 && lin > 0) { - - if (!renderer) DisplayDrawStringAt(col, lin, linebuf, fg_color, 1); - else renderer->DrawStringAt(col, lin, linebuf, fg_color, 1); - } else { - - if (!renderer) DisplayDrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); - else renderer->DrawStringAt(disp_xpos, disp_ypos, linebuf, fg_color, 0); - } - } - - if (auto_draw&1) { - if (renderer) renderer->Updateframe(); - else DisplayDrawFrame(); - } -} - - - -#ifdef USE_DISPLAY_MODES1TO5 - -void DisplayClearScreenBuffer(void) -{ - if (disp_screen_buffer_cols) { - for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { - memset(disp_screen_buffer[i], 0, disp_screen_buffer_cols); - } - } -} - -void DisplayFreeScreenBuffer(void) -{ - if (disp_screen_buffer != nullptr) { - for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { - if (disp_screen_buffer[i] != nullptr) { free(disp_screen_buffer[i]); } - } - free(disp_screen_buffer); - disp_screen_buffer_cols = 0; - disp_screen_buffer_rows = 0; - } -} - -void DisplayAllocScreenBuffer(void) -{ - if (!disp_screen_buffer_cols) { - disp_screen_buffer_rows = Settings.display_rows; - disp_screen_buffer = (char**)malloc(sizeof(*disp_screen_buffer) * disp_screen_buffer_rows); - if (disp_screen_buffer != nullptr) { - for (uint32_t i = 0; i < disp_screen_buffer_rows; i++) { - disp_screen_buffer[i] = (char*)malloc(sizeof(*disp_screen_buffer[i]) * (Settings.display_cols[0] +1)); - if (disp_screen_buffer[i] == nullptr) { - DisplayFreeScreenBuffer(); - break; - } - } - } - if (disp_screen_buffer != nullptr) { - disp_screen_buffer_cols = Settings.display_cols[0] +1; - DisplayClearScreenBuffer(); - } - } -} - -void DisplayReAllocScreenBuffer(void) -{ - DisplayFreeScreenBuffer(); - DisplayAllocScreenBuffer(); -} - -void DisplayFillScreen(uint32_t line) -{ - uint32_t len = disp_screen_buffer_cols - strlen(disp_screen_buffer[line]); - if (len) { - memset(disp_screen_buffer[line] + strlen(disp_screen_buffer[line]), 0x20, len); - disp_screen_buffer[line][disp_screen_buffer_cols -1] = 0; - } -} - - - -void DisplayClearLogBuffer(void) -{ - if (disp_log_buffer_cols) { - for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { - memset(disp_log_buffer[i], 0, disp_log_buffer_cols); - } - } -} - -void DisplayFreeLogBuffer(void) -{ - if (disp_log_buffer != nullptr) { - for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { - if (disp_log_buffer[i] != nullptr) { free(disp_log_buffer[i]); } - } - free(disp_log_buffer); - disp_log_buffer_cols = 0; - } -} - -void DisplayAllocLogBuffer(void) -{ - if (!disp_log_buffer_cols) { - disp_log_buffer = (char**)malloc(sizeof(*disp_log_buffer) * DISPLAY_LOG_ROWS); - if (disp_log_buffer != nullptr) { - for (uint32_t i = 0; i < DISPLAY_LOG_ROWS; i++) { - disp_log_buffer[i] = (char*)malloc(sizeof(*disp_log_buffer[i]) * (Settings.display_cols[0] +1)); - if (disp_log_buffer[i] == nullptr) { - DisplayFreeLogBuffer(); - break; - } - } - } - if (disp_log_buffer != nullptr) { - disp_log_buffer_cols = Settings.display_cols[0] +1; - DisplayClearLogBuffer(); - } - } -} - -void DisplayReAllocLogBuffer(void) -{ - DisplayFreeLogBuffer(); - DisplayAllocLogBuffer(); -} - -void DisplayLogBufferAdd(char* txt) -{ - if (disp_log_buffer_cols) { - strlcpy(disp_log_buffer[disp_log_buffer_idx], txt, disp_log_buffer_cols); - disp_log_buffer_idx++; - if (DISPLAY_LOG_ROWS == disp_log_buffer_idx) { disp_log_buffer_idx = 0; } - } -} - -char* DisplayLogBuffer(char temp_code) -{ - char* result = nullptr; - if (disp_log_buffer_cols) { - if (disp_log_buffer_idx != disp_log_buffer_ptr) { - result = disp_log_buffer[disp_log_buffer_ptr]; - disp_log_buffer_ptr++; - if (DISPLAY_LOG_ROWS == disp_log_buffer_ptr) { disp_log_buffer_ptr = 0; } - - char *pch = strchr(result, '~'); - if (pch != nullptr) { result[pch - result] = temp_code; } - } - } - return result; -} - -void DisplayLogBufferInit(void) -{ - if (Settings.display_mode) { - disp_log_buffer_idx = 0; - disp_log_buffer_ptr = 0; - disp_refresh = Settings.display_refresh; - - snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%c"), TempUnit()); - snprintf_P(disp_pres, sizeof(disp_pres), PressureUnit().c_str()); - - DisplayReAllocLogBuffer(); - - char buffer[40]; - snprintf_P(buffer, sizeof(buffer), PSTR(D_VERSION " %s%s"), my_version, my_image); - DisplayLogBufferAdd(buffer); - snprintf_P(buffer, sizeof(buffer), PSTR("Display mode %d"), Settings.display_mode); - DisplayLogBufferAdd(buffer); - - snprintf_P(buffer, sizeof(buffer), PSTR(D_CMND_HOSTNAME " %s"), my_hostname); - DisplayLogBufferAdd(buffer); - snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_SSID " %s"), SettingsText(SET_STASSID1 + Settings.sta_active)); - DisplayLogBufferAdd(buffer); - snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_MAC " %s"), WiFi.macAddress().c_str()); - DisplayLogBufferAdd(buffer); - if (!global_state.wifi_down) { - snprintf_P(buffer, sizeof(buffer), PSTR("IP %s"), WiFi.localIP().toString().c_str()); - DisplayLogBufferAdd(buffer); - snprintf_P(buffer, sizeof(buffer), PSTR(D_JSON_RSSI " %d%%"), WifiGetRssiAsQuality(WiFi.RSSI())); - DisplayLogBufferAdd(buffer); - } - } -} - - - - - -enum SensorQuantity { - JSON_TEMPERATURE, - JSON_HUMIDITY, JSON_LIGHT, JSON_NOISE, JSON_AIRQUALITY, - JSON_PRESSURE, JSON_PRESSUREATSEALEVEL, - JSON_ILLUMINANCE, - JSON_GAS, - JSON_YESTERDAY, JSON_TOTAL, JSON_TODAY, - JSON_PERIOD, - JSON_POWERFACTOR, JSON_COUNTER, JSON_ANALOG_INPUT, JSON_UV_LEVEL, - JSON_CURRENT, - JSON_VOLTAGE, - JSON_POWERUSAGE, - JSON_CO2, - JSON_FREQUENCY }; -const char kSensorQuantity[] PROGMEM = - D_JSON_TEMPERATURE "|" - D_JSON_HUMIDITY "|" D_JSON_LIGHT "|" D_JSON_NOISE "|" D_JSON_AIRQUALITY "|" - D_JSON_PRESSURE "|" D_JSON_PRESSUREATSEALEVEL "|" - D_JSON_ILLUMINANCE "|" - D_JSON_GAS "|" - D_JSON_YESTERDAY "|" D_JSON_TOTAL "|" D_JSON_TODAY "|" - D_JSON_PERIOD "|" - D_JSON_POWERFACTOR "|" D_JSON_COUNTER "|" D_JSON_ANALOG_INPUT "|" D_JSON_UV_LEVEL "|" - D_JSON_CURRENT "|" - D_JSON_VOLTAGE "|" - D_JSON_POWERUSAGE "|" - D_JSON_CO2 "|" - D_JSON_FREQUENCY ; - -void DisplayJsonValue(const char* topic, const char* device, const char* mkey, const char* value) -{ - char quantity[TOPSZ]; - char buffer[Settings.display_cols[0] +1]; - char spaces[Settings.display_cols[0]]; - char source[Settings.display_cols[0] - Settings.display_cols[1]]; - char svalue[Settings.display_cols[1] +1]; - -#ifdef USE_DEBUG_DRIVER - ShowFreeMem(PSTR("DisplayJsonValue")); -#endif - - memset(spaces, 0x20, sizeof(spaces)); - spaces[sizeof(spaces) -1] = '\0'; - snprintf_P(source, sizeof(source), PSTR("%s%s%s%s"), topic, (strlen(topic))?"/":"", mkey, spaces); - - int quantity_code = GetCommandCode(quantity, sizeof(quantity), mkey, kSensorQuantity); - if ((-1 == quantity_code) || !strcmp_P(mkey, S_RSLT_POWER)) { - return; - } - if (JSON_TEMPERATURE == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s~%s"), value, disp_temp); - } - else if ((quantity_code >= JSON_HUMIDITY) && (quantity_code <= JSON_AIRQUALITY)) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s%%"), value); - } - else if ((quantity_code >= JSON_PRESSURE) && (quantity_code <= JSON_PRESSUREATSEALEVEL)) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s%s"), value, disp_pres); - } - else if (JSON_ILLUMINANCE == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_LUX), value); - } - else if (JSON_GAS == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_KILOOHM), value); - } - else if ((quantity_code >= JSON_YESTERDAY) && (quantity_code <= JSON_TODAY)) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_KILOWATTHOUR), value); - } - else if (JSON_PERIOD == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_WATTHOUR), value); - } - else if ((quantity_code >= JSON_POWERFACTOR) && (quantity_code <= JSON_UV_LEVEL)) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s"), value); - } - else if (JSON_CURRENT == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_AMPERE), value); - } - else if (JSON_VOLTAGE == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_VOLT), value); - } - else if (JSON_POWERUSAGE == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_WATT), value); - } - else if (JSON_CO2 == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_PARTS_PER_MILLION), value); - } - else if (JSON_FREQUENCY == quantity_code) { - snprintf_P(svalue, sizeof(svalue), PSTR("%s" D_UNIT_HERTZ), value); - } - snprintf_P(buffer, sizeof(buffer), PSTR("%s %s"), source, svalue); - - - - DisplayLogBufferAdd(buffer); -} - -void DisplayAnalyzeJson(char *topic, char *json) -{ -# 1145 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_13_display.ino" - String jsonStr = json; - - StaticJsonBuffer<1024> jsonBuf; - JsonObject &root = jsonBuf.parseObject(jsonStr); - if (root.success()) { - - const char *unit; - unit = root[D_JSON_TEMPERATURE_UNIT]; - if (unit) { - snprintf_P(disp_temp, sizeof(disp_temp), PSTR("%s"), unit); - } - unit = root[D_JSON_PRESSURE_UNIT]; - if (unit) { - snprintf_P(disp_pres, sizeof(disp_pres), PSTR("%s"), unit); - } - - for (JsonObject::iterator it = root.begin(); it != root.end(); ++it) { - JsonVariant value = it->value; - if (value.is()) { - JsonObject& Object2 = value; - for (JsonObject::iterator it2 = Object2.begin(); it2 != Object2.end(); ++it2) { - JsonVariant value2 = it2->value; - if (value2.is()) { - JsonObject& Object3 = value2; - for (JsonObject::iterator it3 = Object3.begin(); it3 != Object3.end(); ++it3) { - const char* value = it3->value; - if (value != nullptr) { - DisplayJsonValue(topic, it->key, it3->key, value); - } - } - } else { - const char* value = it2->value; - if (value != nullptr) { - DisplayJsonValue(topic, it->key, it2->key, value); - } - } - } - } else { - const char* value = it->value; - if (value != nullptr) { - DisplayJsonValue(topic, it->key, it->key, value); - } - } - } - } -} - -void DisplayMqttSubscribe(void) -{ - - - - - - - if (Settings.display_model && (Settings.display_mode &0x04)) { - - char stopic[TOPSZ]; - char ntopic[TOPSZ]; - - ntopic[0] = '\0'; - strlcpy(stopic, SettingsText(SET_MQTT_FULLTOPIC), sizeof(stopic)); - char *tp = strtok(stopic, "/"); - while (tp != nullptr) { - if (!strcmp_P(tp, MQTT_TOKEN_PREFIX)) { - break; - } - strncat_P(ntopic, PSTR("+/"), sizeof(ntopic) - strlen(ntopic) -1); - tp = strtok(nullptr, "/"); - } - strncat(ntopic, SettingsText(SET_MQTTPREFIX3), sizeof(ntopic) - strlen(ntopic) -1); - strncat_P(ntopic, PSTR("/#"), sizeof(ntopic) - strlen(ntopic) -1); - MqttSubscribe(ntopic); - disp_subscribed = true; - } else { - disp_subscribed = false; - } -} - -bool DisplayMqttData(void) -{ - if (disp_subscribed) { - char stopic[TOPSZ]; - - snprintf_P(stopic, sizeof(stopic) , PSTR("%s/"), SettingsText(SET_MQTTPREFIX3)); - char *tp = strstr(XdrvMailbox.topic, stopic); - if (tp) { - if (Settings.display_mode &0x04) { - tp = tp + strlen(stopic); - char *topic = strtok(tp, "/"); - DisplayAnalyzeJson(topic, XdrvMailbox.data); - } - return true; - } - } - return false; -} - -void DisplayLocalSensor(void) -{ - if ((Settings.display_mode &0x02) && (0 == tele_period)) { - char no_topic[1] = { 0 }; - - DisplayAnalyzeJson(no_topic, mqtt_data); - } -} - -#endif - - - - - -void DisplayInitDriver(void) -{ - XdspCall(FUNC_DISPLAY_INIT_DRIVER); - - if (renderer) { - renderer->setTextFont(Settings.display_font); - renderer->setTextSize(Settings.display_size); - } - - - - - if (Settings.display_model) { - devices_present++; - disp_device = devices_present; - -#ifndef USE_DISPLAY_MODES1TO5 - Settings.display_mode = 0; -#else - DisplayLogBufferInit(); -#endif - } -} - -void DisplaySetPower(void) -{ - disp_power = bitRead(XdrvMailbox.index, disp_device -1); - - - - if (Settings.display_model) { - if (!renderer) { - XdspCall(FUNC_DISPLAY_POWER); - } else { - renderer->DisplayOnff(disp_power); - } - } -} - - - - - -void CmndDisplay(void) -{ - Response_P(PSTR("{\"" D_PRFX_DISPLAY "\":{\"" D_CMND_DISP_MODEL "\":%d,\"" D_CMND_DISP_WIDTH "\":%d,\"" D_CMND_DISP_HEIGHT "\":%d,\"" - D_CMND_DISP_MODE "\":%d,\"" D_CMND_DISP_DIMMER "\":%d,\"" D_CMND_DISP_SIZE "\":%d,\"" D_CMND_DISP_FONT "\":%d,\"" - D_CMND_DISP_ROTATE "\":%d,\"" D_CMND_DISP_REFRESH "\":%d,\"" D_CMND_DISP_COLS "\":[%d,%d],\"" D_CMND_DISP_ROWS "\":%d}}"), - Settings.display_model, Settings.display_width, Settings.display_height, - Settings.display_mode, Settings.display_dimmer, Settings.display_size, Settings.display_font, - Settings.display_rotate, Settings.display_refresh, Settings.display_cols[0], Settings.display_cols[1], Settings.display_rows); -} - -void CmndDisplayModel(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < DISPLAY_MAX_DRIVERS)) { - uint32_t last_display_model = Settings.display_model; - Settings.display_model = XdrvMailbox.payload; - if (XdspCall(FUNC_DISPLAY_MODEL)) { - restart_flag = 2; - } else { - Settings.display_model = last_display_model; - } - } - ResponseCmndNumber(Settings.display_model); -} - -void CmndDisplayWidth(void) -{ - if (XdrvMailbox.payload > 0) { - if (XdrvMailbox.payload != Settings.display_width) { - Settings.display_width = XdrvMailbox.payload; - restart_flag = 2; - } - } - ResponseCmndNumber(Settings.display_width); -} - -void CmndDisplayHeight(void) -{ - if (XdrvMailbox.payload > 0) { - if (XdrvMailbox.payload != Settings.display_height) { - Settings.display_height = XdrvMailbox.payload; - restart_flag = 2; - } - } - ResponseCmndNumber(Settings.display_height); -} - -void CmndDisplayMode(void) -{ -#ifdef USE_DISPLAY_MODES1TO5 - - - - - - - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { - uint32_t last_display_mode = Settings.display_mode; - Settings.display_mode = XdrvMailbox.payload; - - if (disp_subscribed != (Settings.display_mode &0x04)) { - restart_flag = 2; - } else { - if (last_display_mode && !Settings.display_mode) { - DisplayInit(DISPLAY_INIT_MODE); - if (renderer) renderer->fillScreen(bg_color); - else DisplayClear(); - } else { - DisplayLogBufferInit(); - DisplayInit(DISPLAY_INIT_MODE); - } - } - } -#endif - ResponseCmndNumber(Settings.display_mode); -} - -void CmndDisplayDimmer(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - Settings.display_dimmer = ((XdrvMailbox.payload +1) * 100) / 666; - if (Settings.display_dimmer && !(disp_power)) { - ExecuteCommandPower(disp_device, POWER_ON, SRC_DISPLAY); - } - else if (!Settings.display_dimmer && disp_power) { - ExecuteCommandPower(disp_device, POWER_OFF, SRC_DISPLAY); - } - if (renderer) renderer->dim(Settings.display_dimmer); - } - ResponseCmndNumber(Settings.display_dimmer); -} - -void CmndDisplaySize(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 4)) { - Settings.display_size = XdrvMailbox.payload; - if (renderer) renderer->setTextSize(Settings.display_size); - else DisplaySetSize(Settings.display_size); - } - ResponseCmndNumber(Settings.display_size); -} - -void CmndDisplayFont(void) -{ - if ((XdrvMailbox.payload >=0) && (XdrvMailbox.payload <= 4)) { - Settings.display_font = XdrvMailbox.payload; - if (renderer) renderer->setTextFont(Settings.display_font); - else DisplaySetFont(Settings.display_font); - } - ResponseCmndNumber(Settings.display_font); -} - -void CmndDisplayRotate(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 4)) { - if (Settings.display_rotate != XdrvMailbox.payload) { -# 1428 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_13_display.ino" - Settings.display_rotate = XdrvMailbox.payload; - DisplayInit(DISPLAY_INIT_MODE); -#ifdef USE_DISPLAY_MODES1TO5 - DisplayLogBufferInit(); -#endif - } - } - ResponseCmndNumber(Settings.display_rotate); -} - -void CmndDisplayText(void) -{ - if (disp_device && XdrvMailbox.data_len > 0) { -#ifndef USE_DISPLAY_MODES1TO5 - DisplayText(); -#else - if (!Settings.display_mode) { - DisplayText(); - } else { - DisplayLogBufferAdd(XdrvMailbox.data); - } -#endif - ResponseCmndChar(XdrvMailbox.data); - } -} - -void CmndDisplayAddress(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 8)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { - Settings.display_address[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.display_address[XdrvMailbox.index -1]); - } -} - -void CmndDisplayRefresh(void) -{ - if ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 7)) { - Settings.display_refresh = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.display_refresh); -} - -void CmndDisplayColumns(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 2)) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_COLS)) { - Settings.display_cols[XdrvMailbox.index -1] = XdrvMailbox.payload; -#ifdef USE_DISPLAY_MODES1TO5 - if (1 == XdrvMailbox.index) { - DisplayLogBufferInit(); - DisplayReAllocScreenBuffer(); - } -#endif - } - ResponseCmndIdxNumber(Settings.display_cols[XdrvMailbox.index -1]); - } -} - -void CmndDisplayRows(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= DISPLAY_MAX_ROWS)) { - Settings.display_rows = XdrvMailbox.payload; -#ifdef USE_DISPLAY_MODES1TO5 - DisplayLogBufferInit(); - DisplayReAllocScreenBuffer(); -#endif - } - ResponseCmndNumber(Settings.display_rows); -} - - - - - -#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) -void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp) { - if (!renderer) return; - - - File fp; - fp=SD.open(file,FILE_READ); - if (!fp) return; - uint16_t xsize; - fp.read((uint8_t*)&xsize,2); - uint16_t ysize; - fp.read((uint8_t*)&ysize,2); - -#if 1 -#define XBUFF 128 - uint16_t xdiv=xsize/XBUFF; - renderer->setAddrWindow(xp,yp,xp+xsize,yp+ysize); - for(int16_t j=0; j=2) renderer->pushColors(rgb,len/2,true); - } - OsWatchLoop(); - } - renderer->setAddrWindow(0,0,0,0); -#else - for(int16_t j=0; jwritePixel(xp+i,yp,rgb); - } - delay(0); - OsWatchLoop(); - yp++; - } -#endif - fp.close(); -} -#endif - -#ifdef USE_AWATCH -#define MINUTE_REDUCT 4 - -#ifndef pi -#define pi 3.14159265359 -#endif - - -void DrawAClock(uint16_t rad) { - if (!renderer) return; - float frad=rad; - uint16_t hred=frad/3.0; - renderer->fillCircle(disp_xpos, disp_ypos, rad, bg_color); - renderer->drawCircle(disp_xpos, disp_ypos, rad, fg_color); - renderer->fillCircle(disp_xpos, disp_ypos, 4, fg_color); - for (uint8_t count=0; count<60; count+=5) { - float p1=((float)count*(pi/30)-(pi/2)); - uint8_t len; - if ((count%15)==0) { - len=4; - } else { - len=2; - } - renderer->writeLine(disp_xpos+((float)(rad-len)*cosf(p1)), disp_ypos+((float)(rad-len)*sinf(p1)), disp_xpos+(frad*cosf(p1)), disp_ypos+(frad*sinf(p1)), fg_color); - } - - - float hour=((float)RtcTime.hour*60.0+(float)RtcTime.minute)/60.0; - float temp=(hour*(pi/6.0)-(pi/2.0)); - renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-hred)*cosf(temp),disp_ypos+(frad-hred)*sinf(temp), fg_color); - - - temp=((float)RtcTime.minute*(pi/30.0)-(pi/2.0)); - renderer->writeLine(disp_xpos, disp_ypos,disp_xpos+(frad-MINUTE_REDUCT)*cosf(temp),disp_ypos+(frad-MINUTE_REDUCT)*sinf(temp), fg_color); -} -#endif - - -#ifdef USE_GRAPH - -typedef union { - uint8_t data; - struct { - uint8_t overlay : 1; - uint8_t draw : 1; - uint8_t nu3 : 1; - uint8_t nu4 : 1; - uint8_t nu5 : 1; - uint8_t nu6 : 1; - uint8_t nu7 : 1; - uint8_t nu8 : 1; - }; -} GFLAGS; - -struct GRAPH { - uint16_t xp; - uint16_t yp; - uint16_t xs; - uint16_t ys; - float ymin; - float ymax; - float range; - uint32_t x_time; - uint32_t last_ms; - uint32_t last_ms_redrawn; - int16_t decimation; - uint16_t dcnt; - uint32_t summ; - uint16_t xcnt; - uint8_t *values; - uint8_t xticks; - uint8_t yticks; - uint8_t last_val; - uint8_t color_index; - GFLAGS flags; -}; - - -struct GRAPH *graph[NUM_GRAPHS]; - -#define TICKLEN 4 -void ClrGraph(uint16_t num) { - struct GRAPH *gp=graph[num]; - - uint16_t xticks=gp->xticks; - uint16_t yticks=gp->yticks; - uint16_t count; - - - if (gp->flags.overlay) return; - - renderer->fillRect(gp->xp+1,gp->yp+1,gp->xs-2,gp->ys-2,bg_color); - - if (xticks) { - float cxp=gp->xp,xd=(float)gp->xs/(float)xticks; - for (count=0; countwriteFastVLine(cxp,gp->yp+gp->ys-TICKLEN,TICKLEN,fg_color); - cxp+=xd; - } - } - if (yticks) { - if (gp->ymin<0 && gp->ymax>0) { - - float cxp=0; - float czp=gp->yp+(gp->ymax/gp->range); - while (cxpxs) { - renderer->writeFastHLine(gp->xp+cxp,czp,2,fg_color); - cxp+=6.0; - } - - float cyp=0,yd=gp->ys/yticks; - for (count=0; countgp->yp) { - renderer->writeFastHLine(gp->xp,czp-cyp,TICKLEN,fg_color); - renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,czp-cyp,TICKLEN,fg_color); - } - if ((czp+cyp)<(gp->yp+gp->ys)) { - renderer->writeFastHLine(gp->xp,czp+cyp,TICKLEN,fg_color); - renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,czp+cyp,TICKLEN,fg_color); - } - cyp+=yd; - } - } else { - float cyp=gp->yp,yd=gp->ys/yticks; - for (count=0; countwriteFastHLine(gp->xp,cyp,TICKLEN,fg_color); - renderer->writeFastHLine(gp->xp+gp->xs-TICKLEN,cyp,TICKLEN,fg_color); - cyp+=yd; - } - } - } -} - - -void DefineGraph(uint16_t num,uint16_t xp,uint16_t yp,int16_t xs,uint16_t ys,int16_t dec,float ymin, float ymax,uint8_t icol) { - if (!renderer) return; - uint8_t rflg=0; - if (xs<0) { - rflg=1; - xs=abs(xs); - } - struct GRAPH *gp; - uint16_t count; - uint16_t index=num%NUM_GRAPHS; - if (!graph[index]) { - gp=(struct GRAPH*)calloc(sizeof(struct GRAPH),1); - if (!gp) return; - graph[index]=gp; - } else { - gp=graph[index]; - if (rflg) { - RedrawGraph(index,1); - return; - } - } - - - gp->xticks=(num>>4)&0x3f; - gp->yticks=(num>>10)&0x3f; - gp->xp=xp; - gp->yp=yp; - gp->xs=xs; - gp->ys=ys; - if (!dec) dec=1; - gp->decimation=dec; - if (dec>0) { - - gp->x_time=((float)dec*60000.0)/(float)xs; - gp->last_ms=millis()+gp->x_time; - } - gp->ymin=ymin; - gp->ymax=ymax; - gp->range=(ymax-ymin)/ys; - gp->xcnt=0; - gp->dcnt=0; - gp->summ=0; - if (gp->values) free(gp->values); - gp->values=(uint8_t*) calloc(1,xs+2); - if (!gp->values) { - free(gp); - graph[index]=0; - return; - } - - gp->values[0]=0; - - gp->last_ms_redrawn=millis(); - - if (!icol) icol=1; - gp->color_index=icol; - gp->flags.overlay=0; - gp->flags.draw=1; - - - if (index>0) { - for (uint8_t count=0; countxp==gp1->xp) && (gp->yp==gp1->yp)) { - gp->flags.overlay=1; - break; - } - } - } - } - - - renderer->drawRect(xp,yp,xs,ys,fg_color); - - ClrGraph(index); - -} - - -void DisplayCheckGraph() { - int16_t count; - struct GRAPH *gp; - for (count=0;countdecimation>0) { - - while (millis()>gp->last_ms) { - gp->last_ms+=gp->x_time; - uint8_t val; - if (gp->dcnt) { - val=gp->summ/gp->dcnt; - gp->dcnt=0; - gp->summ=0; - gp->last_val=val; - } else { - val=gp->last_val; - } - AddGraph(count,val); - } - } - } - } -} - - -#if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) -#include - -void Save_graph(uint8_t num, char *path) { - if (!renderer) return; - uint16_t index=num%NUM_GRAPHS; - struct GRAPH *gp=graph[index]; - if (!gp) return; - File fp; - SD.remove(path); - fp=SD.open(path,FILE_WRITE); - if (!fp) return; - char str[32]; - sprintf_P(str,PSTR("%d\t%d\t%d\t"),gp->xcnt,gp->xs,gp->ys); - fp.print(str); - dtostrfd(gp->ymin,2,str); - fp.print(str); - fp.print("\t"); - dtostrfd(gp->ymax,2,str); - fp.print(str); - fp.print("\t"); - for (uint32_t count=0;countxs;count++) { - dtostrfd(gp->values[count],0,str); - fp.print(str); - fp.print("\t"); - } - fp.print("\n"); - fp.close(); -} -void Restore_graph(uint8_t num, char *path) { - if (!renderer) return; - uint16_t index=num%NUM_GRAPHS; - struct GRAPH *gp=graph[index]; - if (!gp) return; - File fp; - fp=SD.open(path,FILE_READ); - if (!fp) return; - char vbuff[32]; - char *cp=vbuff; - uint8_t buf[2]; - uint8_t findex=0; - - for (uint32_t count=0;count<=gp->xs+4;count++) { - cp=vbuff; - findex=0; - while (fp.available()) { - fp.read(buf,1); - if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { - break; - } else { - *cp++=buf[0]; - findex++; - if (findex>=sizeof(vbuff)-1) break; - } - } - *cp=0; - if (count<=4) { - if (count==0) gp->xcnt=atoi(vbuff); - } else { - gp->values[count-5]=atoi(vbuff); - } - } - fp.close(); - RedrawGraph(num,1); -} -#endif - -void RedrawGraph(uint8_t num, uint8_t flags) { - uint16_t index=num%NUM_GRAPHS; - struct GRAPH *gp=graph[index]; - if (!gp) return; - if (!flags) { - gp->flags.draw=0; - return; - } - if (!renderer) return; - - gp->flags.draw=1; - uint16_t linecol=fg_color; - - if (color_type==COLOR_COLOR) { - linecol=renderer->GetColorFromIndex(gp->color_index); - } - - if (!gp->flags.overlay) { - - renderer->drawRect(gp->xp,gp->yp,gp->xs,gp->ys,fg_color); - - ClrGraph(index); - } - - for (uint16_t count=0;countxs-1;count++) { - renderer->writeLine(gp->xp+count,gp->yp+gp->ys-gp->values[count]-1,gp->xp+count+1,gp->yp+gp->ys-gp->values[count+1]-1,linecol); - } -} - - -void AddGraph(uint8_t num,uint8_t val) { - struct GRAPH *gp=graph[num]; - if (!renderer) return; - - uint16_t linecol=fg_color; - if (color_type==COLOR_COLOR) { - linecol=renderer->GetColorFromIndex(gp->color_index); - } - gp->xcnt++; - if (gp->xcnt>gp->xs) { - gp->xcnt=gp->xs; - int16_t count; - - for (count=0;countxs-1;count++) { - gp->values[count]=gp->values[count+1]; - } - gp->values[gp->xcnt-1]=val; - - if (!gp->flags.draw) return; - - - if (millis()-gp->last_ms_redrawn>1000) { - gp->last_ms_redrawn=millis(); - - if (!gp->flags.overlay) { - - renderer->drawRect(gp->xp,gp->yp,gp->xs,gp->ys,fg_color); - - ClrGraph(num); - } - - for (count=0;countxs-1;count++) { - renderer->writeLine(gp->xp+count,gp->yp+gp->ys-gp->values[count]-1,gp->xp+count+1,gp->yp+gp->ys-gp->values[count+1]-1,linecol); - } - } - } else { - - gp->values[gp->xcnt]=val; - if (!gp->flags.draw) return; - renderer->writeLine(gp->xp+gp->xcnt-1,gp->yp+gp->ys-gp->values[gp->xcnt-1]-1,gp->xp+gp->xcnt,gp->yp+gp->ys-gp->values[gp->xcnt]-1,linecol); - } -} - - - -void AddValue(uint8_t num,float fval) { - - num=num%NUM_GRAPHS; - struct GRAPH *gp=graph[num]; - if (!gp) return; - - if (fval>gp->ymax) fval=gp->ymax; - if (fvalymin) fval=gp->ymin; - - int16_t val; - val=(fval-gp->ymin)/gp->range; - - if (val>gp->ys-1) val=gp->ys-1; - if (val<0) val=0; - - - gp->summ+=val; - gp->dcnt++; - - - if (gp->decimation<0) { - if (gp->dcnt>=-gp->decimation) { - gp->dcnt=0; - - val=gp->summ/-gp->decimation; - gp->summ=0; - - AddGraph(num,val); - } - } -} -#endif - - - - - -bool Xdrv13(uint8_t function) -{ - bool result = false; - - if ((i2c_flg || spi_flg || soft_spi_flg) && XdspPresent()) { - switch (function) { - case FUNC_PRE_INIT: - DisplayInitDriver(); -#ifdef USE_GRAPH - for (uint8_t count=0;count - -TasmotaSerial *MP3Player; - - - - - -#define D_CMND_MP3 "MP3" - -const char S_JSON_MP3_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_MP3 "%s\":%d}"; -const char S_JSON_MP3_COMMAND[] PROGMEM = "{\"" D_CMND_MP3 "%s\"}"; -const char kMP3_Commands[] PROGMEM = "Track|Play|Pause|Stop|Volume|EQ|Device|Reset|DAC"; - - - - - -enum MP3_Commands { - CMND_MP3_TRACK, - CMND_MP3_PLAY, - CMND_MP3_PAUSE, - CMND_MP3_STOP, - CMND_MP3_VOLUME, - CMND_MP3_EQ, - CMND_MP3_DEVICE, - CMND_MP3_RESET, - CMND_MP3_DAC }; - - - - - - -#define MP3_CMD_RESET_VALUE 0 - -#define MP3_CMD_TRACK 0x03 -#define MP3_CMD_PLAY 0x0d -#define MP3_CMD_PAUSE 0x0e -#define MP3_CMD_STOP 0x16 -#define MP3_CMD_VOLUME 0x06 -#define MP3_CMD_EQ 0x07 -#define MP3_CMD_DEVICE 0x09 -#define MP3_CMD_RESET 0x0C -#define MP3_CMD_DAC 0x1A - - - - - - -uint16_t MP3_Checksum(uint8_t *array) -{ - uint16_t checksum = 0; - for (uint32_t i = 0; i < 6; i++) { - checksum += array[i]; - } - checksum = checksum^0xffff; - return (checksum+1); -} - - - - - - -void MP3PlayerInit(void) { - MP3Player = new TasmotaSerial(-1, pin[GPIO_MP3_DFR562]); - - if (MP3Player->begin(9600)) { - MP3Player->flush(); - delay(1000); - MP3_CMD(MP3_CMD_RESET, MP3_CMD_RESET_VALUE); - delay(3000); - MP3_CMD(MP3_CMD_VOLUME, MP3_VOLUME); - } - return; -} -# 159 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_14_mp3.ino" -void MP3_CMD(uint8_t mp3cmd,uint16_t val) { - uint8_t i = 0; - uint8_t cmd[10] = {0x7e,0xff,6,0,0,0,0,0,0,0xef}; - cmd[3] = mp3cmd; - cmd[4] = 0; - cmd[5] = val>>8; - cmd[6] = val; - uint16_t chks = MP3_Checksum(&cmd[1]); - cmd[7] = chks>>8; - cmd[8] = chks; - MP3Player->write(cmd, sizeof(cmd)); - delay(1000); - if (mp3cmd == MP3_CMD_RESET) { - MP3_CMD(MP3_CMD_VOLUME, MP3_VOLUME); - } - return; -} - - - - - -bool MP3PlayerCmd(void) { - char command[CMDSZ]; - bool serviced = true; - uint8_t disp_len = strlen(D_CMND_MP3); - - if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_MP3), disp_len)) { - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kMP3_Commands); - - switch (command_code) { - case CMND_MP3_TRACK: - case CMND_MP3_VOLUME: - case CMND_MP3_EQ: - case CMND_MP3_DEVICE: - case CMND_MP3_DAC: - - if (XdrvMailbox.data_len > 0) { - if (command_code == CMND_MP3_TRACK) { MP3_CMD(MP3_CMD_TRACK, XdrvMailbox.payload); } - if (command_code == CMND_MP3_VOLUME) { MP3_CMD(MP3_CMD_VOLUME, XdrvMailbox.payload * 30 / 100); } - if (command_code == CMND_MP3_EQ) { MP3_CMD(MP3_CMD_EQ, XdrvMailbox.payload); } - if (command_code == CMND_MP3_DEVICE) { MP3_CMD(MP3_CMD_DEVICE, XdrvMailbox.payload); } - if (command_code == CMND_MP3_DAC) { MP3_CMD(MP3_CMD_DAC, XdrvMailbox.payload); } - } - Response_P(S_JSON_MP3_COMMAND_NVALUE, command, XdrvMailbox.payload); - break; - case CMND_MP3_PLAY: - case CMND_MP3_PAUSE: - case CMND_MP3_STOP: - case CMND_MP3_RESET: - - if (command_code == CMND_MP3_PLAY) { MP3_CMD(MP3_CMD_PLAY, 0); } - if (command_code == CMND_MP3_PAUSE) { MP3_CMD(MP3_CMD_PAUSE, 0); } - if (command_code == CMND_MP3_STOP) { MP3_CMD(MP3_CMD_STOP, 0); } - if (command_code == CMND_MP3_RESET) { MP3_CMD(MP3_CMD_RESET, 0); } - Response_P(S_JSON_MP3_COMMAND, command, XdrvMailbox.payload); - break; - default: - - serviced = false; - break; - } - } else { - return false; - } - return serviced; -} - - - - - -bool Xdrv14(uint8_t function) -{ - bool result = false; - - if (pin[GPIO_MP3_DFR562] < 99) { - switch (function) { - case FUNC_PRE_INIT: - MP3PlayerInit(); - break; - case FUNC_COMMAND: - result = MP3PlayerCmd(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_15_pca9685.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_15_pca9685.ino" -#ifdef USE_I2C -#ifdef USE_PCA9685 - - - - - - -#define XDRV_15 15 -#define XI2C_01 1 - -#define PCA9685_REG_MODE1 0x00 -#define PCA9685_REG_LED0_ON_L 0x06 -#define PCA9685_REG_PRE_SCALE 0xFE - -#ifndef USE_PCA9685_ADDR - #define USE_PCA9685_ADDR 0x40 -#endif -#ifndef USE_PCA9685_FREQ - #define USE_PCA9685_FREQ 50 -#endif - -bool pca9685_detected = false; -uint16_t pca9685_freq = USE_PCA9685_FREQ; -uint16_t pca9685_pin_pwm_value[16]; - -void PCA9685_Detect(void) -{ - if (I2cActive(USE_PCA9685_ADDR)) { return; } - - uint8_t buffer; - if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { - I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x20); - if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) { - if (0x20 == buffer) { - pca9685_detected = true; - I2cSetActiveFound(USE_PCA9685_ADDR, "PCA9685"); - PCA9685_Reset(); - } - } - } -} - -void PCA9685_Reset(void) -{ - I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x80); - PCA9685_SetPWMfreq(USE_PCA9685_FREQ); - for (uint32_t pin=0;pin<16;pin++) { - PCA9685_SetPWM(pin,0,false); - pca9685_pin_pwm_value[pin] = 0; - } - Response_P(PSTR("{\"PCA9685\":{\"RESET\":\"OK\"}}")); -} - -void PCA9685_SetPWMfreq(double freq) { - - - - - if (freq > 23 && freq < 1527) { - pca9685_freq=freq; - } else { - pca9685_freq=50; - } - uint8_t pre_scale_osc = round(25000000/(4096*pca9685_freq))-1; - if (1526 == pca9685_freq) pre_scale_osc=0xFF; - uint8_t current_mode1 = I2cRead8(USE_PCA9685_ADDR, PCA9685_REG_MODE1); - uint8_t sleep_mode1 = (current_mode1&0x7F) | 0x10; - I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, sleep_mode1); - I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_PRE_SCALE, pre_scale_osc); - I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, current_mode1 | 0xA0); -} - -void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off) { - uint8_t led_reg = PCA9685_REG_LED0_ON_L + 4 * pin; - uint32_t led_data = 0; - I2cWrite8(USE_PCA9685_ADDR, led_reg, on); - I2cWrite8(USE_PCA9685_ADDR, led_reg+1, (on >> 8)); - I2cWrite8(USE_PCA9685_ADDR, led_reg+2, off); - I2cWrite8(USE_PCA9685_ADDR, led_reg+3, (off >> 8)); -} - -void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted) { - if (4096 == pwm) { - PCA9685_SetPWM_Reg(pin, 4096, 0); - } else { - PCA9685_SetPWM_Reg(pin, 0, pwm); - } - pca9685_pin_pwm_value[pin] = pwm; -} - -bool PCA9685_Command(void) -{ - bool serviced = true; - bool validpin = false; - uint8_t paramcount = 0; - if (XdrvMailbox.data_len > 0) { - paramcount=1; - } else { - serviced = false; - return serviced; - } - char sub_string[XdrvMailbox.data_len]; - for (uint32_t ca=0;ca 1) { - uint16_t new_freq = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if ((new_freq >= 24) && (new_freq <= 1526)) { - PCA9685_SetPWMfreq(new_freq); - Response_P(PSTR("{\"PCA9685\":{\"PWMF\":%i, \"Result\":\"OK\"}}"),new_freq); - return serviced; - } - } else { - Response_P(PSTR("{\"PCA9685\":{\"PWMF\":%i}}"),pca9685_freq); - return serviced; - } - } - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"PWM")) { - if (paramcount > 1) { - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if (paramcount > 2) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "ON")) { - PCA9685_SetPWM(pin, 4096, false); - Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,4096); - serviced = true; - return serviced; - } - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "OFF")) { - PCA9685_SetPWM(pin, 0, false); - Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,0); - serviced = true; - return serviced; - } - uint16_t pwm = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - if ((pin >= 0 && pin <= 15) && (pwm >= 0 && pwm <= 4096)) { - PCA9685_SetPWM(pin, pwm, false); - Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,pwm); - serviced = true; - return serviced; - } - } - } - } - return serviced; -} - -void PCA9685_OutputTelemetry(bool telemetry) -{ - ResponseTime_P(PSTR(",\"PCA9685\":{\"PWM_FREQ\":%i,"),pca9685_freq); - for (uint32_t pin=0;pin<16;pin++) { - ResponseAppend_P(PSTR("\"PWM%i\":%i,"),pin,pca9685_pin_pwm_value[pin]); - } - ResponseAppend_P(PSTR("\"END\":1}}")); - if (telemetry) { - MqttPublishTeleSensor(); - } -} - -bool Xdrv15(uint8_t function) -{ - if (!I2cEnabled(XI2C_01)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - PCA9685_Detect(); - } - else if (pca9685_detected) { - switch (function) { - case FUNC_EVERY_SECOND: - if (tele_period == 0) { - PCA9685_OutputTelemetry(true); - } - break; - case FUNC_COMMAND_DRIVER: - if (XDRV_15 == XdrvMailbox.index) { - result = PCA9685_Command(); - } - break; - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_16_tuyamcu.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_16_tuyamcu.ino" -#ifdef USE_LIGHT -#ifdef USE_TUYA_MCU - -#define XDRV_16 16 -#define XNRG_16 16 - -#ifndef TUYA_DIMMER_ID -#define TUYA_DIMMER_ID 0 -#endif - -#define TUYA_CMD_HEARTBEAT 0x00 -#define TUYA_CMD_QUERY_PRODUCT 0x01 -#define TUYA_CMD_MCU_CONF 0x02 -#define TUYA_CMD_WIFI_STATE 0x03 -#define TUYA_CMD_WIFI_RESET 0x04 -#define TUYA_CMD_WIFI_SELECT 0x05 -#define TUYA_CMD_SET_DP 0x06 -#define TUYA_CMD_STATE 0x07 -#define TUYA_CMD_QUERY_STATE 0x08 - -#define TUYA_LOW_POWER_CMD_WIFI_STATE 0x02 -#define TUYA_LOW_POWER_CMD_WIFI_RESET 0x03 -#define TUYA_LOW_POWER_CMD_WIFI_CONFIG 0x04 -#define TUYA_LOW_POWER_CMD_STATE 0x05 - -#define TUYA_TYPE_BOOL 0x01 -#define TUYA_TYPE_VALUE 0x02 -#define TUYA_TYPE_STRING 0x03 -#define TUYA_TYPE_ENUM 0x04 - -#define TUYA_BUFFER_SIZE 256 - -#include - -TasmotaSerial *TuyaSerial = nullptr; - -struct TUYA { - uint16_t new_dim = 0; - bool ignore_dim = false; - uint8_t cmd_status = 0; - uint8_t cmd_checksum = 0; - uint8_t data_len = 0; - uint8_t wifi_state = -2; - uint8_t heartbeat_timer = 0; -#ifdef USE_ENERGY_SENSOR - uint32_t lastPowerCheckTime = 0; -#endif - char *buffer = nullptr; - int byte_counter = 0; - bool low_power_mode = false; - bool send_success_next_second = false; -} Tuya; - - -enum TuyaSupportedFunctions { - TUYA_MCU_FUNC_NONE, - TUYA_MCU_FUNC_SWT1 = 1, - TUYA_MCU_FUNC_SWT2, - TUYA_MCU_FUNC_SWT3, - TUYA_MCU_FUNC_SWT4, - TUYA_MCU_FUNC_REL1 = 11, - TUYA_MCU_FUNC_REL2, - TUYA_MCU_FUNC_REL3, - TUYA_MCU_FUNC_REL4, - TUYA_MCU_FUNC_REL5, - TUYA_MCU_FUNC_REL6, - TUYA_MCU_FUNC_REL7, - TUYA_MCU_FUNC_REL8, - TUYA_MCU_FUNC_DIMMER = 21, - TUYA_MCU_FUNC_POWER = 31, - TUYA_MCU_FUNC_CURRENT, - TUYA_MCU_FUNC_VOLTAGE, - TUYA_MCU_FUNC_BATTERY_STATE, - TUYA_MCU_FUNC_BATTERY_PERCENTAGE, - TUYA_MCU_FUNC_REL1_INV = 41, - TUYA_MCU_FUNC_REL2_INV, - TUYA_MCU_FUNC_REL3_INV, - TUYA_MCU_FUNC_REL4_INV, - TUYA_MCU_FUNC_REL5_INV, - TUYA_MCU_FUNC_REL6_INV, - TUYA_MCU_FUNC_REL7_INV, - TUYA_MCU_FUNC_REL8_INV, - TUYA_MCU_FUNC_LOWPOWER_MODE = 51, - TUYA_MCU_FUNC_LAST = 255 -}; - -const char kTuyaCommand[] PROGMEM = "|" - D_CMND_TUYA_MCU "|" D_CMND_TUYA_MCU_SEND_STATE; - -void (* const TuyaCommand[])(void) PROGMEM = { - &CmndTuyaMcu, &CmndTuyaSend -}; -# 126 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_16_tuyamcu.ino" -void CmndTuyaSend(void) { - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { - if (XdrvMailbox.data_len > 0) { - - char *p; - char *data; - uint8_t i = 0; - uint8_t dpId = 0; - for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) { - if ( i == 0) { - dpId = strtoul(str, nullptr, 0); - } else { - data = str; - } - i++; - } - - if (1 == XdrvMailbox.index) { - TuyaSendBool(dpId, strtoul(data, nullptr, 0)); - } else if (2 == XdrvMailbox.index) { - TuyaSendValue(dpId, strtoull(data, nullptr, 0)); - } else if (3 == XdrvMailbox.index) { - TuyaSendString(dpId, data); - } else if (4 == XdrvMailbox.index) { - TuyaSendEnum(dpId, strtoul(data, nullptr, 0)); - } - } - ResponseCmndDone(); - } -} - - - - - - - -void CmndTuyaMcu(void) { - if (XdrvMailbox.data_len > 0) { - char *p; - uint8_t i = 0; - uint8_t parm[3] = { 0 }; - for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 2; str = strtok_r(nullptr, ", ", &p)) { - parm[i] = strtoul(str, nullptr, 0); - i++; - } - - if (TuyaFuncIdValid(parm[0])) { - TuyaAddMcuFunc(parm[0], parm[1]); - restart_flag = 2; - } else { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("TYA: TuyaMcu Invalid function id=%d"), parm[0]); - } - - } - - Response_P(PSTR("{\"" D_CMND_TUYA_MCU "\":[")); - bool added = false; - for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { - if (Settings.tuya_fnid_map[i].fnid != 0) { - if (added) { - ResponseAppend_P(PSTR(",")); - } - ResponseAppend_P(PSTR("{\"fnId\":%d,\"dpId\":%d}" ), Settings.tuya_fnid_map[i].fnid, Settings.tuya_fnid_map[i].dpid); - added = true; - } - } - ResponseAppend_P(PSTR("]}")); -} - - - - - -void TuyaAddMcuFunc(uint8_t fnId, uint8_t dpId) { - bool added = false; - - if (fnId == 0 || dpId == 0) { - for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { - if ((dpId > 0 && Settings.tuya_fnid_map[i].dpid == dpId) || (fnId > TUYA_MCU_FUNC_NONE && Settings.tuya_fnid_map[i].fnid == fnId)) { - Settings.tuya_fnid_map[i].fnid = TUYA_MCU_FUNC_NONE; - Settings.tuya_fnid_map[i].dpid = 0; - break; - } - } - } else { - for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { - if (Settings.tuya_fnid_map[i].dpid == dpId || Settings.tuya_fnid_map[i].dpid == 0 || Settings.tuya_fnid_map[i].fnid == fnId || Settings.tuya_fnid_map[i].fnid == 0) { - if (!added) { - Settings.tuya_fnid_map[i].fnid = fnId; - Settings.tuya_fnid_map[i].dpid = dpId; - added = true; - } else if (Settings.tuya_fnid_map[i].dpid == dpId || Settings.tuya_fnid_map[i].fnid == fnId) { - Settings.tuya_fnid_map[i].fnid = TUYA_MCU_FUNC_NONE; - Settings.tuya_fnid_map[i].dpid = 0; - } - } - } - } - UpdateDevices(); -} - -void UpdateDevices() { - for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { - uint8_t fnId = Settings.tuya_fnid_map[i].fnid; - if (fnId > TUYA_MCU_FUNC_NONE && Settings.tuya_fnid_map[i].dpid > 0) { - - if (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) { - bitClear(rel_inverted, fnId - TUYA_MCU_FUNC_REL1); - } else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) { - bitSet(rel_inverted, fnId - TUYA_MCU_FUNC_REL1_INV); - } - - } - } -} - -inline bool TuyaFuncIdValid(uint8_t fnId) { - return (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) || - (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) || - fnId == TUYA_MCU_FUNC_DIMMER || - (fnId >= TUYA_MCU_FUNC_POWER && fnId <= TUYA_MCU_FUNC_VOLTAGE) || - (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) || - (fnId == TUYA_MCU_FUNC_LOWPOWER_MODE); -} - -uint8_t TuyaGetFuncId(uint8_t dpid) { - for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { - if (Settings.tuya_fnid_map[i].dpid == dpid) { - return Settings.tuya_fnid_map[i].fnid; - } - } - return TUYA_MCU_FUNC_NONE; -} - -uint8_t TuyaGetDpId(uint8_t fnId) { - for (uint8_t i = 0; i < MAX_TUYA_FUNCTIONS; i++) { - if (Settings.tuya_fnid_map[i].fnid == fnId) { - return Settings.tuya_fnid_map[i].dpid; - } - } - return 0; -} - -void TuyaSendCmd(uint8_t cmd, uint8_t payload[] = nullptr, uint16_t payload_len = 0) -{ - uint8_t checksum = (0xFF + cmd + (payload_len >> 8) + (payload_len & 0xFF)); - TuyaSerial->write(0x55); - TuyaSerial->write(0xAA); - TuyaSerial->write((uint8_t)0x00); - TuyaSerial->write(cmd); - TuyaSerial->write(payload_len >> 8); - TuyaSerial->write(payload_len & 0xFF); - snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Send \"55aa00%02x%02x%02x"), cmd, payload_len >> 8, payload_len & 0xFF); - for (uint32_t i = 0; i < payload_len; ++i) { - TuyaSerial->write(payload[i]); - checksum += payload[i]; - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, payload[i]); - } - TuyaSerial->write(checksum); - TuyaSerial->flush(); - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x\""), log_data, checksum); - AddLog(LOG_LEVEL_DEBUG); -} - -void TuyaSendState(uint8_t id, uint8_t type, uint8_t* value) -{ - uint16_t payload_len = 4; - uint8_t payload_buffer[8]; - payload_buffer[0] = id; - payload_buffer[1] = type; - switch (type) { - case TUYA_TYPE_BOOL: - case TUYA_TYPE_ENUM: - payload_len += 1; - payload_buffer[2] = 0x00; - payload_buffer[3] = 0x01; - payload_buffer[4] = value[0]; - break; - case TUYA_TYPE_VALUE: - payload_len += 4; - payload_buffer[2] = 0x00; - payload_buffer[3] = 0x04; - payload_buffer[4] = value[3]; - payload_buffer[5] = value[2]; - payload_buffer[6] = value[1]; - payload_buffer[7] = value[0]; - break; - - } - - TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); -} - -void TuyaSendBool(uint8_t id, bool value) -{ - TuyaSendState(id, TUYA_TYPE_BOOL, (uint8_t*)&value); -} - -void TuyaSendValue(uint8_t id, uint32_t value) -{ - TuyaSendState(id, TUYA_TYPE_VALUE, (uint8_t*)(&value)); -} - -void TuyaSendEnum(uint8_t id, uint32_t value) -{ - TuyaSendState(id, TUYA_TYPE_ENUM, (uint8_t*)(&value)); -} - -void TuyaSendString(uint8_t id, char data[]) { - - uint16_t len = strlen(data); - uint16_t payload_len = 4 + len; - uint8_t payload_buffer[payload_len]; - payload_buffer[0] = id; - payload_buffer[1] = TUYA_TYPE_STRING; - payload_buffer[2] = len >> 8; - payload_buffer[3] = len & 0xFF; - - for (uint16_t i = 0; i < len; i++) { - payload_buffer[4+i] = data[i]; - } - - TuyaSendCmd(TUYA_CMD_SET_DP, payload_buffer, payload_len); -} - -bool TuyaSetPower(void) -{ - bool status = false; - - uint8_t rpower = XdrvMailbox.index; - int16_t source = XdrvMailbox.payload; - - uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_REL1 + active_device - 1); - if (dpid == 0) dpid = TuyaGetDpId(TUYA_MCU_FUNC_REL1_INV + active_device - 1); - - if (source != SRC_SWITCH && TuyaSerial) { - TuyaSendBool(dpid, bitRead(rpower, active_device-1) ^ bitRead(rel_inverted, active_device-1)); - status = true; - } - return status; -} - -bool TuyaSetChannels(void) -{ - LightSerialDuty(((uint8_t*)XdrvMailbox.data)[0]); - delay(20); - return true; -} - -void LightSerialDuty(uint16_t duty) -{ - uint8_t dpid = TuyaGetDpId(TUYA_MCU_FUNC_DIMMER); - if (duty > 0 && !Tuya.ignore_dim && TuyaSerial && dpid > 0) { - duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max); - if (duty < Settings.dimmer_hw_min) { duty = Settings.dimmer_hw_min; } - if (Tuya.new_dim != duty) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim value=%d (id=%d)"), duty, dpid); - TuyaSendValue(dpid, duty); - } - } else if (dpid > 0) { - Tuya.ignore_dim = false; - duty = changeUIntScale(duty, 0, 255, 0, Settings.dimmer_hw_max); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Send dim skipped value=%d"), duty); - } else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Cannot set dimmer. Dimmer Id unknown")); - } -} - -void TuyaRequestState(void) -{ - if (TuyaSerial) { - - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Read MCU state")); - - TuyaSendCmd(TUYA_CMD_QUERY_STATE); - } -} - -void TuyaResetWifi(void) -{ - if (!Settings.flag.button_restrict) { - char scmnd[20]; - snprintf_P(scmnd, sizeof(scmnd), D_CMND_WIFICONFIG " %d", 2); - ExecuteCommand(scmnd, SRC_BUTTON); - } -} - -void TuyaProcessStatePacket(void) { - char scmnd[20]; - uint8_t dpidStart = 6; - uint8_t fnId; - uint16_t dpDataLen; - - while (dpidStart + 4 < Tuya.byte_counter) { - dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3]; - fnId = TuyaGetFuncId(Tuya.buffer[dpidStart]); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: fnId=%d is set for dpId=%d"), fnId, Tuya.buffer[dpidStart]); - - if (Tuya.buffer[dpidStart + 1] == 1) { - - if (fnId >= TUYA_MCU_FUNC_REL1 && fnId <= TUYA_MCU_FUNC_REL8) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[dpidStart + 4]?"On":"Off",bitRead(power, fnId - TUYA_MCU_FUNC_REL1)?"On":"Off"); - if ((power || Settings.light_dimmer > 0) && (Tuya.buffer[dpidStart + 4] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1))) { - ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1 + 1, Tuya.buffer[dpidStart + 4], SRC_SWITCH); - } - } else if (fnId >= TUYA_MCU_FUNC_REL1_INV && fnId <= TUYA_MCU_FUNC_REL8_INV) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Relay-%d-Inverted --> MCU State: %s Current State:%s"), fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[dpidStart + 4]?"Off":"On",bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1?"Off":"On"); - if (Tuya.buffer[dpidStart + 4] != bitRead(power, fnId - TUYA_MCU_FUNC_REL1_INV) ^ 1) { - ExecuteCommandPower(fnId - TUYA_MCU_FUNC_REL1_INV + 1, Tuya.buffer[dpidStart + 4] ^ 1, SRC_SWITCH); - } - } else if (fnId >= TUYA_MCU_FUNC_SWT1 && fnId <= TUYA_MCU_FUNC_SWT4) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Switch-%d --> MCU State: %d Current State:%d"),fnId - TUYA_MCU_FUNC_SWT1 + 1,Tuya.buffer[dpidStart + 4], SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1)); - - if (SwitchGetVirtual(fnId - TUYA_MCU_FUNC_SWT1) != Tuya.buffer[dpidStart + 4]) { - SwitchSetVirtual(fnId - TUYA_MCU_FUNC_SWT1, Tuya.buffer[dpidStart + 4]); - SwitchHandler(1); - } - } - - } - else if (Tuya.buffer[dpidStart + 1] == 2) { - bool tuya_energy_enabled = (XNRG_16 == energy_flg); - uint16_t packetValue = Tuya.buffer[dpidStart + 6] << 8 | Tuya.buffer[dpidStart + 7]; - if (fnId == TUYA_MCU_FUNC_DIMMER) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX Dim State=%d"), packetValue); - Tuya.new_dim = changeUIntScale(packetValue, 0, Settings.dimmer_hw_max, 0, 100); - if ((power || Settings.flag3.tuya_apply_o20) && - (Tuya.new_dim > 0) && (abs(Tuya.new_dim - Settings.light_dimmer) > 1)) { - Tuya.ignore_dim = true; - - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), Tuya.new_dim ); - ExecuteCommand(scmnd, SRC_SWITCH); - } - } - - #ifdef USE_ENERGY_SENSOR - else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_VOLTAGE) { - Energy.voltage[0] = (float)packetValue / 10; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Voltage=%d"), Tuya.buffer[dpidStart], packetValue); - } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_CURRENT) { - Energy.current[0] = (float)packetValue / 1000; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Current=%d"), Tuya.buffer[dpidStart], packetValue); - } else if (tuya_energy_enabled && fnId == TUYA_MCU_FUNC_POWER) { - Energy.active_power[0] = (float)packetValue / 10; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Rx ID=%d Active_Power=%d"), Tuya.buffer[dpidStart], packetValue); - - if (Tuya.lastPowerCheckTime != 0 && Energy.active_power[0] > 0) { - Energy.kWhtoday += (float)Energy.active_power[0] * (Rtc.utc_time - Tuya.lastPowerCheckTime) / 36; - EnergyUpdateToday(); - } - Tuya.lastPowerCheckTime = Rtc.utc_time; - } - #endif - - } - - - dpidStart += dpDataLen + 4; - } -} - -void TuyaLowPowerModePacketProcess(void) { - switch (Tuya.buffer[3]) { - case TUYA_CMD_QUERY_PRODUCT: - TuyaHandleProductInfoPacket(); - TuyaSetWifiLed(); - break; - - case TUYA_LOW_POWER_CMD_STATE: - TuyaProcessStatePacket(); - Tuya.send_success_next_second = true; - break; - } - -} - -void TuyaHandleProductInfoPacket(void) { - uint16_t dataLength = Tuya.buffer[4] << 8 | Tuya.buffer[5]; - char *data = &Tuya.buffer[6]; - AddLog_P2(LOG_LEVEL_INFO, PSTR("TYA: MCU Product ID: %.*s"), dataLength, data); -} - -void TuyaSendLowPowerSuccessIfNeeded(void) { - uint8_t success = 1; - - if (Tuya.send_success_next_second) { - TuyaSendCmd(TUYA_LOW_POWER_CMD_STATE, &success, 1); - Tuya.send_success_next_second = false; - } -} - -void TuyaNormalPowerModePacketProcess(void) -{ - switch (Tuya.buffer[3]) { - case TUYA_CMD_QUERY_PRODUCT: - TuyaHandleProductInfoPacket(); - TuyaSendCmd(TUYA_CMD_MCU_CONF); - break; - - case TUYA_CMD_HEARTBEAT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat")); - if (Tuya.buffer[6] == 0) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Detected MCU restart")); - Tuya.wifi_state = -2; - } - break; - - case TUYA_CMD_STATE: - TuyaProcessStatePacket(); - break; - - case TUYA_CMD_WIFI_RESET: - case TUYA_CMD_WIFI_SELECT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi Reset")); - TuyaResetWifi(); - break; - - case TUYA_CMD_WIFI_STATE: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX WiFi LED set ACK")); - Tuya.wifi_state = TuyaGetTuyaWifiState(); - break; - - case TUYA_CMD_MCU_CONF: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: RX MCU configuration Mode=%d"), Tuya.buffer[5]); - - if (Tuya.buffer[5] == 2) { - uint8_t led1_gpio = Tuya.buffer[6]; - uint8_t key1_gpio = Tuya.buffer[7]; - bool key1_set = false; - bool led1_set = false; - for (uint32_t i = 0; i < sizeof(Settings.my_gp); i++) { - if (Settings.my_gp.io[i] == GPIO_LED1) led1_set = true; - else if (Settings.my_gp.io[i] == GPIO_KEY1) key1_set = true; - } - if (!Settings.my_gp.io[led1_gpio] && !led1_set) { - Settings.my_gp.io[led1_gpio] = GPIO_LED1; - restart_flag = 2; - } - if (!Settings.my_gp.io[key1_gpio] && !key1_set) { - Settings.my_gp.io[key1_gpio] = GPIO_KEY1; - restart_flag = 2; - } - } - TuyaRequestState(); - break; - - default: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: RX unknown command")); - } -} - - - - - -bool TuyaModuleSelected(void) -{ - if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { - pin[GPIO_TUYA_TX] = 1; - pin[GPIO_TUYA_RX] = 3; - Settings.my_gp.io[1] = GPIO_TUYA_TX; - Settings.my_gp.io[3] = GPIO_TUYA_RX; - restart_flag = 2; - } - - if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) == 0 && TUYA_DIMMER_ID > 0) { - TuyaAddMcuFunc(TUYA_MCU_FUNC_DIMMER, TUYA_DIMMER_ID); - } - - bool relaySet = false; - - for (uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS; i++) { - if ((Settings.tuya_fnid_map[i].fnid >= TUYA_MCU_FUNC_REL1 && Settings.tuya_fnid_map[i].fnid <= TUYA_MCU_FUNC_REL8 ) || - (Settings.tuya_fnid_map[i].fnid >= TUYA_MCU_FUNC_REL1_INV && Settings.tuya_fnid_map[i].fnid <= TUYA_MCU_FUNC_REL8_INV )) { - relaySet = true; - devices_present++; - } - } - - if (!relaySet) { - TuyaAddMcuFunc(TUYA_MCU_FUNC_REL1, 1); - devices_present++; - SettingsSaveAll(); - } - - if (TuyaGetDpId(TUYA_MCU_FUNC_DIMMER) != 0) { - light_type = LT_SERIAL1; - } else { - light_type = LT_BASIC; - } - - if (TuyaGetDpId(TUYA_MCU_FUNC_LOWPOWER_MODE) != 0) { - Tuya.low_power_mode = true; - Settings.flag3.fast_power_cycle_disable = true; - } - - UpdateDevices(); - return true; -} - -void TuyaInit(void) -{ - Tuya.buffer = (char*)(malloc(TUYA_BUFFER_SIZE)); - if (Tuya.buffer != nullptr) { - TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 2); - if (TuyaSerial->begin(9600)) { - if (TuyaSerial->hardwareSerial()) { ClaimSerial(); } - - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Request MCU configuration")); - - TuyaSendCmd(TUYA_CMD_QUERY_PRODUCT); - } - } - Tuya.heartbeat_timer = 0; -} - -void TuyaSerialInput(void) -{ - while (TuyaSerial->available()) { - yield(); - uint8_t serial_in_byte = TuyaSerial->read(); - - if (serial_in_byte == 0x55) { - Tuya.cmd_status = 1; - Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; - Tuya.cmd_checksum += serial_in_byte; - } - else if (Tuya.cmd_status == 1 && serial_in_byte == 0xAA) { - Tuya.cmd_status = 2; - - Tuya.byte_counter = 0; - Tuya.buffer[Tuya.byte_counter++] = 0x55; - Tuya.buffer[Tuya.byte_counter++] = 0xAA; - Tuya.cmd_checksum = 0xFF; - } - else if (Tuya.cmd_status == 2) { - if (Tuya.byte_counter == 5) { - Tuya.cmd_status = 3; - Tuya.data_len = serial_in_byte; - } - Tuya.cmd_checksum += serial_in_byte; - Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; - } - else if ((Tuya.cmd_status == 3) && (Tuya.byte_counter == (6 + Tuya.data_len)) && (Tuya.cmd_checksum == serial_in_byte)) { - Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; - - char hex_char[(Tuya.byte_counter * 2) + 2]; - uint16_t len = Tuya.buffer[4] << 8 | Tuya.buffer[5]; - Response_P(PSTR("{\"" D_JSON_TUYA_MCU_RECEIVED "\":{\"Data\":\"%s\",\"Cmnd\":%d"), ToHex_P((unsigned char*)Tuya.buffer, Tuya.byte_counter, hex_char, sizeof(hex_char)), Tuya.buffer[3]); - - if (len > 0) { - ResponseAppend_P(PSTR(",\"CmndData\":\"%s\""), ToHex_P((unsigned char*)&Tuya.buffer[6], len, hex_char, sizeof(hex_char))); - if (TUYA_CMD_STATE == Tuya.buffer[3]) { - - - uint8_t dpidStart = 6; - while (dpidStart + 4 < Tuya.byte_counter) { - uint16_t dpDataLen = Tuya.buffer[dpidStart + 2] << 8 | Tuya.buffer[dpidStart + 3]; - ResponseAppend_P(PSTR(",\"%d\":{\"DpId\":%d,\"DpIdType\":%d,\"DpIdData\":\"%s\""), Tuya.buffer[dpidStart], Tuya.buffer[dpidStart], Tuya.buffer[dpidStart + 1], ToHex_P((unsigned char*)&Tuya.buffer[dpidStart + 4], dpDataLen, hex_char, sizeof(hex_char))); - if (TUYA_TYPE_STRING == Tuya.buffer[dpidStart + 1]) { - ResponseAppend_P(PSTR(",\"Type3Data\":\"%.*s\""), dpDataLen, (char *)&Tuya.buffer[dpidStart + 4]); - } - ResponseAppend_P(PSTR("}")); - dpidStart += dpDataLen + 4; - } - } - } - - ResponseAppend_P(PSTR("}}")); - - if (Settings.flag3.tuya_serial_mqtt_publish) { - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_TUYA_MCU_RECEIVED)); - } else { - AddLog_P(LOG_LEVEL_DEBUG, mqtt_data); - } - XdrvRulesProcess(); - - if (!Tuya.low_power_mode) { - TuyaNormalPowerModePacketProcess(); - } else { - TuyaLowPowerModePacketProcess(); - } - - Tuya.byte_counter = 0; - Tuya.cmd_status = 0; - Tuya.cmd_checksum = 0; - Tuya.data_len = 0; - } - else if (Tuya.byte_counter < TUYA_BUFFER_SIZE -1) { - Tuya.buffer[Tuya.byte_counter++] = serial_in_byte; - Tuya.cmd_checksum += serial_in_byte; - } else { - Tuya.byte_counter = 0; - Tuya.cmd_status = 0; - Tuya.cmd_checksum = 0; - Tuya.data_len = 0; - } - } -} - -bool TuyaButtonPressed(void) -{ - if (!XdrvMailbox.index && ((PRESSED == XdrvMailbox.payload) && (NOT_PRESSED == Button.last_state[XdrvMailbox.index]))) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Reset GPIO triggered")); - TuyaResetWifi(); - return true; - } - return false; -} - -uint8_t TuyaGetTuyaWifiState(void) { - - uint8_t wifi_state = 0x02; - switch(WifiState()){ - case WIFI_MANAGER: - wifi_state = 0x01; - break; - case WIFI_RESTART: - wifi_state = 0x03; - break; - } - - if (MqttIsConnected()) { - wifi_state = 0x04; - } - - return wifi_state; -} - -void TuyaSetWifiLed(void) -{ - Tuya.wifi_state = TuyaGetTuyaWifiState(); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("TYA: Set WiFi LED %d (%d)"), Tuya.wifi_state, WifiState()); - - if (Tuya.low_power_mode) { - TuyaSendCmd(TUYA_LOW_POWER_CMD_WIFI_STATE, &Tuya.wifi_state, 1); - } else { - TuyaSendCmd(TUYA_CMD_WIFI_STATE, &Tuya.wifi_state, 1); - } -} - -#ifdef USE_ENERGY_SENSOR - - - - -bool Xnrg16(uint8_t function) -{ - bool result = false; - - if (TUYA_DIMMER == my_module_type) { - if (FUNC_PRE_INIT == function) { - if (TuyaGetDpId(TUYA_MCU_FUNC_POWER) != 0) { - if (TuyaGetDpId(TUYA_MCU_FUNC_CURRENT) == 0) { - Energy.current_available = false; - } - if (TuyaGetDpId(TUYA_MCU_FUNC_VOLTAGE) == 0) { - Energy.voltage_available = false; - } - energy_flg = XNRG_16; - } - } - } - return result; -} -#endif - - - - - -bool Xdrv16(uint8_t function) -{ - bool result = false; - - if (TUYA_DIMMER == my_module_type) { - switch (function) { - case FUNC_LOOP: - if (TuyaSerial) { TuyaSerialInput(); } - break; - case FUNC_MODULE_INIT: - result = TuyaModuleSelected(); - break; - case FUNC_PRE_INIT: - TuyaInit(); - break; - case FUNC_SET_DEVICE_POWER: - result = TuyaSetPower(); - break; - case FUNC_BUTTON_PRESSED: - result = TuyaButtonPressed(); - break; - case FUNC_EVERY_SECOND: - if (TuyaSerial && Tuya.wifi_state != TuyaGetTuyaWifiState()) { TuyaSetWifiLed(); } - if (!Tuya.low_power_mode) { - Tuya.heartbeat_timer++; - if (Tuya.heartbeat_timer > 10) { - Tuya.heartbeat_timer = 0; - TuyaSendCmd(TUYA_CMD_HEARTBEAT); - } - } else { - TuyaSendLowPowerSuccessIfNeeded(); - } - break; - case FUNC_SET_CHANNELS: - result = TuyaSetChannels(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kTuyaCommand, TuyaCommand); - break; - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_17_rcswitch.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_17_rcswitch.ino" -#ifdef USE_RC_SWITCH - - - - -#define XDRV_17 17 - -#define D_JSON_RF_PROTOCOL "Protocol" -#define D_JSON_RF_BITS "Bits" -#define D_JSON_RF_DATA "Data" - -#define D_CMND_RFSEND "RFSend" -#define D_JSON_RF_PULSE "Pulse" -#define D_JSON_RF_REPEAT "Repeat" - -const char kRfSendCommands[] PROGMEM = "|" - D_CMND_RFSEND; - -void (* const RfSendCommand[])(void) PROGMEM = { - &CmndRfSend }; - -#include - -RCSwitch mySwitch = RCSwitch(); - -#define RF_TIME_AVOID_DUPLICATE 1000 - -uint32_t rf_lasttime = 0; - -void RfReceiveCheck(void) -{ - if (mySwitch.available()) { - - unsigned long data = mySwitch.getReceivedValue(); - unsigned int bits = mySwitch.getReceivedBitlength(); - int protocol = mySwitch.getReceivedProtocol(); - int delay = mySwitch.getReceivedDelay(); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFR: Data 0x%lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay); - - uint32_t now = millis(); - if ((now - rf_lasttime > RF_TIME_AVOID_DUPLICATE) && (data > 0)) { - rf_lasttime = now; - - char stemp[16]; - if (Settings.flag.rf_receive_decimal) { - snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)data); - } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"0x%lX\""), (uint32_t)data); - } - ResponseTime_P(PSTR(",\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_RF_DATA "\":%s,\"" D_JSON_RF_BITS "\":%d,\"" D_JSON_RF_PROTOCOL "\":%d,\"" D_JSON_RF_PULSE "\":%d}}"), - stemp, bits, protocol, delay); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED)); - XdrvRulesProcess(); -#ifdef USE_DOMOTICZ - DomoticzSensor(DZ_COUNT, data); -#endif - } - mySwitch.resetAvailable(); - } -} - -void RfInit(void) -{ - if (pin[GPIO_RFSEND] < 99) { - mySwitch.enableTransmit(pin[GPIO_RFSEND]); - } - if (pin[GPIO_RFRECV] < 99) { - pinMode( pin[GPIO_RFRECV], INPUT); - mySwitch.enableReceive(pin[GPIO_RFRECV]); - } -} - - - - - -void CmndRfSend(void) -{ - bool error = false; - - if (XdrvMailbox.data_len) { - unsigned long data = 0; - unsigned int bits = 24; - int protocol = 1; - int repeat = 10; - int pulse = 350; - - char dataBufUc[XdrvMailbox.data_len + 1]; - UpperCase(dataBufUc, XdrvMailbox.data); - StaticJsonBuffer<150> jsonBuf; - JsonObject &root = jsonBuf.parseObject(dataBufUc); - if (root.success()) { - - char parm_uc[10]; - data = strtoul(root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_DATA))], nullptr, 0); - bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_BITS))]; - protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PROTOCOL))]; - repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_REPEAT))]; - pulse = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PULSE))]; - } else { - - char *p; - uint8_t i = 0; - for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 5; str = strtok_r(nullptr, ", ", &p)) { - switch (i++) { - case 0: - data = strtoul(str, nullptr, 0); - break; - case 1: - bits = atoi(str); - break; - case 2: - protocol = atoi(str); - break; - case 3: - repeat = atoi(str); - break; - case 4: - pulse = atoi(str); - } - } - } - - if (!protocol) { protocol = 1; } - mySwitch.setProtocol(protocol); - if (!pulse) { pulse = 350; } - mySwitch.setPulseLength(pulse); - if (!repeat) { repeat = 10; } - mySwitch.setRepeatTransmit(repeat); - if (!bits) { bits = 24; } - if (data) { - mySwitch.send(data, bits); - ResponseCmndDone(); - } else { - error = true; - } - } else { - error = true; - } - if (error) { - Response_P(PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_NO " " D_JSON_RF_DATA ", " D_JSON_RF_BITS ", " D_JSON_RF_PROTOCOL ", " D_JSON_RF_REPEAT " " D_JSON_OR " " D_JSON_RF_PULSE "\"}")); - } -} - - - - - -bool Xdrv17(uint8_t function) -{ - bool result = false; - - if ((pin[GPIO_RFSEND] < 99) || (pin[GPIO_RFRECV] < 99)) { - switch (function) { - case FUNC_EVERY_50_MSECOND: - if (pin[GPIO_RFRECV] < 99) { - RfReceiveCheck(); - } - break; - case FUNC_COMMAND: - if (pin[GPIO_RFSEND] < 99) { - result = DecodeCommand(kRfSendCommands, RfSendCommand); - } - break; - case FUNC_INIT: - RfInit(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_18_armtronix_dimmers.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_18_armtronix_dimmers.ino" -#ifdef USE_LIGHT -#ifdef USE_ARMTRONIX_DIMMERS - - - - - - - -#define XDRV_18 18 - -#include - -TasmotaSerial *ArmtronixSerial = nullptr; - -struct ARMTRONIX { - bool ignore_dim = false; - int8_t wifi_state = -2; - int8_t dim_state[2]; - int8_t knob_state[2]; -} Armtronix; - - - - - -bool ArmtronixSetChannels(void) -{ - LightSerial2Duty(((uint8_t*)XdrvMailbox.data)[0], ((uint8_t*)XdrvMailbox.data)[1]); - return true; -} - -void LightSerial2Duty(uint8_t duty1, uint8_t duty2) -{ - if (ArmtronixSerial && !Armtronix.ignore_dim) { - duty1 = ((float)duty1)/2.575757; - duty2 = ((float)duty2)/2.575757; - Armtronix.dim_state[0] = duty1; - Armtronix.dim_state[1] = duty2; - ArmtronixSerial->print("Dimmer1:"); - ArmtronixSerial->print(duty1); - ArmtronixSerial->print("\nDimmer2:"); - ArmtronixSerial->println(duty2); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Serial Packet Dim Values=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]); - - } else { - Armtronix.ignore_dim = false; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send Dim Level skipped due to already set. Value=%d,%d"), Armtronix.dim_state[0],Armtronix.dim_state[1]); - - } -} - -void ArmtronixRequestState(void) -{ - if (ArmtronixSerial) { - - AddLog_P(LOG_LEVEL_DEBUG, PSTR("ARM: Request MCU state")); - ArmtronixSerial->println("Status"); - - } -} - - - - - -bool ArmtronixModuleSelected(void) -{ - devices_present++; - light_type = LT_SERIAL2; - return true; -} - -void ArmtronixInit(void) -{ - Armtronix.dim_state[0] = -1; - Armtronix.dim_state[1] = -1; - Armtronix.knob_state[0] = -1; - Armtronix.knob_state[1] = -1; - ArmtronixSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); - if (ArmtronixSerial->begin(115200)) { - if (ArmtronixSerial->hardwareSerial()) { ClaimSerial(); } - ArmtronixSerial->println("Status"); - } -} - -void ArmtronixSerialInput(void) -{ - String answer; - int8_t newDimState[2]; - uint8_t temp; - int commaIndex; - char scmnd[20]; - if (ArmtronixSerial->available()) { - yield(); - answer = ArmtronixSerial->readStringUntil('\n'); - if (answer.substring(0,7) == "Status:") { - commaIndex = 6; - for (uint32_t i =0; i<2; i++) { - newDimState[i] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); - if (newDimState[i] != Armtronix.dim_state[i]) { - temp = ((float)newDimState[i])*1.01010101010101; - Armtronix.dim_state[i] = newDimState[i]; - Armtronix.ignore_dim = true; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_CHANNEL "%d %d"),i+1, temp); - ExecuteCommand(scmnd,SRC_SWITCH); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Send CMND_CHANNEL=%s"), scmnd ); - } - commaIndex = answer.indexOf(',',commaIndex+1); - } - Armtronix.knob_state[0] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); - commaIndex = answer.indexOf(',',commaIndex+1); - Armtronix.knob_state[1] = answer.substring(commaIndex+1,answer.indexOf(',',commaIndex+1)).toInt(); - } - } -} - -void ArmtronixSetWifiLed(void) -{ - uint8_t wifi_state = 0x02; - - switch (WifiState()) { - case WIFI_MANAGER: - wifi_state = 0x01; - break; - case WIFI_RESTART: - wifi_state = 0x03; - break; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ARM: Set WiFi LED to state %d (%d)"), wifi_state, WifiState()); - - char state = '0' + ((wifi_state & 1) > 0); - ArmtronixSerial->print("Setled:"); - ArmtronixSerial->write(state); - ArmtronixSerial->write(','); - state = '0' + ((wifi_state & 2) > 0); - ArmtronixSerial->write(state); - ArmtronixSerial->write(10); - Armtronix.wifi_state = WifiState(); -} - - - - - -bool Xdrv18(uint8_t function) -{ - bool result = false; - - if (ARMTRONIX_DIMMERS == my_module_type) { - switch (function) { - case FUNC_LOOP: - if (ArmtronixSerial) { ArmtronixSerialInput(); } - break; - case FUNC_MODULE_INIT: - result = ArmtronixModuleSelected(); - break; - case FUNC_INIT: - ArmtronixInit(); - break; - case FUNC_EVERY_SECOND: - if (ArmtronixSerial) { - if (Armtronix.wifi_state!=WifiState()) { ArmtronixSetWifiLed(); } - if (uptime &1) { - ArmtronixSerial->println("Status"); - } - } - break; - case FUNC_SET_CHANNELS: - result = ArmtronixSetChannels(); - break; - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_19_ps16dz_dimmer.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_19_ps16dz_dimmer.ino" -#ifdef USE_LIGHT -#ifdef USE_PS_16_DZ - - - - -#define XDRV_19 19 - -#define PS16DZ_BUFFER_SIZE 80 - -#include - -TasmotaSerial *PS16DZSerial = nullptr; - -struct PS16DZ { - char *rx_buffer = nullptr; - int byte_counter = 0; - uint8_t dimmer = 0; -} Ps16dz; - - - - - -void PS16DZSerialSend(const char *tx_buffer) -{ - - - PS16DZSerial->print(tx_buffer); - PS16DZSerial->write(0x1B); - PS16DZSerial->flush(); -} - -void PS16DZSerialSendOk(void) -{ - char tx_buffer[16]; - snprintf_P(tx_buffer, sizeof(tx_buffer), PSTR("AT+SEND=ok")); - PS16DZSerialSend(tx_buffer); -} - - - - -void PS16DZSerialSendUpdateCommand(void) -{ - uint8_t light_state_dimmer = light_state.getDimmer(); - - light_state_dimmer = (light_state_dimmer < Settings.dimmer_hw_min) ? Settings.dimmer_hw_min : light_state_dimmer; - light_state_dimmer = (light_state_dimmer > Settings.dimmer_hw_max) ? Settings.dimmer_hw_max : light_state_dimmer; - - char tx_buffer[80]; - snprintf_P(tx_buffer, sizeof(tx_buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"bright\":%d"), - LocalTime(), millis()%1000, power?"on":"off", light_state_dimmer); - - PS16DZSerialSend(tx_buffer); -} - - - - - -void PS16DZSerialInput(void) -{ - char scmnd[20]; - while (PS16DZSerial->available()) { - yield(); - uint8_t serial_in_byte = PS16DZSerial->read(); - if (serial_in_byte != 0x1B) { - if (Ps16dz.byte_counter >= PS16DZ_BUFFER_SIZE - 1) { - memset(Ps16dz.rx_buffer, 0, PS16DZ_BUFFER_SIZE); - Ps16dz.byte_counter = 0; - } - if (Ps16dz.byte_counter || (!Ps16dz.byte_counter && ('A' == serial_in_byte))) { - Ps16dz.rx_buffer[Ps16dz.byte_counter++] = serial_in_byte; - } - } else { - Ps16dz.rx_buffer[Ps16dz.byte_counter++] = 0x00; - - - - - if (!strncmp(Ps16dz.rx_buffer+3, "RESULT", 6)) { - - } - else if (!strncmp(Ps16dz.rx_buffer+3, "UPDATE", 6)) { - - char *end_str; - char *string = Ps16dz.rx_buffer+10; - char *token = strtok_r(string, ",", &end_str); - - bool is_switch_change = false; - bool is_brightness_change = false; - - while (token != nullptr) { - char* end_token; - char* token2 = strtok_r(token, ":", &end_token); - char* token3 = strtok_r(nullptr, ":", &end_token); - - if (!strncmp(token2, "\"switch\"", 8)) { - bool switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; - - - - is_switch_change = (switch_state != power); - if (is_switch_change) { - ExecuteCommandPower(1, switch_state, SRC_SWITCH); - } - } - else if (!strncmp(token2, "\"bright\"", 8)) { - Ps16dz.dimmer = atoi(token3); - - - - is_brightness_change = Ps16dz.dimmer != Settings.light_dimmer; - if (power && (Ps16dz.dimmer > 0) && is_brightness_change) { - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), Ps16dz.dimmer); - ExecuteCommand(scmnd, SRC_SWITCH); - } - } - else if (!strncmp(token2, "\"sequence\"", 10)) { - - - - } - token = strtok_r(nullptr, ",", &end_str); - } - - if (!is_brightness_change) { - - - - PS16DZSerialSendOk(); - } - } - else if (!strncmp(Ps16dz.rx_buffer+3, "SETTING", 7)) { - - - if (!Settings.flag.button_restrict) { - int state = WIFI_MANAGER; - if (!strncmp(Ps16dz.rx_buffer+10, "=exit", 5)) { state = WIFI_RETRY; } - if (state != Settings.sta_config) { - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_WIFICONFIG " %d"), state); - ExecuteCommand(scmnd, SRC_BUTTON); - } - } - } - memset(Ps16dz.rx_buffer, 0, PS16DZ_BUFFER_SIZE); - Ps16dz.byte_counter = 0; - } - } -} - -bool PS16DZSerialSendUpdateCommandIfRequired(void) -{ - if (!PS16DZSerial) { return true; } - - bool is_switch_change = (XdrvMailbox.payload != SRC_SWITCH); - bool is_brightness_change = (light_state.getDimmer() != Ps16dz.dimmer); - - if (is_switch_change || is_brightness_change) { - PS16DZSerialSendUpdateCommand(); - } - - return true; -} - -void PS16DZInit(void) -{ - Ps16dz.rx_buffer = (char*)(malloc(PS16DZ_BUFFER_SIZE)); - if (Ps16dz.rx_buffer != nullptr) { - PS16DZSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); - if (PS16DZSerial->begin(19200)) { - if (PS16DZSerial->hardwareSerial()) { ClaimSerial(); } - } - } -} - -bool PS16DZModuleSelected(void) -{ - devices_present++; - light_type = LT_SERIAL1; - - return true; -} - - - - - -bool Xdrv19(uint8_t function) -{ - bool result = false; - - if (PS_16_DZ == my_module_type) { - switch (function) { - case FUNC_LOOP: - if (PS16DZSerial) { PS16DZSerialInput(); } - break; - case FUNC_SET_DEVICE_POWER: - case FUNC_SET_CHANNELS: - result = PS16DZSerialSendUpdateCommandIfRequired(); - break; - case FUNC_INIT: - PS16DZInit(); - break; - case FUNC_MODULE_INIT: - result = PS16DZModuleSelected(); - break; - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_20_hue.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_20_hue.ino" -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined(USE_EMULATION_HUE) && defined(USE_LIGHT) -# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_20_hue.ino" -#define XDRV_20 20 - -const char HUE_RESPONSE[] PROGMEM = - "HTTP/1.1 200 OK\r\n" - "HOST: 239.255.255.250:1900\r\n" - "CACHE-CONTROL: max-age=100\r\n" - "EXT:\r\n" - "LOCATION: http://%s:80/description.xml\r\n" - "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.24.0\r\n" - "hue-bridgeid: %s\r\n"; -const char HUE_ST1[] PROGMEM = - "ST: upnp:rootdevice\r\n" - "USN: uuid:%s::upnp:rootdevice\r\n" - "\r\n"; -const char HUE_ST2[] PROGMEM = - "ST: uuid:%s\r\n" - "USN: uuid:%s\r\n" - "\r\n"; -const char HUE_ST3[] PROGMEM = - "ST: urn:schemas-upnp-org:device:basic:1\r\n" - "USN: uuid:%s\r\n" - "\r\n"; - -String HueBridgeId(void) -{ - String temp = WiFi.macAddress(); - temp.replace(":", ""); - String bridgeid = temp.substring(0, 6) + "FFFE" + temp.substring(6); - return bridgeid; -} - -String HueSerialnumber(void) -{ - String serial = WiFi.macAddress(); - serial.replace(":", ""); - serial.toLowerCase(); - return serial; -} - -String HueUuid(void) -{ - String uuid = F("f6543a06-da50-11ba-8d8f-"); - uuid += HueSerialnumber(); - return uuid; -} - -void HueRespondToMSearch(void) -{ - char message[TOPSZ]; - - TickerMSearch.detach(); - if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) { - char response[320]; - snprintf_P(response, sizeof(response), HUE_RESPONSE, WiFi.localIP().toString().c_str(), HueBridgeId().c_str()); - int len = strlen(response); - - snprintf_P(response + len, sizeof(response) - len, HUE_ST1, HueUuid().c_str()); - PortUdp.write(response); - PortUdp.endPacket(); - - snprintf_P(response + len, sizeof(response) - len, HUE_ST2, HueUuid().c_str(), HueUuid().c_str()); - PortUdp.write(response); - PortUdp.endPacket(); - - snprintf_P(response + len, sizeof(response) - len, HUE_ST3, HueUuid().c_str()); - PortUdp.write(response); - PortUdp.endPacket(); - - snprintf_P(message, sizeof(message), PSTR(D_3_RESPONSE_PACKETS_SENT)); - } else { - snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE)); - } - - PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_HUE " %s " D_TO " %s:%d"), - message, udp_remote_ip.toString().c_str(), udp_remote_port); - - udp_response_mutex = false; -} - - - - - -const char HUE_DESCRIPTION_XML[] PROGMEM = - "" - "" - "" - "1" - "0" - "" - - "http://{x1:80/" - "" - "urn:schemas-upnp-org:device:Basic:1" - "Amazon-Echo-HA-Bridge ({x1)" - - "Royal Philips Electronics" - "http://www.philips.com" - "Philips hue Personal Wireless Lighting" - "Philips hue bridge 2012" - "929000226503" - "{x3" - "uuid:{x2" - "" - "\r\n" - "\r\n"; -const char HUE_LIGHTS_STATUS_JSON1[] PROGMEM = - "{\"on\":{state}," - "{light_status}" - "\"alert\":\"none\"," - "\"effect\":\"none\"," - "\"reachable\":true}"; -const char HUE_LIGHTS_STATUS_JSON2[] PROGMEM = - ",\"type\":\"Extended color light\"," - "\"name\":\"{j1\"," - "\"modelid\":\"LCT007\"," - "\"uniqueid\":\"{j2\"," - "\"swversion\":\"5.50.1.19085\"}"; -const char HUE_GROUP0_STATUS_JSON[] PROGMEM = - "{\"name\":\"Group 0\"," - "\"lights\":[{l1]," - "\"type\":\"LightGroup\"," - "\"action\":"; - -const char HueConfigResponse_JSON[] PROGMEM = - "{\"name\":\"Philips hue\"," - "\"mac\":\"{ma\"," - "\"dhcp\":true," - "\"ipaddress\":\"{ip\"," - "\"netmask\":\"{ms\"," - "\"gateway\":\"{gw\"," - "\"proxyaddress\":\"none\"," - "\"proxyport\":0," - "\"bridgeid\":\"{br\"," - "\"UTC\":\"{dt\"," - "\"whitelist\":{\"{id\":{" - "\"last use date\":\"{dt\"," - "\"create date\":\"{dt\"," - "\"name\":\"Remote\"}}," - "\"swversion\":\"01041302\"," - "\"apiversion\":\"1.17.0\"," - "\"swupdate\":{\"updatestate\":0,\"url\":\"\",\"text\":\"\",\"notify\": false}," - "\"linkbutton\":false," - "\"portalservices\":false" - "}"; -const char HUE_LIGHT_RESPONSE_JSON[] PROGMEM = - "{\"success\":{\"/lights/{id/state/{cm\":{re}}"; -const char HUE_ERROR_JSON[] PROGMEM = - "[{\"error\":{\"type\":901,\"address\":\"/\",\"description\":\"Internal Error\"}}]"; - - - -String GetHueDeviceId(uint8_t id) -{ - String deviceid = WiFi.macAddress() + F(":00:11-") + String(id); - deviceid.toLowerCase(); - return deviceid; -} - -String GetHueUserId(void) -{ - char userid[7]; - - snprintf_P(userid, sizeof(userid), PSTR("%03x"), ESP.getChipId()); - return String(userid); -} - -void HandleUpnpSetupHue(void) -{ - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_HUE_BRIDGE_SETUP)); - String description_xml = FPSTR(HUE_DESCRIPTION_XML); - description_xml.replace("{x1", WiFi.localIP().toString()); - description_xml.replace("{x2", HueUuid()); - description_xml.replace("{x3", HueSerialnumber()); - WSSend(200, CT_XML, description_xml); -} - -void HueNotImplemented(String *path) -{ - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API_NOT_IMPLEMENTED " (%s)"), path->c_str()); - - WSSend(200, CT_JSON, "{}"); -} - -void HueConfigResponse(String *response) -{ - *response += FPSTR(HueConfigResponse_JSON); - response->replace("{ma", WiFi.macAddress()); - response->replace("{ip", WiFi.localIP().toString()); - response->replace("{ms", WiFi.subnetMask().toString()); - response->replace("{gw", WiFi.gatewayIP().toString()); - response->replace("{br", HueBridgeId()); - response->replace("{dt", GetDateAndTime(DT_UTC)); - response->replace("{id", GetHueUserId()); -} - -void HueConfig(String *path) -{ - String response = ""; - HueConfigResponse(&response); - WSSend(200, CT_JSON, response); -} - - - -bool g_gotct = false; - - - - -uint16_t prev_hue = 0; -uint8_t prev_sat = 0; -uint8_t prev_bri = 254; -uint16_t prev_ct = 254; -char prev_x_str[24] = "\0"; -char prev_y_str[24] = "\0"; - -uint8_t getLocalLightSubtype(uint8_t device) { - if (light_type) { - if (device >= Light.device) { - if (Settings.flag3.pwm_multi_channels) { - return LST_SINGLE; - } else { - return Light.subtype; - } - } else { - return LST_NONE; - } - } else { - return LST_NONE; - } -} - -void HueLightStatus1(uint8_t device, String *response) -{ - uint16_t ct = 0; - uint8_t color_mode; - String light_status = ""; - uint16_t hue = 0; - uint8_t sat = 0; - uint8_t bri = 254; - uint32_t echo_gen = findEchoGeneration(); - - - uint8_t local_light_subtype = getLocalLightSubtype(device); - - bri = LightGetBri(device); - if (bri > 254) bri = 254; - if (bri < 1) bri = 1; - -#ifdef USE_SHUTTER - if (ShutterState(device)) { - bri = (float)((Settings.shutter_options[device-1] & 1) ? 100 - Settings.shutter_position[device-1] : Settings.shutter_position[device-1]) / 100; - } -#endif - - if (light_type) { - light_state.getHSB(&hue, &sat, nullptr); - - if ((bri > prev_bri ? bri - prev_bri : prev_bri - bri) < 1) - bri = prev_bri; - - if (sat > 254) sat = 254; - if ((sat > prev_sat ? sat - prev_sat : prev_sat - sat) < 1) { - sat = prev_sat; - } else { - prev_x_str[0] = prev_y_str[0] = 0; - } - - hue = changeUIntScale(hue, 0, 359, 0, 65535); - if ((hue > prev_hue ? hue - prev_hue : prev_hue - hue) < 400) { - hue = prev_hue; - } else { - prev_x_str[0] = prev_y_str[0] = 0; - } - - color_mode = light_state.getColorMode(); - ct = light_state.getCT(); - if (LCM_RGB == color_mode) { g_gotct = false; } - if (LCM_CT == color_mode) { g_gotct = true; } - - - - if ((ct > prev_ct ? ct - prev_ct : prev_ct - ct) < 1) - ct = prev_ct; - - - - } - - *response += FPSTR(HUE_LIGHTS_STATUS_JSON1); - response->replace("{state}", (power & (1 << (device-1))) ? "true" : "false"); - - if ((1 == echo_gen) || (LST_SINGLE <= local_light_subtype)) { - light_status += "\"bri\":"; - light_status += String(bri); - light_status += ","; - } - if (LST_COLDWARM <= local_light_subtype) { - light_status += F("\"colormode\":\""); - light_status += (g_gotct ? "ct" : "hs"); - light_status += "\","; - } - if (LST_RGB <= local_light_subtype) { - if (prev_x_str[0] && prev_y_str[0]) { - light_status += "\"xy\":["; - light_status += prev_x_str; - light_status += ","; - light_status += prev_y_str; - light_status += "],"; - } else { - float x, y; - light_state.getXY(&x, &y); - light_status += "\"xy\":["; - light_status += String(x, 5); - light_status += ","; - light_status += String(y, 5); - light_status += "],"; - } - light_status += "\"hue\":"; - light_status += String(hue); - light_status += ","; - - light_status += "\"sat\":"; - light_status += String(sat); - light_status += ","; - } - if (LST_COLDWARM == local_light_subtype || LST_RGBW <= local_light_subtype) { - light_status += "\"ct\":"; - light_status += String(ct > 0 ? ct : 284); - light_status += ","; - } - response->replace("{light_status}", light_status); -} - - - -bool HueActive(uint8_t device) { - if (device > MAX_FRIENDLYNAMES) { device = MAX_FRIENDLYNAMES; } - return '$' != *SettingsText(SET_FRIENDLYNAME1 +device -1); -} - -void HueLightStatus2(uint8_t device, String *response) -{ - *response += FPSTR(HUE_LIGHTS_STATUS_JSON2); - if (device <= MAX_FRIENDLYNAMES) { - response->replace("{j1", SettingsText(SET_FRIENDLYNAME1 +device -1)); - } else { - char fname[33]; - strcpy(fname, SettingsText(SET_FRIENDLYNAME1 + MAX_FRIENDLYNAMES -1)); - uint32_t fname_len = strlen(fname); - if (fname_len > 30) { fname_len = 30; } - fname[fname_len++] = '-'; - if (device - MAX_FRIENDLYNAMES < 10) { - fname[fname_len++] = '0' + device - MAX_FRIENDLYNAMES; - } else { - fname[fname_len++] = 'A' + device - MAX_FRIENDLYNAMES - 10; - } - fname[fname_len] = 0x00; - - response->replace("{j1", fname); - } - response->replace("{j2", GetHueDeviceId(device)); -} - - - - -uint32_t EncodeLightId(uint8_t relay_id) -{ - uint8_t mac[6]; - WiFi.macAddress(mac); - uint32_t id = 0; - - if (relay_id >= 32) { - relay_id = 0; - } - if (relay_id > 15) { - id = (1 << 28); - } - - id |= (mac[3] << 20) | (mac[4] << 12) | (mac[5] << 4) | (relay_id & 0xF); - return id; -} - - - -uint32_t DecodeLightId(uint32_t hue_id) { - uint8_t relay_id = hue_id & 0xF; - if (hue_id & (1 << 28)) { - relay_id += 16; - } - if (0 == relay_id) { - relay_id = 32; - } - return relay_id; -} - -static const char * FIRST_GEN_UA[] = { - "AEOBC", -}; - - -uint32_t findEchoGeneration(void) { - - String user_agent = WebServer->header("User-Agent"); - uint32_t gen = 2; - - for (uint32_t i = 0; i < sizeof(FIRST_GEN_UA)/sizeof(char*); i++) { - if (user_agent.indexOf(FIRST_GEN_UA[i]) >= 0) { - gen = 1; - break; - } - } - if (0 == user_agent.length()) { - gen = 1; - } - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, D_LOG_HTTP D_HUE " User-Agent: %s, gen=%d", user_agent.c_str(), gen); - - return gen; -} - -void HueGlobalConfig(String *path) { - String response; - uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; - - path->remove(0,1); - response = F("{\"lights\":{"); - bool appending = false; - for (uint32_t i = 1; i <= maxhue; i++) { - if (HueActive(i)) { - if (appending) { response += ","; } - response += "\""; - response += EncodeLightId(i); - response += F("\":{\"state\":"); - HueLightStatus1(i, &response); - HueLightStatus2(i, &response); - appending = true; - } - } - response += F("},\"groups\":{},\"schedules\":{},\"config\":"); - HueConfigResponse(&response); - response += "}"; - WSSend(200, CT_JSON, response); -} - -void HueAuthentication(String *path) -{ - char response[38]; - - snprintf_P(response, sizeof(response), PSTR("[{\"success\":{\"username\":\"%s\"}}]"), GetHueUserId().c_str()); - WSSend(200, CT_JSON, response); -} - -void HueLights(String *path) -{ - - - - String response; - int code = 200; - uint16_t tmp = 0; - uint16_t hue = 0; - uint8_t sat = 0; - uint8_t bri = 254; - uint16_t ct = 0; - bool resp = false; - bool on = false; - bool change = false; - uint8_t device = 1; - uint8_t local_light_subtype = Light.subtype; - uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; - - path->remove(0,path->indexOf("/lights")); - if (path->endsWith("/lights")) { - response = "{"; - bool appending = false; - for (uint32_t i = 1; i <= maxhue; i++) { - if (HueActive(i)) { - if (appending) { response += ","; } - response += "\""; - response += EncodeLightId(i); - response += F("\":{\"state\":"); - HueLightStatus1(i, &response); - HueLightStatus2(i, &response); - appending = true; - } - } -#ifdef USE_SCRIPT_HUE - Script_Check_Hue(&response); -#endif - response += "}"; - } - else if (path->endsWith("/state")) { - path->remove(0,8); - path->remove(path->indexOf("/state")); - device = DecodeLightId(atoi(path->c_str())); - -#ifdef USE_SCRIPT_HUE - if (device>devices_present) { - return Script_Handle_Hue(path); - } -#endif - - if ((device < 1) || (device > maxhue)) { - device = 1; - } - local_light_subtype = getLocalLightSubtype(device); - - if (WebServer->args()) { - response = "["; - - StaticJsonBuffer<400> jsonBuffer; - JsonObject &hue_json = jsonBuffer.parseObject(WebServer->arg((WebServer->args())-1)); - if (hue_json.containsKey("on")) { - - response += FPSTR(HUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(EncodeLightId(device))); - response.replace("{cm", "on"); - -#ifdef USE_SHUTTER - if (ShutterState(device)) { - if (!change) { - on = hue_json["on"]; - bri = on ? 1.0f : 0.0f; - change = true; - } - response.replace("{re", on ? "true" : "false"); - } else { -#endif - on = hue_json["on"]; - switch(on) - { - case false : ExecuteCommandPower(device, POWER_OFF, SRC_HUE); - response.replace("{re", "false"); - break; - case true : ExecuteCommandPower(device, POWER_ON, SRC_HUE); - response.replace("{re", "true"); - break; - default : response.replace("{re", (power & (1 << (device-1))) ? "true" : "false"); - break; - } - resp = true; -#ifdef USE_SHUTTER - } -#endif - } - - if (light_type && (local_light_subtype >= LST_SINGLE)) { - if (!Settings.flag3.pwm_multi_channels) { - light_state.getHSB(&hue, &sat, nullptr); - bri = light_state.getBri(); - ct = light_state.getCT(); - uint8_t color_mode = light_state.getColorMode(); - if (LCM_RGB == color_mode) { g_gotct = false; } - if (LCM_CT == color_mode) { g_gotct = true; } - - } else { - bri = LightGetBri(device); - } - } - prev_x_str[0] = prev_y_str[0] = 0; - - if (hue_json.containsKey("bri")) { - tmp = hue_json["bri"]; - prev_bri = bri = tmp; - - if (254 <= bri) { bri = 255; } - if (resp) { response += ","; } - response += FPSTR(HUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(device)); - response.replace("{cm", "bri"); - response.replace("{re", String(tmp)); - if (LST_SINGLE <= Light.subtype) { - change = true; - } - resp = true; - } - - - if (hue_json.containsKey("xy")) { - float x, y; - x = hue_json["xy"][0]; - y = hue_json["xy"][1]; - const String &x_str = hue_json["xy"][0]; - const String &y_str = hue_json["xy"][1]; - x_str.toCharArray(prev_x_str, sizeof(prev_x_str)); - y_str.toCharArray(prev_y_str, sizeof(prev_y_str)); - - uint8_t rr,gg,bb; - LightStateClass::XyToRgb(x, y, &rr, &gg, &bb); - LightStateClass::RgbToHsb(rr, gg, bb, &hue, &sat, nullptr); - prev_hue = changeUIntScale(hue, 0, 359, 0, 65535); - prev_sat = (sat > 254 ? 254 : sat); - - if (resp) { response += ","; } - response += FPSTR(HUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(device)); - response.replace("{cm", "xy"); - response.replace("{re", "[" + x_str + "," + y_str + "]"); - g_gotct = false; - resp = true; - change = true; - } - if (hue_json.containsKey("hue")) { - tmp = hue_json["hue"]; - prev_hue = tmp; - - hue = changeUIntScale(tmp, 0, 65535, 0, 359); - if (resp) { response += ","; } - response += FPSTR(HUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(device)); - response.replace("{cm", "hue"); - response.replace("{re", String(tmp)); - if (LST_RGB <= Light.subtype) { - g_gotct = false; - change = true; - } - resp = true; - } - if (hue_json.containsKey("sat")) { - tmp = hue_json["sat"]; - prev_sat = sat = tmp; - - if (254 <= sat) { sat = 255; } - if (resp) { response += ","; } - response += FPSTR(HUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(device)); - response.replace("{cm", "sat"); - response.replace("{re", String(tmp)); - if (LST_RGB <= Light.subtype) { - g_gotct = false; - change = true; - } - resp = true; - } - if (hue_json.containsKey("ct")) { - ct = hue_json["ct"]; - prev_ct = ct; - if (resp) { response += ","; } - response += FPSTR(HUE_LIGHT_RESPONSE_JSON); - response.replace("{id", String(device)); - response.replace("{cm", "ct"); - response.replace("{re", String(ct)); - if ((LST_COLDWARM == Light.subtype) || (LST_RGBW <= Light.subtype)) { - g_gotct = true; - change = true; - } - resp = true; - } - if (change) { -#ifdef USE_SHUTTER - if (ShutterState(device)) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Settings.shutter_invert: %d"), Settings.shutter_options[device-1] & 1); - ShutterSetPosition(device, bri * 100.0f ); - } else -#endif - if (light_type && (local_light_subtype > LST_NONE)) { - if (!Settings.flag3.pwm_multi_channels) { - if (g_gotct) { - light_controller.changeCTB(ct, bri); - } else { - light_controller.changeHSB(hue, sat, bri); - } - LightPreparePower(); - } else { - LightSetBri(device, bri); - } - if (LST_COLDWARM <= local_light_subtype) { - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_COLOR)); - } else { - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_DIMMER)); - } - XdrvRulesProcess(); - } - change = false; - } - response += "]"; - if (2 == response.length()) { - response = FPSTR(HUE_ERROR_JSON); - } - } - else { - response = FPSTR(HUE_ERROR_JSON); - } - } - else if(path->indexOf("/lights/") >= 0) { - AddLog_P2(LOG_LEVEL_DEBUG_MORE, "/lights path=%s", path->c_str()); - path->remove(0,8); - device = DecodeLightId(atoi(path->c_str())); - -#ifdef USE_SCRIPT_HUE - if (device>devices_present) { - Script_HueStatus(&response,device-devices_present-1); - goto exit; -} -#endif - - if ((device < 1) || (device > maxhue)) { - device = 1; - } - response += F("{\"state\":"); - HueLightStatus1(device, &response); - HueLightStatus2(device, &response); - } - else { - response = "{}"; - code = 406; - } - exit: - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE " Result (%s)"), response.c_str()); - WSSend(code, CT_JSON, response); -} - -void HueGroups(String *path) -{ - - - - String response = "{}"; - uint8_t maxhue = (devices_present > MAX_HUE_DEVICES) ? MAX_HUE_DEVICES : devices_present; - - if (path->endsWith("/0")) { - response = FPSTR(HUE_GROUP0_STATUS_JSON); - String lights = F("\"1\""); - for (uint32_t i = 2; i <= maxhue; i++) { - lights += ",\""; - lights += EncodeLightId(i); - lights += "\""; - } - response.replace("{l1", lights); - HueLightStatus1(1, &response); - response += F("}"); - } - - WSSend(200, CT_JSON, response); -} - -void HandleHueApi(String *path) -{ -# 784 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_20_hue.ino" - uint8_t args = 0; - - path->remove(0, 4); - uint16_t apilen = path->length(); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_API " (%s)"), path->c_str()); - for (args = 0; args < WebServer->args(); args++) { - String json = WebServer->arg(args); - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_HTTP D_HUE_POST_ARGS " (%s)"), json.c_str()); - } - - if (path->endsWith("/invalid/")) {} - else if (!apilen) HueAuthentication(path); - else if (path->endsWith("/")) HueAuthentication(path); - else if (path->endsWith("/config")) HueConfig(path); - else if (path->indexOf("/lights") >= 0) HueLights(path); - else if (path->indexOf("/groups") >= 0) HueGroups(path); - else if (path->endsWith("/schedules")) HueNotImplemented(path); - else if (path->endsWith("/sensors")) HueNotImplemented(path); - else if (path->endsWith("/scenes")) HueNotImplemented(path); - else if (path->endsWith("/rules")) HueNotImplemented(path); - else if (path->endsWith("/resourcelinks")) HueNotImplemented(path); - else HueGlobalConfig(path); -} - - - - - -bool Xdrv20(uint8_t function) -{ - bool result = false; - -#ifdef USE_SCRIPT_HUE - if ((EMUL_HUE == Settings.flag2.emulation)) { -#else - if (devices_present && (EMUL_HUE == Settings.flag2.emulation)) { -#endif - switch (function) { - case FUNC_WEB_ADD_HANDLER: - WebServer->on("/description.xml", HandleUpnpSetupHue); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_21_wemo.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_21_wemo.ino" -#if defined(USE_WEBSERVER) && defined(USE_EMULATION) && defined (USE_EMULATION_WEMO) - - - - -#define XDRV_21 21 - -const char WEMO_MSEARCH[] PROGMEM = - "HTTP/1.1 200 OK\r\n" - "CACHE-CONTROL: max-age=86400\r\n" - "DATE: Fri, 15 Apr 2016 04:56:29 GMT\r\n" - "EXT:\r\n" - "LOCATION: http://%s:80/setup.xml\r\n" - "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" - "01-NLS: b9200ebb-736d-4b93-bf03-835149d13983\r\n" - "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n" - "ST: %s\r\n" - "USN: uuid:%s::%s\r\n" - "X-User-Agent: redsonic\r\n" - "\r\n"; - -String WemoSerialnumber(void) -{ - char serial[16]; - - snprintf_P(serial, sizeof(serial), PSTR("201612K%08X"), ESP.getChipId()); - return String(serial); -} - -String WemoUuid(void) -{ - char uuid[27]; - - snprintf_P(uuid, sizeof(uuid), PSTR("Socket-1_0-%s"), WemoSerialnumber().c_str()); - return String(uuid); -} - -void WemoRespondToMSearch(int echo_type) -{ - char message[TOPSZ]; - - TickerMSearch.detach(); - if (PortUdp.beginPacket(udp_remote_ip, udp_remote_port)) { - char type[24]; - if (1 == echo_type) { - strcpy_P(type, URN_BELKIN_DEVICE_CAP); - } else { - strcpy_P(type, UPNP_ROOTDEVICE); - } - char response[400]; - snprintf_P(response, sizeof(response), WEMO_MSEARCH, WiFi.localIP().toString().c_str(), type, WemoUuid().c_str(), type); - PortUdp.write(response); - PortUdp.endPacket(); - snprintf_P(message, sizeof(message), PSTR(D_RESPONSE_SENT)); - } else { - snprintf_P(message, sizeof(message), PSTR(D_FAILED_TO_SEND_RESPONSE)); - } - - PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_WEMO " " D_JSON_TYPE " %d, %s " D_TO " %s:%d"), - echo_type, message, udp_remote_ip.toString().c_str(), udp_remote_port); - - udp_response_mutex = false; -} - - - - - -const char WEMO_EVENTSERVICE_XML[] PROGMEM = - "" - "" - "" - "SetBinaryState" - "" - "" - "" - "BinaryState" - "BinaryState" - "in" - "" - "" - "" - "" - "GetBinaryState" - "" - "" - "" - "BinaryState" - "BinaryState" - "out" - "" - "" - "" - "" - "" - "" - "BinaryState" - "bool" - "0" - "" - "" - "level" - "string" - "0" - "" - "" - "\r\n\r\n"; - -const char WEMO_METASERVICE_XML[] PROGMEM = - "" - "" - "1" - "0" - "" - "" - "" - "GetMetaInfo" - "" - "" - "GetMetaInfo" - "MetaInfo" - "in" - "" - "" - "" - "" - "" - "MetaInfo" - "string" - "0" - "" - "" - "\r\n\r\n"; - -const char WEMO_RESPONSE_STATE_SOAP[] PROGMEM = - "" - "" - "" - "%d" - "" - "" - "\r\n"; - -const char WEMO_SETUP_XML[] PROGMEM = - "" - "" - "" - "urn:Belkin:device:controllee:1" - "{x1" - "Belkin International Inc." - "Socket" - "3.1415" - "uuid:{x2" - "{x3" - "0" - "" - "" - "urn:Belkin:service:basicevent:1" - "urn:Belkin:serviceId:basicevent1" - "/upnp/control/basicevent1" - "/upnp/event/basicevent1" - "/eventservice.xml" - "" - "" - "urn:Belkin:service:metainfo:1" - "urn:Belkin:serviceId:metainfo1" - "/upnp/control/metainfo1" - "/upnp/event/metainfo1" - "/metainfoservice.xml" - "" - "" - "" - "\r\n"; - - - -void HandleUpnpEvent(void) -{ - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR(D_WEMO_BASIC_EVENT)); - - char event[500]; - strlcpy(event, WebServer->arg(0).c_str(), sizeof(event)); - - - - - char state = 'G'; - if (strstr_P(event, PSTR("SetBinaryState")) != nullptr) { - state = 'S'; - uint8_t power = POWER_TOGGLE; - if (strstr_P(event, PSTR("State>10on("/upnp/control/basicevent1", HTTP_POST, HandleUpnpEvent); - WebServer->on("/eventservice.xml", HandleUpnpService); - WebServer->on("/metainfoservice.xml", HandleUpnpMetaService); - WebServer->on("/setup.xml", HandleUpnpSetupWemo); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" -#ifdef USE_SONOFF_IFAN - - - - -#define XDRV_22 22 - -const uint8_t MAX_FAN_SPEED = 4; - -const uint8_t kIFan02Speed[MAX_FAN_SPEED] = { 0x00, 0x01, 0x03, 0x05 }; -const uint8_t kIFan03Speed[MAX_FAN_SPEED +2] = { 0x00, 0x01, 0x03, 0x04, 0x05, 0x06 }; -const uint8_t kIFan03Sequence[MAX_FAN_SPEED][MAX_FAN_SPEED] = {{0, 2, 2, 2}, {0, 1, 2, 4}, {1, 1, 2, 5}, {4, 4, 5, 3}}; - -const char kSonoffIfanCommands[] PROGMEM = "|" - D_CMND_FANSPEED; - -void (* const SonoffIfanCommand[])(void) PROGMEM = { - &CmndFanspeed }; - -uint8_t ifan_fanspeed_timer = 0; -uint8_t ifan_fanspeed_goal = 0; -bool ifan_receive_flag = false; -bool ifan_restart_flag = true; - - - -bool IsModuleIfan(void) -{ - return ((SONOFF_IFAN02 == my_module_type) || (SONOFF_IFAN03 == my_module_type)); -} - -uint8_t MaxFanspeed(void) -{ - return MAX_FAN_SPEED; -} - -uint8_t GetFanspeed(void) -{ - if (ifan_fanspeed_timer) { - return ifan_fanspeed_goal; - } else { - - - - - - - uint8_t fanspeed = (uint8_t)(power &0xF) >> 1; - if (fanspeed) { fanspeed = (fanspeed >> 1) +1; } - return fanspeed; - } -} - - - -void SonoffIFanSetFanspeed(uint8_t fanspeed, bool sequence) -{ - ifan_fanspeed_timer = 0; - ifan_fanspeed_goal = fanspeed; - - uint8_t fanspeed_now = GetFanspeed(); - - if (fanspeed == fanspeed_now) { return; } - - uint8_t fans = kIFan02Speed[fanspeed]; - if (SONOFF_IFAN03 == my_module_type) { - if (sequence) { - fanspeed = kIFan03Sequence[fanspeed_now][ifan_fanspeed_goal]; - if (fanspeed != ifan_fanspeed_goal) { - if (0 == fanspeed_now) { - ifan_fanspeed_timer = 20; - } else { - ifan_fanspeed_timer = 2; - } - } - } - fans = kIFan03Speed[fanspeed]; - } - for (uint32_t i = 2; i < 5; i++) { - uint8_t state = (fans &1) + POWER_OFF_NO_STATE; - ExecuteCommandPower(i, state, SRC_IGNORE); - fans >>= 1; - } - -#ifdef USE_DOMOTICZ - if (sequence) { DomoticzUpdateFanState(); } -#endif -} - - - -void SonoffIfanReceived(void) -{ - char svalue[32]; - - uint8_t mode = serial_in_buffer[3]; - uint8_t action = serial_in_buffer[6]; - - if (4 == mode) { - if (action < 4) { - - - - - if (action != GetFanspeed()) { - snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), action); - ExecuteCommand(svalue, SRC_REMOTE); -#ifdef USE_BUZZER - BuzzerEnabledBeep((action) ? action : 1, (action) ? 1 : 4); -#endif - } - } else { - - ExecuteCommandPower(1, POWER_TOGGLE, SRC_REMOTE); - } - } - if (6 == mode) { - - Settings.flag3.buzzer_enable = !Settings.flag3.buzzer_enable; - } - if (7 == mode) { - -#ifdef USE_BUZZER - BuzzerEnabledBeep(4, 1); -#endif - } - - - - serial_in_buffer[5] = 0; - serial_in_buffer[6] = 0; - for (uint32_t i = 0; i < 7; i++) { - if ((i > 1) && (i < 6)) { serial_in_buffer[6] += serial_in_buffer[i]; } - Serial.write(serial_in_buffer[i]); - } -} - -bool SonoffIfanSerialInput(void) -{ - if (SONOFF_IFAN03 == my_module_type) { - if (0xAA == serial_in_byte) { - serial_in_byte_counter = 0; - ifan_receive_flag = true; - } - if (ifan_receive_flag) { - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - if (serial_in_byte_counter == 8) { -# 176 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_22_sonoff_ifan.ino" - AddLogSerial(LOG_LEVEL_DEBUG); - uint8_t crc = 0; - for (uint32_t i = 2; i < 7; i++) { - crc += serial_in_buffer[i]; - } - if (crc == serial_in_buffer[7]) { - SonoffIfanReceived(); - ifan_receive_flag = false; - return true; - } - } - serial_in_byte = 0; - } - return false; - } -} - - - - - -void CmndFanspeed(void) -{ - if (XdrvMailbox.data_len > 0) { - if ('-' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = (int16_t)GetFanspeed() -1; - if (XdrvMailbox.payload < 0) { XdrvMailbox.payload = MAX_FAN_SPEED -1; } - } - else if ('+' == XdrvMailbox.data[0]) { - XdrvMailbox.payload = GetFanspeed() +1; - if (XdrvMailbox.payload > MAX_FAN_SPEED -1) { XdrvMailbox.payload = 0; } - } - } - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_FAN_SPEED)) { - SonoffIFanSetFanspeed(XdrvMailbox.payload, true); - } - ResponseCmndNumber(GetFanspeed()); -} - - - -bool SonoffIfanInit(void) -{ - if (SONOFF_IFAN03 == my_module_type) { - SetSerial(9600, TS_SERIAL_8N1); - } - return false; -} - -void SonoffIfanUpdate(void) -{ - if (SONOFF_IFAN03 == my_module_type) { - if (ifan_fanspeed_timer) { - ifan_fanspeed_timer--; - if (!ifan_fanspeed_timer) { - SonoffIFanSetFanspeed(ifan_fanspeed_goal, false); - } - } - } - - if (ifan_restart_flag && (4 == uptime) && (SONOFF_IFAN02 == my_module_type)) { - ifan_restart_flag = false; - SetDevicePower(1, SRC_RETRY); - SetDevicePower(power, SRC_RETRY); - } -} - - - - - -bool Xdrv22(uint8_t function) -{ - bool result = false; - - if (IsModuleIfan()) { - switch (function) { - case FUNC_EVERY_250_MSECOND: - SonoffIfanUpdate(); - break; - case FUNC_SERIAL: - result = SonoffIfanSerialInput(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kSonoffIfanCommands, SonoffIfanCommand); - break; - case FUNC_MODULE_INIT: - result = SonoffIfanInit(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" -#ifdef USE_ZIGBEE - -#define OCCUPANCY "Occupancy" - -typedef uint64_t Z_IEEEAddress; -typedef uint16_t Z_ShortAddress; - -enum ZnpCommandType { - Z_POLL = 0x00, - Z_SREQ = 0x20, - Z_AREQ = 0x40, - Z_SRSP = 0x60 -}; - -enum ZnpSubsystem { - Z_RPC_Error = 0x00, - Z_SYS = 0x01, - Z_MAC = 0x02, - Z_NWK = 0x03, - Z_AF = 0x04, - Z_ZDO = 0x05, - Z_SAPI = 0x06, - Z_UTIL = 0x07, - Z_DEBUG = 0x08, - Z_APP = 0x09 -}; - - -enum SysCommand { - SYS_RESET = 0x00, - SYS_PING = 0x01, - SYS_VERSION = 0x02, - SYS_SET_EXTADDR = 0x03, - SYS_GET_EXTADDR = 0x04, - SYS_RAM_READ = 0x05, - SYS_RAM_WRITE = 0x06, - SYS_OSAL_NV_ITEM_INIT = 0x07, - SYS_OSAL_NV_READ = 0x08, - SYS_OSAL_NV_WRITE = 0x09, - SYS_OSAL_START_TIMER = 0x0A, - SYS_OSAL_STOP_TIMER = 0x0B, - SYS_RANDOM = 0x0C, - SYS_ADC_READ = 0x0D, - SYS_GPIO = 0x0E, - SYS_STACK_TUNE = 0x0F, - SYS_SET_TIME = 0x10, - SYS_GET_TIME = 0x11, - SYS_OSAL_NV_DELETE = 0x12, - SYS_OSAL_NV_LENGTH = 0x13, - SYS_TEST_RF = 0x40, - SYS_TEST_LOOPBACK = 0x41, - SYS_RESET_IND = 0x80, - SYS_OSAL_TIMER_EXPIRED = 0x81, -}; - -enum SapiCommand { - SAPI_START_REQUEST = 0x00, - SAPI_BIND_DEVICE = 0x01, - SAPI_ALLOW_BIND = 0x02, - SAPI_SEND_DATA_REQUEST = 0x03, - SAPI_READ_CONFIGURATION = 0x04, - SAPI_WRITE_CONFIGURATION = 0x05, - SAPI_GET_DEVICE_INFO = 0x06, - SAPI_FIND_DEVICE_REQUEST = 0x07, - SAPI_PERMIT_JOINING_REQUEST = 0x08, - SAPI_SYSTEM_RESET = 0x09, - SAPI_START_CONFIRM = 0x80, - SAPI_BIND_CONFIRM = 0x81, - SAPI_ALLOW_BIND_CONFIRM = 0x82, - SAPI_SEND_DATA_CONFIRM = 0x83, - SAPI_FIND_DEVICE_CONFIRM = 0x85, - SAPI_RECEIVE_DATA_INDICATION = 0x87, -}; -enum Z_configuration { - CONF_EXTADDR = 0x01, - CONF_BOOTCOUNTER = 0x02, - CONF_STARTUP_OPTION = 0x03, - CONF_START_DELAY = 0x04, - CONF_NIB = 0x21, - CONF_DEVICE_LIST = 0x22, - CONF_ADDRMGR = 0x23, - CONF_POLL_RATE = 0x24, - CONF_QUEUED_POLL_RATE = 0x25, - CONF_RESPONSE_POLL_RATE = 0x26, - CONF_REJOIN_POLL_RATE = 0x27, - CONF_DATA_RETRIES = 0x28, - CONF_POLL_FAILURE_RETRIES = 0x29, - CONF_STACK_PROFILE = 0x2A, - CONF_INDIRECT_MSG_TIMEOUT = 0x2B, - CONF_ROUTE_EXPIRY_TIME = 0x2C, - CONF_EXTENDED_PAN_ID = 0x2D, - CONF_BCAST_RETRIES = 0x2E, - CONF_PASSIVE_ACK_TIMEOUT = 0x2F, - CONF_BCAST_DELIVERY_TIME = 0x30, - CONF_NWK_MODE = 0x31, - CONF_CONCENTRATOR_ENABLE = 0x32, - CONF_CONCENTRATOR_DISCOVERY = 0x33, - CONF_CONCENTRATOR_RADIUS = 0x34, - CONF_CONCENTRATOR_RC = 0x36, - CONF_NWK_MGR_MODE = 0x37, - CONF_SRC_RTG_EXPIRY_TIME = 0x38, - CONF_ROUTE_DISCOVERY_TIME = 0x39, - CONF_NWK_ACTIVE_KEY_INFO = 0x3A, - CONF_NWK_ALTERN_KEY_INFO = 0x3B, - CONF_ROUTER_OFF_ASSOC_CLEANUP = 0x3C, - CONF_NWK_LEAVE_REQ_ALLOWED = 0x3D, - CONF_NWK_CHILD_AGE_ENABLE = 0x3E, - CONF_DEVICE_LIST_KA_TIMEOUT = 0x3F, - CONF_BINDING_TABLE = 0x41, - CONF_GROUP_TABLE = 0x42, - CONF_APS_FRAME_RETRIES = 0x43, - CONF_APS_ACK_WAIT_DURATION = 0x44, - CONF_APS_ACK_WAIT_MULTIPLIER = 0x45, - CONF_BINDING_TIME = 0x46, - CONF_APS_USE_EXT_PANID = 0x47, - CONF_APS_USE_INSECURE_JOIN = 0x48, - CONF_COMMISSIONED_NWK_ADDR = 0x49, - CONF_APS_NONMEMBER_RADIUS = 0x4B, - CONF_APS_LINK_KEY_TABLE = 0x4C, - CONF_APS_DUPREJ_TIMEOUT_INC = 0x4D, - CONF_APS_DUPREJ_TIMEOUT_COUNT = 0x4E, - CONF_APS_DUPREJ_TABLE_SIZE = 0x4F, - CONF_DIAGNOSTIC_STATS = 0x50, - CONF_SECURITY_LEVEL = 0x61, - CONF_PRECFGKEY = 0x62, - CONF_PRECFGKEYS_ENABLE = 0x63, - CONF_SECURITY_MODE = 0x64, - CONF_SECURE_PERMIT_JOIN = 0x65, - CONF_APS_LINK_KEY_TYPE = 0x66, - CONF_APS_ALLOW_R19_SECURITY = 0x67, - CONF_IMPLICIT_CERTIFICATE = 0x69, - CONF_DEVICE_PRIVATE_KEY = 0x6A, - CONF_CA_PUBLIC_KEY = 0x6B, - CONF_KE_MAX_DEVICES = 0x6C, - CONF_USE_DEFAULT_TCLK = 0x6D, - CONF_RNG_COUNTER = 0x6F, - CONF_RANDOM_SEED = 0x70, - CONF_TRUSTCENTER_ADDR = 0x71, - CONF_USERDESC = 0x81, - CONF_NWKKEY = 0x82, - CONF_PANID = 0x83, - CONF_CHANLIST = 0x84, - CONF_LEAVE_CTRL = 0x85, - CONF_SCAN_DURATION = 0x86, - CONF_LOGICAL_TYPE = 0x87, - CONF_NWKMGR_MIN_TX = 0x88, - CONF_NWKMGR_ADDR = 0x89, - CONF_ZDO_DIRECT_CB = 0x8F, - CONF_TCLK_TABLE_START = 0x0101, - ZNP_HAS_CONFIGURED = 0xF00 -}; -# 210 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" -enum Z_Status { - Z_Success = 0x00, - Z_Failure = 0x01, - Z_InvalidParameter = 0x02, - Z_MemError = 0x03, - Z_Created = 0x09, - Z_BufferFull = 0x11 -}; - -enum Z_App_Profiles { - Z_PROF_IPM = 0x0101, - Z_PROF_HA = 0x0104, - Z_PROF_CBA = 0x0105, - Z_PROF_TA = 0x0107, - Z_PROF_PHHC = 0x0108, - Z_PROF_AMI = 0x0109, -}; - -enum Z_Device_Ids { - Z_DEVID_CONF_TOOL = 0x0005, -# 262 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" -}; -# 275 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_0_constants.ino" -enum AfCommand : uint8_t { - AF_REGISTER = 0x00, - AF_DATA_REQUEST = 0x01, - AF_DATA_REQUEST_EXT = 0x02, - AF_DATA_REQUEST_SRC_RTG = 0x03, - AF_INTER_PAN_CTL = 0x10, - AF_DATA_STORE = 0x11, - AF_DATA_RETRIEVE = 0x12, - AF_APSF_CONFIG_SET = 0x13, - AF_DATA_CONFIRM = 0x80, - AF_REFLECT_ERROR = 0x83, - AF_INCOMING_MSG = 0x81, - AF_INCOMING_MSG_EXT = 0x82 -}; - - -enum : uint8_t { - ZDO_NWK_ADDR_REQ = 0x00, - ZDO_IEEE_ADDR_REQ = 0x01, - ZDO_NODE_DESC_REQ = 0x02, - ZDO_POWER_DESC_REQ = 0x03, - ZDO_SIMPLE_DESC_REQ = 0x04, - ZDO_ACTIVE_EP_REQ = 0x05, - ZDO_MATCH_DESC_REQ = 0x06, - ZDO_COMPLEX_DESC_REQ = 0x07, - ZDO_USER_DESC_REQ = 0x08, - ZDO_DEVICE_ANNCE = 0x0A, - ZDO_USER_DESC_SET = 0x0B, - ZDO_SERVER_DISC_REQ = 0x0C, - ZDO_END_DEVICE_BIND_REQ = 0x20, - ZDO_BIND_REQ = 0x21, - ZDO_UNBIND_REQ = 0x22, - ZDO_SET_LINK_KEY = 0x23, - ZDO_REMOVE_LINK_KEY = 0x24, - ZDO_GET_LINK_KEY = 0x25, - ZDO_MGMT_NWK_DISC_REQ = 0x30, - ZDO_MGMT_LQI_REQ = 0x31, - ZDO_MGMT_RTQ_REQ = 0x32, - ZDO_MGMT_BIND_REQ = 0x33, - ZDO_MGMT_LEAVE_REQ = 0x34, - ZDO_MGMT_DIRECT_JOIN_REQ = 0x35, - ZDO_MGMT_PERMIT_JOIN_REQ = 0x36, - ZDO_MGMT_NWK_UPDATE_REQ = 0x37, - ZDO_MSG_CB_REGISTER = 0x3E, - ZDO_MGS_CB_REMOVE = 0x3F, - ZDO_STARTUP_FROM_APP = 0x40, - ZDO_AUTO_FIND_DESTINATION = 0x41, - ZDO_EXT_REMOVE_GROUP = 0x47, - ZDO_EXT_REMOVE_ALL_GROUP = 0x48, - ZDO_EXT_FIND_ALL_GROUPS_ENDPOINT = 0x49, - ZDO_EXT_FIND_GROUP = 0x4A, - ZDO_EXT_ADD_GROUP = 0x4B, - ZDO_EXT_COUNT_ALL_GROUPS = 0x4C, - ZDO_NWK_ADDR_RSP = 0x80, - ZDO_IEEE_ADDR_RSP = 0x81, - ZDO_NODE_DESC_RSP = 0x82, - ZDO_POWER_DESC_RSP = 0x83, - ZDO_SIMPLE_DESC_RSP = 0x84, - ZDO_ACTIVE_EP_RSP = 0x85, - ZDO_MATCH_DESC_RSP = 0x86, - ZDO_COMPLEX_DESC_RSP = 0x87, - ZDO_USER_DESC_RSP = 0x88, - ZDO_USER_DESC_CONF = 0x89, - ZDO_SERVER_DISC_RSP = 0x8A, - ZDO_END_DEVICE_BIND_RSP = 0xA0, - ZDO_BIND_RSP = 0xA1, - ZDO_UNBIND_RSP = 0xA2, - ZDO_MGMT_NWK_DISC_RSP = 0xB0, - ZDO_MGMT_LQI_RSP = 0xB1, - ZDO_MGMT_RTG_RSP = 0xB2, - ZDO_MGMT_BIND_RSP = 0xB3, - ZDO_MGMT_LEAVE_RSP = 0xB4, - ZDO_MGMT_DIRECT_JOIN_RSP = 0xB5, - ZDO_MGMT_PERMIT_JOIN_RSP = 0xB6, - ZDO_STATE_CHANGE_IND = 0xC0, - ZDO_END_DEVICE_ANNCE_IND = 0xC1, - ZDO_MATCH_DESC_RSP_SENT = 0xC2, - ZDO_STATUS_ERROR_RSP = 0xC3, - ZDO_SRC_RTG_IND = 0xC4, - ZDO_LEAVE_IND = 0xC9, - ZDO_TC_DEV_IND = 0xCA, - ZDO_PERMIT_JOIN_IND = 0xCB, - ZDO_MSG_CB_INCOMING = 0xFF -}; - - -enum ZdoStates { - ZDO_DEV_HOLD = 0x00, - ZDO_DEV_INIT = 0x01, - ZDO_DEV_NWK_DISC = 0x02, - ZDO_DEV_NWK_JOINING = 0x03, - ZDO_DEV_NWK_REJOIN = 0x04, - ZDO_DEV_END_DEVICE_UNAUTH = 0x05, - ZDO_DEV_END_DEVICE = 0x06, - ZDO_DEV_ROUTER = 0x07, - ZDO_DEV_COORD_STARTING = 0x08, - ZDO_DEV_ZB_COORD = 0x09, - ZDO_DEV_NWK_ORPHAN = 0x0A, -}; - - -enum Z_Util { - Z_UTIL_GET_DEVICE_INFO = 0x00, - Z_UTIL_GET_NV_INFO = 0x01, - Z_UTIL_SET_PANID = 0x02, - Z_UTIL_SET_CHANNELS = 0x03, - Z_UTIL_SET_SECLEVEL = 0x04, - Z_UTIL_SET_PRECFGKEY = 0x05, - Z_UTIL_CALLBACK_SUB_CMD = 0x06, - Z_UTIL_KEY_EVENT = 0x07, - Z_UTIL_TIME_ALIVE = 0x09, - Z_UTIL_LED_CONTROL = 0x0A, - Z_UTIL_TEST_LOOPBACK = 0x10, - Z_UTIL_DATA_REQ = 0x11, - Z_UTIL_SRC_MATCH_ENABLE = 0x20, - Z_UTIL_SRC_MATCH_ADD_ENTRY = 0x21, - Z_UTIL_SRC_MATCH_DEL_ENTRY = 0x22, - Z_UTIL_SRC_MATCH_CHECK_SRC_ADDR = 0x23, - Z_UTIL_SRC_MATCH_ACK_ALL_PENDING = 0x24, - Z_UTIL_SRC_MATCH_CHECK_ALL_PENDING = 0x25, - Z_UTIL_ADDRMGR_EXT_ADDR_LOOKUP = 0x40, - Z_UTIL_ADDRMGR_NWK_ADDR_LOOKUP = 0x41, - Z_UTIL_APSME_LINK_KEY_DATA_GET = 0x44, - Z_UTIL_APSME_LINK_KEY_NV_ID_GET = 0x45, - Z_UTIL_ASSOC_COUNT = 0x48, - Z_UTIL_ASSOC_FIND_DEVICE = 0x49, - Z_UTIL_ASSOC_GET_WITH_ADDRESS = 0x4A, - Z_UTIL_APSME_REQUEST_KEY_CMD = 0x4B, - Z_UTIL_ZCL_KEY_EST_INIT_EST = 0x80, - Z_UTIL_ZCL_KEY_EST_SIGN = 0x81, - Z_UTIL_UTIL_SYNC_REQ = 0xE0, - Z_UTIL_ZCL_KEY_ESTABLISH_IND = 0xE1 -}; - -enum ZCL_Global_Commands { - ZCL_READ_ATTRIBUTES = 0x00, - ZCL_READ_ATTRIBUTES_RESPONSE = 0x01, - ZCL_WRITE_ATTRIBUTES = 0x02, - ZCL_WRITE_ATTRIBUTES_UNDIVIDED = 0x03, - ZCL_WRITE_ATTRIBUTES_RESPONSE = 0x04, - ZCL_WRITE_ATTRIBUTES_NORESPONSE = 0x05, - ZCL_CONFIGURE_REPORTING = 0x06, - ZCL_CONFIGURE_REPORTING_RESPONSE = 0x07, - ZCL_READ_REPORTING_CONFIGURATION = 0x08, - ZCL_READ_REPORTING_CONFIGURATION_RESPONSE = 0x09, - ZCL_REPORT_ATTRIBUTES = 0x0a, - ZCL_DEFAULT_RESPONSE = 0x0b, - ZCL_DISCOVER_ATTRIBUTES = 0x0c, - ZCL_DISCOVER_ATTRIBUTES_RESPONSE = 0x0d - -}; - -const uint16_t Z_ProfileIds[] PROGMEM = { 0x0104, 0x0109, 0xA10E, 0xC05E }; -const char Z_ProfileNames[] PROGMEM = "ZigBee Home Automation|ZigBee Smart Energy|ZigBee Green Power|ZigBee Light Link"; - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_1_headers.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_1_headers.ino" -#ifdef USE_ZIGBEE - - - -void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp = true, uint8_t transacId = 1); - - - -JsonVariant &getCaseInsensitive(const JsonObject &json, const char *needle) { - - if ((nullptr == &json) || (nullptr == needle) || (0 == pgm_read_byte(needle))) { - return *(JsonVariant*)nullptr; - } - - for (auto kv : json) { - const char *key = kv.key; - JsonVariant &value = kv.value; - - if (0 == strcasecmp_P(key, needle)) { - return value; - } - } - - return *(JsonVariant*)nullptr; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" -#ifdef USE_ZIGBEE - -#include -#include - -#ifndef ZIGBEE_SAVE_DELAY_SECONDS -#define ZIGBEE_SAVE_DELAY_SECONDS 10; -#endif -const uint16_t kZigbeeSaveDelaySeconds = ZIGBEE_SAVE_DELAY_SECONDS; - -typedef int32_t (*Z_DeviceTimer)(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value); - -typedef struct Z_Device { - uint16_t shortaddr; - uint64_t longaddr; - uint32_t firstSeen; - uint32_t lastSeen; - String manufacturerId; - String modelId; - String friendlyName; - std::vector endpoints; - std::vector clusters_in; - std::vector clusters_out; - - uint32_t timer; - uint16_t cluster; - uint16_t endpoint; - uint32_t value; - Z_DeviceTimer func; - - DynamicJsonBuffer *json_buffer; - JsonObject *json; -} Z_Device; - - - - - - - -class Z_Devices { -public: - Z_Devices() {}; - - - - - - - uint16_t isKnownShortAddr(uint16_t shortaddr) const; - uint16_t isKnownLongAddr(uint64_t longaddr) const; - uint16_t isKnownIndex(uint32_t index) const; - uint16_t isKnownFriendlyName(const char * name) const; - - uint64_t getDeviceLongAddr(uint16_t shortaddr) const; - - - - void updateDevice(uint16_t shortaddr, uint64_t longaddr = 0); - - - void addEndoint(uint16_t shortaddr, uint8_t endpoint); - - - void addEndointProfile(uint16_t shortaddr, uint8_t endpoint, uint16_t profileId); - - - void addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluster, bool out); - - uint8_t findClusterEndpointIn(uint16_t shortaddr, uint16_t cluster); - - void setManufId(uint16_t shortaddr, const char * str); - void setModelId(uint16_t shortaddr, const char * str); - void setFriendlyName(uint16_t shortaddr, const char * str); - const String * getFriendlyName(uint16_t) const; - - - void updateLastSeen(uint16_t shortaddr); - - - String dump(uint32_t dump_mode, uint16_t status_shortaddr = 0) const; - - - void resetTimer(uint32_t shortaddr); - void setTimer(uint32_t shortaddr, uint32_t wait_ms, uint16_t cluster, uint16_t endpoint, uint32_t value, Z_DeviceTimer func); - void runTimer(void); - - - void jsonClear(uint16_t shortaddr); - void jsonAppend(uint16_t shortaddr, const JsonObject &values); - const JsonObject *jsonGet(uint16_t shortaddr); - void jsonPublishFlush(uint16_t shortaddr); - bool jsonIsConflict(uint16_t shortaddr, const JsonObject &values); - void jsonPublishNow(uint16_t shortaddr, JsonObject &values); - - - size_t devicesSize(void) const { - return _devices.size(); - } - const Z_Device &devicesAt(size_t i) const { - return _devices.at(i); - } - - - bool removeDevice(uint16_t shortaddr); - - - void dirty(void); - void clean(void); - - - uint16_t parseDeviceParam(const char * param, bool short_must_be_known = false) const; - -private: - std::vector _devices = {}; - uint32_t _saveTimer = 0; - - template < typename T> - static bool findInVector(const std::vector & vecOfElements, const T & element); - - template < typename T> - static int32_t findEndpointInVector(const std::vector & vecOfElements, uint8_t element); - - - static int32_t findClusterEndpoint(const std::vector & vecOfElements, uint16_t element); - - Z_Device & getShortAddr(uint16_t shortaddr); - const Z_Device & getShortAddrConst(uint16_t shortaddr) const ; - Z_Device & getLongAddr(uint64_t longaddr); - - int32_t findShortAddr(uint16_t shortaddr) const; - int32_t findLongAddr(uint64_t longaddr) const; - int32_t findFriendlyName(const char * name) const; - - void _updateLastSeen(Z_Device &device) { - if (&device != nullptr) { - device.lastSeen = Rtc.utc_time; - } - }; - - - Z_Device & createDeviceEntry(uint16_t shortaddr, uint64_t longaddr = 0); -}; - -Z_Devices zigbee_devices = Z_Devices(); - - -uint64_t localIEEEAddr = 0; - - -template < typename T> -bool Z_Devices::findInVector(const std::vector & vecOfElements, const T & element) { - - auto it = std::find(vecOfElements.begin(), vecOfElements.end(), element); - - if (it != vecOfElements.end()) { - return true; - } else { - return false; - } -} - -template < typename T> -int32_t Z_Devices::findEndpointInVector(const std::vector & vecOfElements, uint8_t element) { - - - int32_t found = 0; - for (auto &elem : vecOfElements) { - if ( ((elem >> 16) & 0xFF) == element) { return found; } - found++; - } - - return -1; -} -# 204 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" -int32_t Z_Devices::findClusterEndpoint(const std::vector & vecOfElements, uint16_t cluster) { - int32_t found = 0; - for (auto &elem : vecOfElements) { - if ((elem & 0xFFFF) == cluster) { return found; } - found++; - } - return -1; -} - - - - - -Z_Device & Z_Devices::createDeviceEntry(uint16_t shortaddr, uint64_t longaddr) { - if (!shortaddr && !longaddr) { return *(Z_Device*) nullptr; } - Z_Device device = { shortaddr, longaddr, - Rtc.utc_time, Rtc.utc_time, - String(), - String(), - String(), - std::vector(), - std::vector(), - std::vector(), - 0,0,0,0, - nullptr, - nullptr, nullptr }; - device.json_buffer = new DynamicJsonBuffer(); - _devices.push_back(device); - dirty(); - return _devices.back(); -} -# 244 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" -int32_t Z_Devices::findShortAddr(uint16_t shortaddr) const { - if (!shortaddr) { return -1; } - int32_t found = 0; - if (shortaddr) { - for (auto &elem : _devices) { - if (elem.shortaddr == shortaddr) { return found; } - found++; - } - } - return -1; -} -# 263 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" -int32_t Z_Devices::findLongAddr(uint64_t longaddr) const { - if (!longaddr) { return -1; } - int32_t found = 0; - if (longaddr) { - for (auto &elem : _devices) { - if (elem.longaddr == longaddr) { return found; } - found++; - } - } - return -1; -} -# 282 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" -int32_t Z_Devices::findFriendlyName(const char * name) const { - if (!name) { return -1; } - size_t name_len = strlen(name); - int32_t found = 0; - if (name_len) { - for (auto &elem : _devices) { - if (elem.friendlyName == name) { return found; } - found++; - } - } - return -1; -} - - -uint16_t Z_Devices::isKnownShortAddr(uint16_t shortaddr) const { - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - return shortaddr; - } else { - return 0; - } -} - -uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const { - int32_t found = findLongAddr(longaddr); - if (found >= 0) { - const Z_Device & device = devicesAt(found); - return device.shortaddr; - } else { - return 0; - } -} - -uint16_t Z_Devices::isKnownIndex(uint32_t index) const { - if (index < devicesSize()) { - const Z_Device & device = devicesAt(index); - return device.shortaddr; - } else { - return 0; - } -} - -uint16_t Z_Devices::isKnownFriendlyName(const char * name) const { - if ((!name) || (0 == strlen(name))) { return 0xFFFF; } - int32_t found = findFriendlyName(name); - if (found >= 0) { - const Z_Device & device = devicesAt(found); - return device.shortaddr; - } else { - return 0; - } -} - -uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const { - const Z_Device & device = getShortAddrConst(shortaddr); - return device.longaddr; -} - - - - -Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) { - if (!shortaddr) { return *(Z_Device*) nullptr; } - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - return _devices[found]; - } - - return createDeviceEntry(shortaddr, 0); -} - -const Z_Device & Z_Devices::getShortAddrConst(uint16_t shortaddr) const { - if (!shortaddr) { return *(Z_Device*) nullptr; } - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - return _devices[found]; - } - return *((Z_Device*)nullptr); -} - - -Z_Device & Z_Devices::getLongAddr(uint64_t longaddr) { - if (!longaddr) { return *(Z_Device*) nullptr; } - int32_t found = findLongAddr(longaddr); - if (found > 0) { - return _devices[found]; - } - return createDeviceEntry(0, longaddr); -} - - -bool Z_Devices::removeDevice(uint16_t shortaddr) { - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - _devices.erase(_devices.begin() + found); - dirty(); - return true; - } - return false; -} - - - - - - -void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) { - int32_t s_found = findShortAddr(shortaddr); - int32_t l_found = findLongAddr(longaddr); - - if ((s_found >= 0) && (l_found >= 0)) { - if (s_found == l_found) { - updateLastSeen(shortaddr); - } else { - - _devices[l_found].shortaddr = shortaddr; - - _devices.erase(_devices.begin() + s_found); - updateLastSeen(shortaddr); - dirty(); - } - } else if (s_found >= 0) { - - - _devices[s_found].longaddr = longaddr; - updateLastSeen(shortaddr); - dirty(); - } else if (l_found >= 0) { - - _devices[l_found].shortaddr = shortaddr; - dirty(); - } else { - - if (shortaddr || longaddr) { - createDeviceEntry(shortaddr, longaddr); - } - } -} - - - - -void Z_Devices::addEndoint(uint16_t shortaddr, uint8_t endpoint) { - if (!shortaddr) { return; } - uint32_t ep_profile = (endpoint << 16); - Z_Device &device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - _updateLastSeen(device); - if (findEndpointInVector(device.endpoints, endpoint) < 0) { - device.endpoints.push_back(ep_profile); - dirty(); - } -} - -void Z_Devices::addEndointProfile(uint16_t shortaddr, uint8_t endpoint, uint16_t profileId) { - if (!shortaddr) { return; } - uint32_t ep_profile = (endpoint << 16) | profileId; - Z_Device &device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - _updateLastSeen(device); - int32_t found = findEndpointInVector(device.endpoints, endpoint); - if (found < 0) { - device.endpoints.push_back(ep_profile); - dirty(); - } else { - if (device.endpoints[found] != ep_profile) { - device.endpoints[found] = ep_profile; - dirty(); - } - } -} - -void Z_Devices::addCluster(uint16_t shortaddr, uint8_t endpoint, uint16_t cluster, bool out) { - if (!shortaddr) { return; } - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - _updateLastSeen(device); - uint32_t ep_cluster = (endpoint << 16) | cluster; - if (!out) { - if (!findInVector(device.clusters_in, ep_cluster)) { - device.clusters_in.push_back(ep_cluster); - dirty(); - } - } else { - if (!findInVector(device.clusters_out, ep_cluster)) { - device.clusters_out.push_back(ep_cluster); - dirty(); - } - } -} - - - -uint8_t Z_Devices::findClusterEndpointIn(uint16_t shortaddr, uint16_t cluster){ - int32_t short_found = findShortAddr(shortaddr); - if (short_found < 0) return 0; - Z_Device &device = getShortAddr(shortaddr); - if (&device == nullptr) { return 0; } - int32_t found = findClusterEndpoint(device.clusters_in, cluster); - if (found >= 0) { - return (device.clusters_in[found] >> 16) & 0xFF; - } else { - return 0; - } -} - - -void Z_Devices::setManufId(uint16_t shortaddr, const char * str) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - _updateLastSeen(device); - if (!device.manufacturerId.equals(str)) { - dirty(); - } - device.manufacturerId = str; -} -void Z_Devices::setModelId(uint16_t shortaddr, const char * str) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - _updateLastSeen(device); - if (!device.modelId.equals(str)) { - dirty(); - } - device.modelId = str; -} -void Z_Devices::setFriendlyName(uint16_t shortaddr, const char * str) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - _updateLastSeen(device); - if (!device.friendlyName.equals(str)) { - dirty(); - } - device.friendlyName = str; -} - -const String * Z_Devices::getFriendlyName(uint16_t shortaddr) const { - int32_t found = findShortAddr(shortaddr); - if (found >= 0) { - const Z_Device & device = devicesAt(found); - if (device.friendlyName.length() > 0) { - return &device.friendlyName; - } - } - return nullptr; -} - - -void Z_Devices::updateLastSeen(uint16_t shortaddr) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - _updateLastSeen(device); -} - - - - -void Z_Devices::resetTimer(uint32_t shortaddr) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - device.timer = 0; - device.func = nullptr; -} - - -void Z_Devices::setTimer(uint32_t shortaddr, uint32_t wait_ms, uint16_t cluster, uint16_t endpoint, uint32_t value, Z_DeviceTimer func) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - - device.cluster = cluster; - device.endpoint = endpoint; - device.value = value; - device.func = func; - device.timer = wait_ms + millis(); -} - - -void Z_Devices::runTimer(void) { - for (std::vector::iterator it = _devices.begin(); it != _devices.end(); ++it) { - Z_Device &device = *it; - uint16_t shortaddr = device.shortaddr; - - uint32_t timer = device.timer; - if ((timer) && TimeReached(timer)) { - device.timer = 0; - - (*device.func)(device.shortaddr, device.cluster, device.endpoint, device.value); - } - } - - if ((_saveTimer) && TimeReached(_saveTimer)) { - saveZigbeeDevices(); - _saveTimer = 0; - } -} - -void Z_Devices::jsonClear(uint16_t shortaddr) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - - device.json = nullptr; - device.json_buffer->clear(); -} - -void CopyJsonVariant(JsonObject &to, const String &key, const JsonVariant &val) { - to.remove(key); - - if (val.is()) { - String sval = val.as(); - to.set(key, sval); - } else if (val.is()) { - JsonArray &nested_arr = to.createNestedArray(key); - CopyJsonArray(nested_arr, val.as()); - } else if (val.is()) { - JsonObject &nested_obj = to.createNestedObject(key); - CopyJsonObject(nested_obj, val.as()); - } else { - to.set(key, val); - } -} - -void CopyJsonArray(JsonArray &to, const JsonArray &arr) { - for (auto v : arr) { - if (v.is()) { - String sval = v.as(); - to.add(sval); - } else if (v.is()) { - } else if (v.is()) { - } else { - to.add(v); - } - } -} - -void CopyJsonObject(JsonObject &to, const JsonObject &from) { - for (auto kv : from) { - String key_string = kv.key; - JsonVariant &val = kv.value; - - CopyJsonVariant(to, key_string, val); - } -} - - -bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const JsonObject &values) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return false; } - if (&values == nullptr) { return false; } - - if (nullptr == device.json) { - return false; - } - - for (auto kv : values) { - String key_string = kv.key; - - if (strcasecmp_P(kv.key, PSTR(D_CMND_ZIGBEE_LINKQUALITY))) { - if (device.json->containsKey(kv.key)) { - return true; - } - } - } - return false; -} - -void Z_Devices::jsonAppend(uint16_t shortaddr, const JsonObject &values) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - if (&values == nullptr) { return; } - - if (nullptr == device.json) { - device.json = &(device.json_buffer->createObject()); - } - - char sa[8]; - snprintf_P(sa, sizeof(sa), PSTR("0x%04X"), shortaddr); - device.json->set(F(D_JSON_ZIGBEE_DEVICE), sa); - - const String * fname = zigbee_devices.getFriendlyName(shortaddr); - if (fname) { - device.json->set(F(D_JSON_ZIGBEE_NAME), (char*)fname->c_str()); - } - - - CopyJsonObject(*device.json, values); -} - -const JsonObject *Z_Devices::jsonGet(uint16_t shortaddr) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return nullptr; } - return device.json; -} - -void Z_Devices::jsonPublishFlush(uint16_t shortaddr) { - Z_Device & device = getShortAddr(shortaddr); - if (&device == nullptr) { return; } - JsonObject * json = device.json; - if (json == nullptr) { return; } - - const String * fname = zigbee_devices.getFriendlyName(shortaddr); - bool use_fname = (Settings.flag4.zigbee_use_names) && (fname); -# 693 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_3_devices.ino" - if (use_fname) { - json->remove(F(D_JSON_ZIGBEE_NAME)); - } else { - json->remove(F(D_JSON_ZIGBEE_DEVICE)); - } - - String msg = ""; - json->printTo(msg); - zigbee_devices.jsonClear(shortaddr); - - if (use_fname) { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str()); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - XdrvRulesProcess(); - - Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"%s\":%s}}"), fname->c_str(), msg.c_str()); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - XdrvRulesProcess(); - } else { - Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str()); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - XdrvRulesProcess(); - - Response_P(PSTR("{\"" D_JSON_ZIGBEE_RECEIVED_LEGACY "\":{\"0x%04X\":%s}}"), shortaddr, msg.c_str()); - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - XdrvRulesProcess(); - } - - -} - -void Z_Devices::jsonPublishNow(uint16_t shortaddr, JsonObject & values) { - jsonPublishFlush(shortaddr); - jsonAppend(shortaddr, values); - jsonPublishFlush(shortaddr); -} - -void Z_Devices::dirty(void) { - _saveTimer = kZigbeeSaveDelaySeconds * 1000 + millis(); -} -void Z_Devices::clean(void) { - _saveTimer = 0; -} - - - - - - -uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_known) const { - if (nullptr == param) { return 0; } - size_t param_len = strlen(param); - char dataBuf[param_len + 1]; - strcpy(dataBuf, param); - RemoveSpace(dataBuf); - uint16_t shortaddr = 0; - - if (strlen(dataBuf) < 4) { - - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 99)) { - shortaddr = zigbee_devices.isKnownIndex(XdrvMailbox.payload - 1); - } - } else if ((dataBuf[0] == '0') && (dataBuf[1] == 'x')) { - - if (strlen(dataBuf) < 18) { - - shortaddr = strtoull(dataBuf, nullptr, 0); - if (short_must_be_known) { - shortaddr = zigbee_devices.isKnownShortAddr(shortaddr); - } - - } else { - - uint64_t longaddr = strtoull(dataBuf, nullptr, 0); - shortaddr = zigbee_devices.isKnownLongAddr(longaddr); - } - } else { - - shortaddr = zigbee_devices.isKnownFriendlyName(dataBuf); - } - - return shortaddr; -} - - - - - -String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const { - DynamicJsonBuffer jsonBuffer; - JsonArray& json = jsonBuffer.createArray(); - JsonArray& devices = json; - - for (std::vector::const_iterator it = _devices.begin(); it != _devices.end(); ++it) { - const Z_Device& device = *it; - uint16_t shortaddr = device.shortaddr; - char hex[22]; - - - if ((status_shortaddr) && (status_shortaddr != shortaddr)) { continue; } - - JsonObject& dev = devices.createNestedObject(); - - snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), shortaddr); - dev[F(D_JSON_ZIGBEE_DEVICE)] = hex; - - if (device.friendlyName.length() > 0) { - dev[F(D_JSON_ZIGBEE_NAME)] = device.friendlyName; - } - - if (2 <= dump_mode) { - hex[0] = '0'; - hex[1] = 'x'; - Uint64toHex(device.longaddr, &hex[2], 64); - dev[F("IEEEAddr")] = hex; - if (device.modelId.length() > 0) { - dev[F(D_JSON_MODEL D_JSON_ID)] = device.modelId; - } - if (device.manufacturerId.length() > 0) { - dev[F("Manufacturer")] = device.manufacturerId; - } - } - - - if (3 <= dump_mode) { - JsonObject& dev_endpoints = dev.createNestedObject(F("Endpoints")); - for (std::vector::const_iterator ite = device.endpoints.begin() ; ite != device.endpoints.end(); ++ite) { - uint32_t ep_profile = *ite; - uint8_t endpoint = (ep_profile >> 16) & 0xFF; - uint16_t profileId = ep_profile & 0xFFFF; - - snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); - JsonObject& ep = dev_endpoints.createNestedObject(hex); - - snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), profileId); - ep[F("ProfileId")] = hex; - - int32_t found = -1; - for (uint32_t i = 0; i < sizeof(Z_ProfileIds) / sizeof(Z_ProfileIds[0]); i++) { - if (pgm_read_word(&Z_ProfileIds[i]) == profileId) { - found = i; - break; - } - } - if (found > 0) { - GetTextIndexed(hex, sizeof(hex), found, Z_ProfileNames); - ep[F("ProfileIdName")] = hex; - } - - ep.createNestedArray(F("ClustersIn")); - ep.createNestedArray(F("ClustersOut")); - } - - for (std::vector::const_iterator itc = device.clusters_in.begin() ; itc != device.clusters_in.end(); ++itc) { - uint16_t cluster = *itc & 0xFFFF; - uint8_t endpoint = (*itc >> 16) & 0xFF; - - snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); - JsonArray &cluster_arr = dev_endpoints[hex][F("ClustersIn")]; - - snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), cluster); - cluster_arr.add(hex); - } - - for (std::vector::const_iterator itc = device.clusters_out.begin() ; itc != device.clusters_out.end(); ++itc) { - uint16_t cluster = *itc & 0xFFFF; - uint8_t endpoint = (*itc >> 16) & 0xFF; - - snprintf_P(hex, sizeof(hex), PSTR("0x%02X"), endpoint); - JsonArray &cluster_arr = dev_endpoints[hex][F("ClustersOut")]; - - snprintf_P(hex, sizeof(hex), PSTR("0x%04X"), cluster); - cluster_arr.add(hex); - } - } - } - String payload = ""; - payload.reserve(200); - json.printTo(payload); - return payload; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" -#ifdef USE_ZIGBEE -# 50 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_4_persistence.ino" -const static uint16_t z_spi_start_sector = 0xFF; -const static uint8_t* z_spi_start = (uint8_t*) 0x402FF000; -const static uint8_t* z_dev_start = z_spi_start + 0x0800; -const static size_t z_spi_len = 0x1000; -const static size_t z_block_offset = 0x0800; -const static size_t z_block_len = 0x0800; - -class z_flashdata_t { -public: - uint32_t name; - uint16_t len; - uint16_t reserved; -}; - -const static uint32_t ZIGB_NAME = 0x3167697A; -const static size_t Z_MAX_FLASH = z_block_len - sizeof(z_flashdata_t); - - -const uint16_t Z_ClusterNumber[] PROGMEM = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0100, 0x0101, 0x0102, - 0x0201, 0x0202, 0x0203, 0x0204, - 0x0300, 0x0301, - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, - 0x0500, 0x0501, 0x0502, - 0x0700, 0x0701, 0x0702, - 0x0B00, 0x0B01, 0x0B02, 0x0B03, 0x0B04, 0x0B05, - 0x1000, - 0xFC0F, -}; - - -uint16_t fromClusterCode(uint8_t c) { - if (c >= sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0])) { - return 0xFFFF; - } - return pgm_read_word(&Z_ClusterNumber[c]); -} - - -uint8_t toClusterCode(uint16_t c) { - for (uint32_t i = 0; i < sizeof(Z_ClusterNumber)/sizeof(Z_ClusterNumber[0]); i++) { - if (c == pgm_read_word(&Z_ClusterNumber[i])) { - return i; - } - } - return 0xFF; -} - -class SBuffer hibernateDevice(const struct Z_Device &device) { - SBuffer buf(128); - - buf.add8(0x00); - buf.add16(device.shortaddr); - buf.add64(device.longaddr); - uint32_t endpoints = device.endpoints.size(); - if (endpoints > 254) { endpoints = 254; } - buf.add8(endpoints); - - for (std::vector::const_iterator ite = device.endpoints.begin() ; ite != device.endpoints.end(); ++ite) { - uint32_t ep_profile = *ite; - uint8_t endpoint = (ep_profile >> 16) & 0xFF; - uint16_t profileId = ep_profile & 0xFFFF; - - buf.add8(endpoint); - buf.add16(profileId); - for (std::vector::const_iterator itc = device.clusters_in.begin() ; itc != device.clusters_in.end(); ++itc) { - uint16_t cluster = *itc & 0xFFFF; - uint8_t c_endpoint = (*itc >> 16) & 0xFF; - - if (endpoint == c_endpoint) { - uint8_t clusterCode = toClusterCode(cluster); - if (0xFF != clusterCode) { buf.add8(clusterCode); } - } - } - buf.add8(0xFF); - - for (std::vector::const_iterator itc = device.clusters_out.begin() ; itc != device.clusters_out.end(); ++itc) { - uint16_t cluster = *itc & 0xFFFF; - uint8_t c_endpoint = (*itc >> 16) & 0xFF; - - if (endpoint == c_endpoint) { - uint8_t clusterCode = toClusterCode(cluster); - if (0xFF != clusterCode) { buf.add8(clusterCode); } - } - } - buf.add8(0xFF); - } - - - size_t model_len = device.modelId.length(); - if (model_len > 32) { model_len = 32; } - buf.addBuffer(device.modelId.c_str(), model_len); - buf.add8(0x00); - - - size_t manuf_len = device.manufacturerId.length(); - if (manuf_len > 32) {manuf_len = 32; } - buf.addBuffer(device.manufacturerId.c_str(), manuf_len); - buf.add8(0x00); - - - size_t frname_len = device.friendlyName.length(); - if (frname_len > 32) {frname_len = 32; } - buf.addBuffer(device.friendlyName.c_str(), frname_len); - buf.add8(0x00); - - - buf.set8(0, buf.len()); - - return buf; -} - -class SBuffer hibernateDevices(void) { - SBuffer buf(2048); - - size_t devices_size = zigbee_devices.devicesSize(); - if (devices_size > 32) { devices_size = 32; } - buf.add8(devices_size); - - for (uint32_t i = 0; i < devices_size; i++) { - const Z_Device & device = zigbee_devices.devicesAt(i); - const SBuffer buf_device = hibernateDevice(device); - buf.addBuffer(buf_device); - } - - size_t buf_len = buf.len(); - if (buf_len > 2040) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Devices list too big to fit in Flash (%d)"), buf_len); - } - - - char *hex_char = (char*) malloc((buf_len * 2) + 2); - if (hex_char) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "ZbFlashStore %s"), - ToHex_P(buf.getBuffer(), buf_len, hex_char, (buf_len * 2) + 2)); - free(hex_char); - } - - return buf; -} - -void hidrateDevices(const SBuffer &buf) { - uint32_t buf_len = buf.len(); - if (buf_len <= 10) { return; } - - uint32_t k = 0; - uint32_t num_devices = buf.get8(k++); - - for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) { - uint32_t dev_record_len = buf.get8(k); - - SBuffer buf_d = buf.subBuffer(k, dev_record_len); - - uint32_t d = 1; - uint16_t shortaddr = buf_d.get16(d); d += 2; - uint64_t longaddr = buf_d.get64(d); d += 8; - zigbee_devices.updateDevice(shortaddr, longaddr); - - uint32_t endpoints = buf_d.get8(d++); - for (uint32_t j = 0; j < endpoints; j++) { - uint8_t ep = buf_d.get8(d++); - uint16_t ep_profile = buf_d.get16(d); d += 2; - zigbee_devices.addEndointProfile(shortaddr, ep, ep_profile); - - - while (d < dev_record_len) { - uint8_t ep_cluster = buf_d.get8(d++); - if (0xFF == ep_cluster) { break; } - zigbee_devices.addCluster(shortaddr, ep, fromClusterCode(ep_cluster), false); - } - - while (d < dev_record_len) { - uint8_t ep_cluster = buf_d.get8(d++); - if (0xFF == ep_cluster) { break; } - zigbee_devices.addCluster(shortaddr, ep, fromClusterCode(ep_cluster), true); - } - } - - - char empty[] = ""; - - - uint32_t s_len = buf_d.strlen_s(d); - char *ptr = s_len ? buf_d.charptr(d) : empty; - zigbee_devices.setModelId(shortaddr, ptr); - d += s_len + 1; - - - s_len = buf_d.strlen_s(d); - ptr = s_len ? buf_d.charptr(d) : empty; - zigbee_devices.setManufId(shortaddr, ptr); - d += s_len + 1; - - - s_len = buf_d.strlen_s(d); - ptr = s_len ? buf_d.charptr(d) : empty; - zigbee_devices.setFriendlyName(shortaddr, ptr); - d += s_len + 1; - - - k += dev_record_len; - } -} - -void loadZigbeeDevices(void) { - z_flashdata_t flashdata; - memcpy_P(&flashdata, z_dev_start, sizeof(z_flashdata_t)); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "Zigbee signature in Flash: %08X - %d"), flashdata.name, flashdata.len); - - - if ((flashdata.name == ZIGB_NAME) && (flashdata.len > 0)) { - uint16_t buf_len = flashdata.len; - - SBuffer buf(buf_len); - buf.addBuffer(z_dev_start + sizeof(z_flashdata_t), buf_len); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee devices data in Flash (%d bytes)"), buf_len); - hidrateDevices(buf); - zigbee_devices.clean(); - } else { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "No zigbee devices data in Flash")); - } -} - -void saveZigbeeDevices(void) { - SBuffer buf = hibernateDevices(); - size_t buf_len = buf.len(); - if (buf_len > Z_MAX_FLASH) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Buffer too big to fit in Flash (%d bytes)"), buf_len); - return; - } - - - uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); - if (!spi_buffer) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); - return; - } - - ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - - z_flashdata_t *flashdata = (z_flashdata_t*)(spi_buffer + z_block_offset); - flashdata->name = ZIGB_NAME; - flashdata->len = buf_len; - flashdata->reserved = 0; - - memcpy(spi_buffer + z_block_offset + sizeof(z_flashdata_t), buf.getBuffer(), buf_len); - - - if (ESP.flashEraseSector(z_spi_start_sector)) { - ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - } - - free(spi_buffer); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data store in Flash (0x%08X - %d bytes)"), z_dev_start, buf_len); -} - - -void eraseZigbeeDevices(void) { - zigbee_devices.clean(); - - uint8_t *spi_buffer = (uint8_t*) malloc(z_spi_len); - if (!spi_buffer) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Cannot allocate 4KB buffer")); - return; - } - - ESP.flashRead(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - - - memset(spi_buffer + z_block_offset, 0xFF, z_block_len); - - - if (ESP.flashEraseSector(z_spi_start_sector)) { - ESP.flashWrite(z_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) spi_buffer, SPI_FLASH_SEC_SIZE); - } - - free(spi_buffer); - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Zigbee Devices Data erased (0x%08X - %d bytes)"), z_dev_start, z_block_len); -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" -#ifdef USE_ZIGBEE - - - - - -typedef union ZCLHeaderFrameControl_t { - struct { - uint8_t frame_type : 2; - uint8_t manuf_specific : 1; - uint8_t direction : 1; - uint8_t disable_def_resp : 1; - uint8_t reserved : 3; - } b; - uint32_t d8; -} ZCLHeaderFrameControl_t; - - -class ZCLFrame { -public: - - ZCLFrame(uint8_t frame_control, uint16_t manuf_code, uint8_t transact_seq, uint8_t cmd_id, - const char *buf, size_t buf_len, uint16_t clusterid, uint16_t groupid, - uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, - uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, - uint32_t timestamp): - _cmd_id(cmd_id), _manuf_code(manuf_code), _transact_seq(transact_seq), - _payload(buf_len ? buf_len : 250), - _cluster_id(clusterid), _group_id(groupid), - _srcaddr(srcaddr), _srcendpoint(srcendpoint), _dstendpoint(dstendpoint), _wasbroadcast(wasbroadcast), - _linkquality(linkquality), _securityuse(securityuse), _seqnumber(seqnumber), - _timestamp(timestamp) - { - _frame_control.d8 = frame_control; - _payload.addBuffer(buf, buf_len); - }; - - - void log(void) { - char hex_char[_payload.len()*2+2]; - ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); - Response_P(PSTR("{\"" D_JSON_ZIGBEEZCL_RECEIVED "\":{" - "\"groupid\":%d," "\"clusterid\":%d," "\"srcaddr\":\"0x%04X\"," - "\"srcendpoint\":%d," "\"dstendpoint\":%d," "\"wasbroadcast\":%d," - "\"" D_CMND_ZIGBEE_LINKQUALITY "\":%d," "\"securityuse\":%d," "\"seqnumber\":%d," - "\"timestamp\":%d," - "\"fc\":\"0x%02X\",\"manuf\":\"0x%04X\",\"transact\":%d," - "\"cmdid\":\"0x%02X\",\"payload\":\"%s\"}}"), - _group_id, _cluster_id, _srcaddr, - _srcendpoint, _dstendpoint, _wasbroadcast, - _linkquality, _securityuse, _seqnumber, - _timestamp, - _frame_control, _manuf_code, _transact_seq, _cmd_id, - hex_char); - if (Settings.flag3.tuya_serial_mqtt_publish) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - XdrvRulesProcess(); - } else { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); - } - } - - static ZCLFrame parseRawFrame(const SBuffer &buf, uint8_t offset, uint8_t len, uint16_t clusterid, uint16_t groupid, - uint16_t srcaddr, uint8_t srcendpoint, uint8_t dstendpoint, uint8_t wasbroadcast, - uint8_t linkquality, uint8_t securityuse, uint8_t seqnumber, - uint32_t timestamp) { - uint32_t i = offset; - ZCLHeaderFrameControl_t frame_control; - uint16_t manuf_code = 0; - uint8_t transact_seq; - uint8_t cmd_id; - - frame_control.d8 = buf.get8(i++); - if (frame_control.b.manuf_specific) { - manuf_code = buf.get16(i); - i += 2; - } - transact_seq = buf.get8(i++); - cmd_id = buf.get8(i++); - ZCLFrame zcl_frame(frame_control.d8, manuf_code, transact_seq, cmd_id, - (const char *)(buf.buf() + i), len + offset - i, - clusterid, groupid, - srcaddr, srcendpoint, dstendpoint, wasbroadcast, - linkquality, securityuse, seqnumber, - timestamp); - return zcl_frame; - } - - bool isClusterSpecificCommand(void) { - return _frame_control.b.frame_type & 1; - } - - static void generateAttributeName(const JsonObject& json, uint16_t cluster, uint16_t attr, char *key, size_t key_len); - void parseRawAttributes(JsonObject& json, uint8_t offset = 0); - void parseReadAttributes(JsonObject& json, uint8_t offset = 0); - void parseClusterSpecificCommand(JsonObject& json, uint8_t offset = 0); - void postProcessAttributes(uint16_t shortaddr, JsonObject& json); - - inline void setGroupId(uint16_t groupid) { - _group_id = groupid; - } - - inline void setClusterId(uint16_t clusterid) { - _cluster_id = clusterid; - } - - inline uint8_t getCmdId(void) const { - return _cmd_id; - } - - inline uint16_t getClusterId(void) const { - return _cluster_id; - } - - inline uint16_t getSrcEndpoint(void) const { - return _srcendpoint; - } - - const SBuffer &getPayload(void) const { - return _payload; - } - - uint16_t getManufCode(void) const { - return _manuf_code; - } - -private: - ZCLHeaderFrameControl_t _frame_control = { .d8 = 0 }; - uint16_t _manuf_code = 0; - uint8_t _transact_seq = 0; - uint8_t _cmd_id = 0; - uint16_t _cluster_id = 0; - uint16_t _group_id = 0; - SBuffer _payload; - - uint16_t _srcaddr; - uint8_t _srcendpoint; - uint8_t _dstendpoint; - uint8_t _wasbroadcast; - uint8_t _linkquality; - uint8_t _securityuse; - uint8_t _seqnumber; - uint32_t _timestamp; -}; - - - - - - -uint8_t toPercentageCR2032(uint32_t voltage) { - uint32_t percentage; - if (voltage < 2100) { - percentage = 0; - } else if (voltage < 2440) { - percentage = 6 - ((2440 - voltage) * 6) / 340; - } else if (voltage < 2740) { - percentage = 18 - ((2740 - voltage) * 12) / 300; - } else if (voltage < 2900) { - percentage = 42 - ((2900 - voltage) * 24) / 160; - } else if (voltage < 3000) { - percentage = 100 - ((3000 - voltage) * 58) / 100; - } else if (voltage >= 3000) { - percentage = 100; - } - return percentage; -} - - -uint32_t parseSingleAttribute(JsonObject& json, char *attrid_str, class SBuffer &buf, - uint32_t offset, uint32_t len) { - - uint32_t i = offset; - uint32_t attrtype = buf.get8(i++); - - - json[attrid_str] = (char*) nullptr; - - - switch (attrtype) { - case 0x00: - case 0xFF: - break; - case 0x10: - { - uint8_t val_bool = buf.get8(i++); - if (0xFF != val_bool) { - json[attrid_str] = (bool) (val_bool ? true : false); - } - } - break; - case 0x20: - { - uint8_t uint8_val = buf.get8(i); - i += 1; - if (0xFF != uint8_val) { - json[attrid_str] = uint8_val; - } - } - break; - case 0x21: - { - uint16_t uint16_val = buf.get16(i); - i += 2; - if (0xFFFF != uint16_val) { - json[attrid_str] = uint16_val; - } - } - break; - case 0x23: - { - uint32_t uint32_val = buf.get32(i); - i += 4; - if (0xFFFFFFFF != uint32_val) { - json[attrid_str] = uint32_val; - } - } - break; - - case 0x24: - case 0x25: - case 0x26: - case 0x27: - { - uint8_t len = attrtype - 0x1F; - - char hex[2*len+1]; - ToHex_P(buf.buf(i), len, hex, sizeof(hex)); - json[attrid_str] = hex; - i += len; - } - break; - case 0x28: - { - int8_t int8_val = buf.get8(i); - i += 1; - if (0x80 != int8_val) { - json[attrid_str] = int8_val; - } - } - break; - case 0x29: - { - int16_t int16_val = buf.get16(i); - i += 2; - if (0x8000 != int16_val) { - json[attrid_str] = int16_val; - } - } - break; - case 0x2B: - { - int32_t int32_val = buf.get32(i); - i += 4; - if (0x80000000 != int32_val) { - json[attrid_str] = int32_val; - } - } - break; - - case 0x2C: - case 0x2D: - case 0x2E: - case 0x2F: - { - uint8_t len = attrtype - 0x27; - - char hex[2*len+1]; - ToHex_P(buf.buf(i), len, hex, sizeof(hex)); - json[attrid_str] = hex; - i += len; - } - break; - - case 0x41: - case 0x42: - case 0x43: - case 0x44: - - { - bool parse_as_string = true; - uint32_t len = (attrtype <= 0x42) ? buf.get8(i) : buf.get16(i); - i += (attrtype <= 0x42) ? 1 : 2; - if (i + len > buf.len()) { - len = buf.len() - i; - } - - - if ((0x41 == attrtype) || (0x43 == attrtype)) { parse_as_string = false; } -# 318 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" - if (parse_as_string) { - char str[len+1]; - strncpy(str, buf.charptr(i), len); - str[len] = 0x00; - json[attrid_str] = str; - } else { - - char hex[2*len+1]; - ToHex_P(buf.buf(i), len, hex, sizeof(hex)); - json[attrid_str] = hex; - } - - i += len; - break; - } - i += buf.get8(i) + 1; - break; - - case 0x08: - case 0x18: - { - uint8_t uint8_val = buf.get8(i); - i += 1; - json[attrid_str] = uint8_val; - } - break; - case 0x09: - case 0x19: - { - uint16_t uint16_val = buf.get16(i); - i += 2; - json[attrid_str] = uint16_val; - } - break; - case 0x0B: - case 0x1B: - { - uint32_t uint32_val = buf.get32(i); - i += 4; - json[attrid_str] = uint32_val; - } - break; - - case 0x30: - case 0x31: - i += attrtype - 0x2F; - break; - - - case 0x39: - { - uint32_t uint32_val = buf.get32(i); - float * float_val = (float*) &uint32_val; - i += 4; - json[attrid_str] = *float_val; - } - break; - - case 0xE0: - case 0xE1: - case 0xE2: - i += 4; - break; - - case 0xE8: - case 0xE9: - i += 2; - break; - case 0xEA: - i += 4; - break; - - case 0xF0: - i += 8; - break; - case 0xF1: - i += 16; - break; - - - case 0x0A: - case 0x0C: - case 0x0D: - case 0x0E: - case 0x0F: - i += attrtype - 0x07; - break; - - case 0x1A: - case 0x1C: - case 0x1D: - case 0x1E: - case 0x1F: - i += attrtype - 0x17; - break; - - case 0x38: - i += 2; - break; - case 0x3A: - { - uint64_t uint64_val = buf.get64(i); - double * double_val = (double*) &uint64_val; - i += 8; - json[attrid_str] = *double_val; - } - break; - } - - - - - - - return i - offset; -} - - -void ZCLFrame::generateAttributeName(const JsonObject& json, uint16_t cluster, uint16_t attr, char *key, size_t key_len) { - uint32_t suffix = 1; - - snprintf_P(key, key_len, PSTR("%04X/%04X"), cluster, attr); - while (json.containsKey(key)) { - suffix++; - snprintf_P(key, key_len, PSTR("%04X/%04X+%d"), cluster, attr, suffix); - } -} - - -void ZCLFrame::parseRawAttributes(JsonObject& json, uint8_t offset) { - uint32_t i = offset; - uint32_t len = _payload.len(); - - while (len >= i + 3) { - uint16_t attrid = _payload.get16(i); - i += 2; - - char key[16]; - generateAttributeName(json, _cluster_id, attrid, key, sizeof(key)); - - - if ((0x0000 == _cluster_id) && (0xFF01 == attrid)) { - if (0x42 == _payload.get8(i)) { - _payload.set8(i, 0x41); - } - } - i += parseSingleAttribute(json, key, _payload, i, len); - } -} - - -void ZCLFrame::parseReadAttributes(JsonObject& json, uint8_t offset) { - uint32_t i = offset; - uint32_t len = _payload.len(); - - while (len - i >= 4) { - uint16_t attrid = _payload.get16(i); - i += 2; - uint8_t status = _payload.get8(i++); - - if (0 == status) { - char key[16]; - generateAttributeName(json, _cluster_id, attrid, key, sizeof(key)); - - i += parseSingleAttribute(json, key, _payload, i, len); - } - } -} - - - - -void ZCLFrame::parseClusterSpecificCommand(JsonObject& json, uint8_t offset) { - uint32_t i = offset; - uint32_t len = _payload.len(); - - char attrid_str[12]; - snprintf_P(attrid_str, sizeof(attrid_str), PSTR("%04X!%02X"), _cluster_id, _cmd_id); - - char hex_char[_payload.len()*2+2]; - ToHex_P((unsigned char*)_payload.getBuffer(), _payload.len(), hex_char, sizeof(hex_char)); - - json[attrid_str] = hex_char; -} - - - - -typedef int32_t (*Z_AttrConverter)(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr); -typedef struct Z_AttributeConverter { - uint16_t cluster; - uint16_t attribute; - const char * name; - Z_AttrConverter func; -} Z_AttributeConverter; - - -const Z_AttributeConverter Z_PostProcess[] PROGMEM = { - { 0x0000, 0x0000, "ZCLVersion", &Z_Copy }, - { 0x0000, 0x0001, "AppVersion", &Z_Copy }, - { 0x0000, 0x0002, "StackVersion", &Z_Copy }, - { 0x0000, 0x0003, "HWVersion", &Z_Copy }, - { 0x0000, 0x0004, "Manufacturer", &Z_ManufKeep }, - { 0x0000, 0x0005, D_JSON_MODEL D_JSON_ID, &Z_ModelKeep }, - { 0x0000, 0x0006, "DateCode", &Z_Copy }, - { 0x0000, 0x0007, "PowerSource", &Z_Copy }, - { 0x0000, 0x4000, "SWBuildID", &Z_Copy }, - { 0x0000, 0xFFFF, nullptr, &Z_Remove }, - - { 0x0000, 0xFF01, nullptr, &Z_AqaraSensor }, - - - { 0x0001, 0x0000, "MainsVoltage", &Z_Copy }, - { 0x0001, 0x0001, "MainsFrequency", &Z_Copy }, - { 0x0001, 0x0020, "BatteryVoltage", &Z_Copy }, - { 0x0001, 0x0021, "BatteryPercentageRemaining",&Z_Copy }, - - - { 0x0002, 0x0000, "CurrentTemperature", &Z_Copy }, - { 0x0002, 0x0001, "MinTempExperienced", &Z_Copy }, - { 0x0002, 0x0002, "MaxTempExperienced", &Z_Copy }, - { 0x0002, 0x0003, "OverTempTotalDwell", &Z_Copy }, - - - { 0x0006, 0x0000, "Power", &Z_Copy }, - { 0x0006, 0x8000, "Power", &Z_Copy }, - - - { 0x0007, 0x0000, "SwitchType", &Z_Copy }, - - - { 0x0008, 0x0000, "Dimmer", &Z_Copy }, -# 558 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" - { 0x0009, 0x0000, "AlarmCount", &Z_Copy }, - - { 0x000A, 0x0000, "Time", &Z_Copy }, - { 0x000A, 0x0001, "TimeStatus", &Z_Copy }, - { 0x000A, 0x0002, "TimeZone", &Z_Copy }, - { 0x000A, 0x0003, "DstStart", &Z_Copy }, - { 0x000A, 0x0004, "DstStart", &Z_Copy }, - { 0x000A, 0x0005, "DstShift", &Z_Copy }, - { 0x000A, 0x0006, "StandardTime", &Z_Copy }, - { 0x000A, 0x0007, "LocalTime", &Z_Copy }, - { 0x000A, 0x0008, "LastSetTime", &Z_Copy }, - { 0x000A, 0x0009, "ValidUntilTime", &Z_Copy }, - - { 0x000B, 0x0000, "LocationType", &Z_Copy }, - { 0x000B, 0x0000, "LocationMethod", &Z_Copy }, - { 0x000B, 0x0000, "LocationAge", &Z_Copy }, - { 0x000B, 0x0000, "QualityMeasure", &Z_Copy }, - { 0x000B, 0x0000, "NumberOfDevices", &Z_Copy }, - - { 0x000C, 0x0004, "ActiveText", &Z_Copy }, - { 0x000C, 0x001C, "Description", &Z_Copy }, - { 0x000C, 0x002E, "InactiveText", &Z_Copy }, - { 0x000C, 0x0041, "MaxPresentValue", &Z_Copy }, - { 0x000C, 0x0045, "MinPresentValue", &Z_Copy }, - { 0x000C, 0x0051, "OutOfService", &Z_Copy }, - { 0x000C, 0x0055, "AqaraRotate", &Z_Copy }, - { 0x000C, 0x0057, "PriorityArray", &Z_Copy }, - { 0x000C, 0x0067, "Reliability", &Z_Copy }, - { 0x000C, 0x0068, "RelinquishDefault", &Z_Copy }, - { 0x000C, 0x006A, "Resolution", &Z_Copy }, - { 0x000C, 0x006F, "StatusFlags", &Z_Copy }, - { 0x000C, 0x0075, "EngineeringUnits", &Z_Copy }, - { 0x000C, 0x0100, "ApplicationType", &Z_Copy }, - { 0x000C, 0xFF05, "Aqara_FF05", &Z_Copy }, - - { 0x0010, 0x0004, "ActiveText", &Z_Copy }, - { 0x0010, 0x001C, "Description", &Z_Copy }, - { 0x0010, 0x002E, "InactiveText", &Z_Copy }, - { 0x0010, 0x0042, "MinimumOffTime", &Z_Copy }, - { 0x0010, 0x0043, "MinimumOnTime", &Z_Copy }, - { 0x0010, 0x0051, "OutOfService", &Z_Copy }, - { 0x0010, 0x0054, "Polarity", &Z_Copy }, - { 0x0010, 0x0055, "PresentValue", &Z_Copy }, - { 0x0010, 0x0057, "PriorityArray", &Z_Copy }, - { 0x0010, 0x0067, "Reliability", &Z_Copy }, - { 0x0010, 0x0068, "RelinquishDefault", &Z_Copy }, - { 0x0010, 0x006F, "StatusFlags", &Z_Copy }, - { 0x0010, 0x0100, "ApplicationType", &Z_Copy }, - - { 0x0011, 0x0004, "ActiveText", &Z_Copy }, - { 0x0011, 0x001C, "Description", &Z_Copy }, - { 0x0011, 0x002E, "InactiveText", &Z_Copy }, - { 0x0011, 0x0042, "MinimumOffTime", &Z_Copy }, - { 0x0011, 0x0043, "MinimumOnTime", &Z_Copy }, - { 0x0011, 0x0051, "OutOfService", &Z_Copy }, - { 0x0011, 0x0055, "PresentValue", &Z_Copy }, - { 0x0011, 0x0057, "PriorityArray", &Z_Copy }, - { 0x0011, 0x0067, "Reliability", &Z_Copy }, - { 0x0011, 0x0068, "RelinquishDefault", &Z_Copy }, - { 0x0011, 0x006F, "StatusFlags", &Z_Copy }, - { 0x0011, 0x0100, "ApplicationType", &Z_Copy }, - - { 0x0012, 0x000E, "StateText", &Z_Copy }, - { 0x0012, 0x001C, "Description", &Z_Copy }, - { 0x0012, 0x004A, "NumberOfStates", &Z_Copy }, - { 0x0012, 0x0051, "OutOfService", &Z_Copy }, - { 0x0012, 0x0055, "PresentValue", &Z_AqaraCube }, - { 0x0012, 0x0067, "Reliability", &Z_Copy }, - { 0x0012, 0x006F, "StatusFlags", &Z_Copy }, - { 0x0012, 0x0100, "ApplicationType", &Z_Copy }, - - { 0x0013, 0x000E, "StateText", &Z_Copy }, - { 0x0013, 0x001C, "Description", &Z_Copy }, - { 0x0013, 0x004A, "NumberOfStates", &Z_Copy }, - { 0x0013, 0x0051, "OutOfService", &Z_Copy }, - { 0x0013, 0x0055, "PresentValue", &Z_Copy }, - { 0x0013, 0x0057, "PriorityArray", &Z_Copy }, - { 0x0013, 0x0067, "Reliability", &Z_Copy }, - { 0x0013, 0x0068, "RelinquishDefault", &Z_Copy }, - { 0x0013, 0x006F, "StatusFlags", &Z_Copy }, - { 0x0013, 0x0100, "ApplicationType", &Z_Copy }, - - { 0x0014, 0x000E, "StateText", &Z_Copy }, - { 0x0014, 0x001C, "Description", &Z_Copy }, - { 0x0014, 0x004A, "NumberOfStates", &Z_Copy }, - { 0x0014, 0x0051, "OutOfService", &Z_Copy }, - { 0x0014, 0x0055, "PresentValue", &Z_Copy }, - { 0x0014, 0x0067, "Reliability", &Z_Copy }, - { 0x0014, 0x0068, "RelinquishDefault", &Z_Copy }, - { 0x0014, 0x006F, "StatusFlags", &Z_Copy }, - { 0x0014, 0x0100, "ApplicationType", &Z_Copy }, - - { 0x001A, 0x0000, "TotalProfileNum", &Z_Copy }, - { 0x001A, 0x0001, "MultipleScheduling", &Z_Copy }, - { 0x001A, 0x0002, "EnergyFormatting", &Z_Copy }, - { 0x001A, 0x0003, "EnergyRemote", &Z_Copy }, - { 0x001A, 0x0004, "ScheduleMode", &Z_Copy }, - - { 0x0020, 0x0000, "CheckinInterval", &Z_Copy }, - { 0x0020, 0x0001, "LongPollInterval", &Z_Copy }, - { 0x0020, 0x0002, "ShortPollInterval", &Z_Copy }, - { 0x0020, 0x0003, "FastPollTimeout", &Z_Copy }, - { 0x0020, 0x0004, "CheckinIntervalMin", &Z_Copy }, - { 0x0020, 0x0005, "LongPollIntervalMin", &Z_Copy }, - { 0x0020, 0x0006, "FastPollTimeoutMax", &Z_Copy }, - - { 0x0100, 0x0000, "PhysicalClosedLimit", &Z_Copy }, - { 0x0100, 0x0001, "MotorStepSize", &Z_Copy }, - { 0x0100, 0x0002, "Status", &Z_Copy }, - { 0x0100, 0x0010, "ClosedLimit", &Z_Copy }, - { 0x0100, 0x0011, "Mode", &Z_Copy }, - - { 0x0101, 0x0000, "LockState", &Z_Copy }, - { 0x0101, 0x0001, "LockType", &Z_Copy }, - { 0x0101, 0x0002, "ActuatorEnabled", &Z_Copy }, - { 0x0101, 0x0003, "DoorState", &Z_Copy }, - { 0x0101, 0x0004, "DoorOpenEvents", &Z_Copy }, - { 0x0101, 0x0005, "DoorClosedEvents", &Z_Copy }, - { 0x0101, 0x0006, "OpenPeriod", &Z_Copy }, - - { 0x0101, 0x0055, "AqaraVibrationMode", &Z_AqaraVibration }, - { 0x0101, 0x0503, "AqaraVibrationsOrAngle", &Z_Copy }, - { 0x0101, 0x0505, "AqaraVibration505", &Z_Copy }, - { 0x0101, 0x0508, "AqaraAccelerometer", &Z_AqaraVibration }, - - { 0x0102, 0x0000, "WindowCoveringType", &Z_Copy }, - { 0x0102, 0x0001, "PhysicalClosedLimitLift",&Z_Copy }, - { 0x0102, 0x0002, "PhysicalClosedLimitTilt",&Z_Copy }, - { 0x0102, 0x0003, "CurrentPositionLift", &Z_Copy }, - { 0x0102, 0x0004, "CurrentPositionTilt", &Z_Copy }, - { 0x0102, 0x0005, "NumberofActuationsLift",&Z_Copy }, - { 0x0102, 0x0006, "NumberofActuationsTilt",&Z_Copy }, - { 0x0102, 0x0007, "ConfigStatus", &Z_Copy }, - { 0x0102, 0x0008, "CurrentPositionLiftPercentage",&Z_Copy }, - { 0x0102, 0x0009, "CurrentPositionTiltPercentage",&Z_Copy }, - { 0x0102, 0x0010, "InstalledOpenLimitLift",&Z_Copy }, - { 0x0102, 0x0011, "InstalledClosedLimitLift",&Z_Copy }, - { 0x0102, 0x0012, "InstalledOpenLimitTilt", &Z_Copy }, - { 0x0102, 0x0013, "InstalledClosedLimitTilt", &Z_Copy }, - { 0x0102, 0x0014, "VelocityLift",&Z_Copy }, - { 0x0102, 0x0015, "AccelerationTimeLift",&Z_Copy }, - { 0x0102, 0x0016, "DecelerationTimeLift", &Z_Copy }, - { 0x0102, 0x0017, "Mode",&Z_Copy }, - { 0x0102, 0x0018, "IntermediateSetpointsLift",&Z_Copy }, - { 0x0102, 0x0019, "IntermediateSetpointsTilt",&Z_Copy }, - - - { 0x0300, 0x0000, "Hue", &Z_Copy }, - { 0x0300, 0x0001, "Sat", &Z_Copy }, - { 0x0300, 0x0002, "RemainingTime", &Z_Copy }, - { 0x0300, 0x0003, "X", &Z_Copy }, - { 0x0300, 0x0004, "Y", &Z_Copy }, - { 0x0300, 0x0005, "DriftCompensation", &Z_Copy }, - { 0x0300, 0x0006, "CompensationText", &Z_Copy }, - { 0x0300, 0x0007, "CT", &Z_Copy }, - { 0x0300, 0x0008, "ColorMode", &Z_Copy }, - { 0x0300, 0x0010, "NumberOfPrimaries", &Z_Copy }, - { 0x0300, 0x0011, "Primary1X", &Z_Copy }, - { 0x0300, 0x0012, "Primary1Y", &Z_Copy }, - { 0x0300, 0x0013, "Primary1Intensity", &Z_Copy }, - { 0x0300, 0x0015, "Primary2X", &Z_Copy }, - { 0x0300, 0x0016, "Primary2Y", &Z_Copy }, - { 0x0300, 0x0017, "Primary2Intensity", &Z_Copy }, - { 0x0300, 0x0019, "Primary3X", &Z_Copy }, - { 0x0300, 0x001A, "Primary3Y", &Z_Copy }, - { 0x0300, 0x001B, "Primary3Intensity", &Z_Copy }, - { 0x0300, 0x0030, "WhitePointX", &Z_Copy }, - { 0x0300, 0x0031, "WhitePointY", &Z_Copy }, - { 0x0300, 0x0032, "ColorPointRX", &Z_Copy }, - { 0x0300, 0x0033, "ColorPointRY", &Z_Copy }, - { 0x0300, 0x0034, "ColorPointRIntensity", &Z_Copy }, - { 0x0300, 0x0036, "ColorPointGX", &Z_Copy }, - { 0x0300, 0x0037, "ColorPointGY", &Z_Copy }, - { 0x0300, 0x0038, "ColorPointGIntensity", &Z_Copy }, - { 0x0300, 0x003A, "ColorPointBX", &Z_Copy }, - { 0x0300, 0x003B, "ColorPointBY", &Z_Copy }, - { 0x0300, 0x003C, "ColorPointBIntensity", &Z_Copy }, - - - { 0x0400, 0x0000, D_JSON_ILLUMINANCE, &Z_Copy }, - { 0x0400, 0x0001, "MinMeasuredValue", &Z_Copy }, - { 0x0400, 0x0002, "MaxMeasuredValue", &Z_Copy }, - { 0x0400, 0x0003, "Tolerance", &Z_Copy }, - { 0x0400, 0x0004, "LightSensorType", &Z_Copy }, - { 0x0400, 0xFFFF, nullptr, &Z_Remove }, - - - { 0x0401, 0x0000, "LevelStatus", &Z_Copy }, - { 0x0401, 0x0001, "LightSensorType", &Z_Copy }, - { 0x0401, 0xFFFF, nullptr, &Z_Remove }, - - - { 0x0402, 0x0000, D_JSON_TEMPERATURE, &Z_FloatDiv100 }, - { 0x0402, 0x0001, "MinMeasuredValue", &Z_FloatDiv100 }, - { 0x0402, 0x0002, "MaxMeasuredValue", &Z_FloatDiv100 }, - { 0x0402, 0x0003, "Tolerance", &Z_FloatDiv100 }, - { 0x0402, 0xFFFF, nullptr, &Z_Remove }, - - - { 0x0403, 0x0000, D_JSON_PRESSURE_UNIT, &Z_AddPressureUnit }, - { 0x0403, 0x0000, D_JSON_PRESSURE, &Z_Copy }, - { 0x0403, 0x0001, "MinMeasuredValue", &Z_Copy }, - { 0x0403, 0x0002, "MaxMeasuredValue", &Z_Copy }, - { 0x0403, 0x0003, "Tolerance", &Z_Copy }, - { 0x0403, 0x0010, "ScaledValue", &Z_Copy }, - { 0x0403, 0x0011, "MinScaledValue", &Z_Copy }, - { 0x0403, 0x0012, "MaxScaledValue", &Z_Copy }, - { 0x0403, 0x0013, "ScaledTolerance", &Z_Copy }, - { 0x0403, 0x0014, "Scale", &Z_Copy }, - { 0x0403, 0xFFFF, nullptr, &Z_Remove }, - - - { 0x0404, 0x0000, D_JSON_FLOWRATE, &Z_FloatDiv10 }, - { 0x0404, 0x0001, "MinMeasuredValue", &Z_Copy }, - { 0x0404, 0x0002, "MaxMeasuredValue", &Z_Copy }, - { 0x0404, 0x0003, "Tolerance", &Z_Copy }, - { 0x0404, 0xFFFF, nullptr, &Z_Remove }, - - - { 0x0405, 0x0000, D_JSON_HUMIDITY, &Z_FloatDiv100 }, - { 0x0405, 0x0001, "MinMeasuredValue", &Z_Copy }, - { 0x0405, 0x0002, "MaxMeasuredValue", &Z_Copy }, - { 0x0405, 0x0003, "Tolerance", &Z_Copy }, - { 0x0405, 0xFFFF, nullptr, &Z_Remove }, - - - { 0x0406, 0x0000, OCCUPANCY, &Z_Copy }, - { 0x0406, 0x0001, "OccupancySensorType", &Z_Copy }, - { 0x0406, 0xFFFF, nullptr, &Z_Remove }, - - - { 0x0B01, 0x0000, "CompanyName", &Z_Copy }, - { 0x0B01, 0x0001, "MeterTypeID", &Z_Copy }, - { 0x0B01, 0x0004, "DataQualityID", &Z_Copy }, - { 0x0B01, 0x0005, "CustomerName", &Z_Copy }, - { 0x0B01, 0x0006, "Model", &Z_Copy }, - { 0x0B01, 0x0007, "PartNumber", &Z_Copy }, - { 0x0B01, 0x000A, "SoftwareRevision", &Z_Copy }, - { 0x0B01, 0x000C, "POD", &Z_Copy }, - { 0x0B01, 0x000D, "AvailablePower", &Z_Copy }, - { 0x0B01, 0x000E, "PowerThreshold", &Z_Copy }, - - - { 0x0B05, 0x0000, "NumberOfResets", &Z_Copy }, - { 0x0B05, 0x0001, "PersistentMemoryWrites",&Z_Copy }, - { 0x0B05, 0x011C, "LastMessageLQI", &Z_Copy }, - { 0x0B05, 0x011D, "LastMessageRSSI", &Z_Copy }, - -}; - - - -int32_t Z_ManufKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = value; - zigbee_devices.setManufId(shortaddr, value.as()); - return 1; -} - -int32_t Z_ModelKeep(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = value; - zigbee_devices.setModelId(shortaddr, value.as()); - return 1; -} - - - -int32_t Z_Remove(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - return 1; -} - - -int32_t Z_Copy(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = value; - return 1; -} - - -int32_t Z_AddPressureUnit(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = F(D_UNIT_PRESSURE); - return 0; -} - - -int32_t Z_FloatDiv100(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = ((float)value) / 100.0f; - return 1; -} - -int32_t Z_FloatDiv10(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = ((float)value) / 10.0f; - return 1; -} - - -int32_t Z_OccupancyCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) { - DynamicJsonBuffer jsonBuffer; - JsonObject& json = jsonBuffer.createObject(); - json[F(OCCUPANCY)] = 0; - zigbee_devices.jsonPublishNow(shortaddr, json); -} - - -int32_t Z_AqaraCube(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - json[new_name] = value; - int32_t val = value; - const __FlashStringHelper *aqara_cube = F("AqaraCube"); - const __FlashStringHelper *aqara_cube_side = F("AqaraCubeSide"); - const __FlashStringHelper *aqara_cube_from_side = F("AqaraCubeFromSide"); - - switch (val) { - case 0: - json[aqara_cube] = F("shake"); - break; - case 2: - json[aqara_cube] = F("wakeup"); - break; - case 3: - json[aqara_cube] = F("fall"); - break; - case 64 ... 127: - json[aqara_cube] = F("flip90"); - json[aqara_cube_side] = val % 8; - json[aqara_cube_from_side] = (val - 64) / 8; - break; - case 128 ... 132: - json[aqara_cube] = F("flip180"); - json[aqara_cube_side] = val - 128; - break; - case 256 ... 261: - json[aqara_cube] = F("slide"); - json[aqara_cube_side] = val - 256; - break; - case 512 ... 517: - json[aqara_cube] = F("tap"); - json[aqara_cube_side] = val - 512; - break; - } -# 915 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_5_converters.ino" - return 1; -} - - -int32_t Z_AqaraVibration(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - - switch (attr) { - case 0x0055: - { - int32_t ivalue = value; - const __FlashStringHelper * svalue; - switch (ivalue) { - case 1: svalue = F("vibrate"); break; - case 2: svalue = F("tilt"); break; - case 3: svalue = F("drop"); break; - default: svalue = F("unknown"); break; - } - json[new_name] = svalue; - } - break; - - - - - case 0x0508: - { - - - String hex = value; - SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); - int16_t x, y, z; - z = buf2.get16(0); - y = buf2.get16(2); - x = buf2.get16(4); - JsonArray& xyz = json.createNestedArray(new_name); - xyz.add(x); - xyz.add(y); - xyz.add(z); - - float X = x; - float Y = y; - float Z = z; - int32_t Angle_X = 0.5f + atanf(X/sqrtf(z*z+y*y)) * f_180pi; - int32_t Angle_Y = 0.5f + atanf(Y/sqrtf(x*x+z*z)) * f_180pi; - int32_t Angle_Z = 0.5f + atanf(Z/sqrtf(x*x+y*y)) * f_180pi; - - - - JsonArray& angles = json.createNestedArray(F("AqaraAngles")); - angles.add(Angle_X); - angles.add(Angle_Y); - angles.add(Angle_Z); - } - break; - } - return 1; -} - -int32_t Z_AqaraSensor(const class ZCLFrame *zcl, uint16_t shortaddr, JsonObject& json, const char *name, JsonVariant& value, const String &new_name, uint16_t cluster, uint16_t attr) { - String hex = value; - SBuffer buf2 = SBuffer::SBufferFromHex(hex.c_str(), hex.length()); - uint32_t i = 0; - uint32_t len = buf2.len(); - char tmp[] = "tmp"; - - JsonVariant sub_value; - - while (len - i >= 2) { - uint8_t attrid = buf2.get8(i++); - - i += parseSingleAttribute(json, tmp, buf2, i, len); - float val = json[tmp]; - json.remove(tmp); - if (0x01 == attrid) { - json[F(D_JSON_VOLTAGE)] = val / 1000.0f; - json[F("Battery")] = toPercentageCR2032(val); - } else if (0 == zcl->getManufCode()) { - - if (0x64 == attrid) { - json[F(D_JSON_TEMPERATURE)] = val / 100.0f; - } else if (0x65 == attrid) { - json[F(D_JSON_HUMIDITY)] = val / 100.0f; - } else if (0x66 == attrid) { - json[F(D_JSON_PRESSURE)] = val / 100.0f; - json[F(D_JSON_PRESSURE_UNIT)] = F(D_UNIT_PRESSURE); - } else if (0x01 == attrid) { - json[F(D_JSON_VOLTAGE)] = val / 1000.0f; - json[F("Battery")] = toPercentageCR2032(val); - } - } else if (0x115F == zcl->getManufCode()) { - - json[F("AqaraUnknown")] = val; - } - } - return 1; -} - - -void ZCLFrame::postProcessAttributes(uint16_t shortaddr, JsonObject& json) { - - for (auto kv : json) { - String key_string = kv.key; - const char * key = key_string.c_str(); - JsonVariant& value = kv.value; - - char * delimiter = strchr(key, '/'); - char * delimiter2 = strchr(key, '+'); - if (delimiter) { - uint16_t attribute; - uint16_t suffix = 1; - uint16_t cluster = strtoul(key, &delimiter, 16); - if (!delimiter2) { - attribute = strtoul(delimiter+1, nullptr, 16); - } else { - attribute = strtoul(delimiter+1, &delimiter2, 16); - suffix = strtoul(delimiter2+1, nullptr, 10); - } - - - for (uint32_t i = 0; i < sizeof(Z_PostProcess) / sizeof(Z_PostProcess[0]); i++) { - const Z_AttributeConverter *converter = &Z_PostProcess[i]; - uint16_t conv_cluster = pgm_read_word(&converter->cluster); - uint16_t conv_attribute = pgm_read_word(&converter->attribute); - - if ((conv_cluster == cluster) && - ((conv_attribute == attribute) || (conv_attribute == 0xFFFF)) ) { - String new_name_str = converter->name; - if (suffix > 1) { new_name_str += suffix; } - int32_t drop = (*converter->func)(this, shortaddr, json, key, value, new_name_str, conv_cluster, conv_attribute); - if (drop) { - json.remove(key); - } - - } - } - } - } -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_6_commands.ino" -#ifdef USE_ZIGBEE - - -typedef struct Z_CommandConverter { - const char * tasmota_cmd; - const char * zcl_cmd; -} Z_CommandConverter; - - -const Z_CommandConverter Z_Commands[] = { - { "Power", "0006!xx" }, - { "Dimmer", "0008!04/xx0A00" }, - { "Dimmer+", "0008!06/001902" }, - { "Dimmer-", "0008!06/011902" }, - { "DimmerStop", "0008!03" }, - { "ResetAlarm", "0009!00/xxyyyy" }, - { "ResetAllAlarms","0009!01" }, - { "Hue", "0300!00/xx000A00" }, - { "Sat", "0300!03/xx0A00" }, - { "HueSat", "0300!06/xxyy0A00" }, - { "Color", "0300!07/xxxxyyyy0A00" }, - { "CT", "0300!0A/xxxx0A00" }, - { "Shutter", "0102!xx" }, - { "ShutterOpen", "0102!00" }, - { "ShutterClose", "0102!01" }, - { "ShutterStop", "0102!02" }, - { "ShutterLift", "0102!05xx" }, - { "ShutterTilt", "0102!08xx" }, -}; - -#define ZLE(x) ((x) & 0xFF), ((x) >> 8) - - -const uint8_t CLUSTER_0006[] = { ZLE(0x0000) }; -const uint8_t CLUSTER_0008[] = { ZLE(0x0000) }; -const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; -const uint8_t CLUSTER_0300[] = { ZLE(0x0000), ZLE(0x0001), ZLE(0x0003), ZLE(0x0004), ZLE(0x0007) }; - -int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) { - size_t attrs_len = 0; - const uint8_t* attrs = nullptr; - - switch (cluster) { - case 0x0006: - attrs = CLUSTER_0006; - attrs_len = sizeof(CLUSTER_0006); - break; - case 0x0008: - attrs = CLUSTER_0008; - attrs_len = sizeof(CLUSTER_0008); - break; - case 0x0009: - attrs = CLUSTER_0009; - attrs_len = sizeof(CLUSTER_0009); - break; - case 0x0300: - attrs = CLUSTER_0300; - attrs_len = sizeof(CLUSTER_0300); - break; - } - if (attrs) { - ZigbeeZCLSend(shortaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false ); - } -} - - - -void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint) { - uint32_t wait_ms = 0; - - switch (cluster) { - case 0x0006: - case 0x0009: - wait_ms = 200; - break; - case 0x0008: - case 0x0300: - wait_ms = 1050; - break; - case 0x0102: - wait_ms = 10000; - break; - } - if (wait_ms) { - zigbee_devices.setTimer(shortaddr, wait_ms, cluster, endpoint, 0 , &Z_ReadAttrCallback); - } -} - -const __FlashStringHelper* zigbeeFindCommand(const char *command) { - char parm_uc[16]; - for (uint32_t i = 0; i < sizeof(Z_Commands) / sizeof(Z_Commands[0]); i++) { - const Z_CommandConverter *conv = &Z_Commands[i]; - if (0 == strcasecmp_P(command, conv->tasmota_cmd)) { - return (const __FlashStringHelper*) conv->zcl_cmd; - } - } - - return nullptr; -} - -inline bool isXYZ(char c) { - return (c >= 'x') && (c <= 'z'); -} - - -inline char hexDigit(uint32_t h) { - uint32_t nybble = h & 0x0F; - return (nybble > 9) ? 'A' - 10 + nybble : '0' + nybble; -} - - -String zigbeeCmdAddParams(const char *zcl_cmd_P, uint32_t x, uint32_t y, uint32_t z) { - size_t len = strlen_P(zcl_cmd_P); - char zcl_cmd[len+1]; - strcpy_P(zcl_cmd, zcl_cmd_P); - - char *p = zcl_cmd; - while (*p) { - if (isXYZ(*p) && (*p == *(p+1))) { - uint8_t val; - switch (*p) { - case 'x': - val = x & 0xFF; - x = x >> 8; - break; - case 'y': - val = y & 0xFF; - y = y >> 8; - break; - case 'z': - val = z & 0xFF; - z = z >> 8; - break; - } - *p = hexDigit(val >> 4); - *(p+1) = hexDigit(val); - p++; - } - p++; - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SendZCLCommand_P: zcl_cmd = %s"), zcl_cmd); - - return String(zcl_cmd); -} - -const char kZ_Alias[] PROGMEM = "OFF|" D_OFF "|" D_FALSE "|" D_STOP "|" "OPEN" "|" - "ON|" D_ON "|" D_TRUE "|" D_START "|" "CLOSE" "|" - "TOGGLE|" D_TOGGLE "|" - "ALL" ; - -const uint8_t kZ_Numbers[] PROGMEM = { 0,0,0,0,0, - 1,1,1,1,1, - 2,2, - 255 }; - - -uint32_t ZigbeeAliasOrNumber(const char *state_text) { - char command[16]; - int state_number = GetCommandCode(command, sizeof(command), state_text, kZ_Alias); - if (state_number >= 0) { - - return pgm_read_byte(kZ_Numbers + state_number); - } else { - - return strtoul(state_text, nullptr, 0); - } -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" -#ifdef USE_ZIGBEE - - - -const uint8_t ZIGBEE_STATUS_OK = 0; -const uint8_t ZIGBEE_STATUS_BOOT = 1; -const uint8_t ZIGBEE_STATUS_RESET_CONF = 2; -const uint8_t ZIGBEE_STATUS_STARTING = 3; -const uint8_t ZIGBEE_STATUS_PERMITJOIN_CLOSE = 20; -const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_60 = 21; -const uint8_t ZIGBEE_STATUS_PERMITJOIN_OPEN_XX = 22; -const uint8_t ZIGBEE_STATUS_DEVICE_ANNOUNCE = 30; -const uint8_t ZIGBEE_STATUS_NODE_DESC = 31; -const uint8_t ZIGBEE_STATUS_ACTIVE_EP = 32; -const uint8_t ZIGBEE_STATUS_SIMPLE_DESC = 33; -const uint8_t ZIGBEE_STATUS_DEVICE_INDICATION = 34; -const uint8_t ZIGBEE_STATUS_CC_VERSION = 50; -const uint8_t ZIGBEE_STATUS_CC_INFO = 51; -const uint8_t ZIGBEE_STATUS_UNSUPPORTED_VERSION = 98; -const uint8_t ZIGBEE_STATUS_ABORT = 99; - -typedef int32_t (*ZB_Func)(uint8_t value); -typedef int32_t (*ZB_RecvMsgFunc)(int32_t res, const class SBuffer &buf); - -typedef union Zigbee_Instruction { - struct { - uint8_t i; - uint8_t d8; - uint16_t d16; - } i; - const void *p; - - - -} Zigbee_Instruction; - - - - -typedef struct Zigbee_Instruction_Type { - uint8_t instr; - uint8_t data; -} Zigbee_Instruction_Type; - -enum Zigbee_StateMachine_Instruction_Set { - - ZGB_INSTR_4_BYTES = 0, - ZGB_INSTR_NOOP = 0, - ZGB_INSTR_LABEL, - ZGB_INSTR_GOTO, - ZGB_INSTR_ON_ERROR_GOTO, - ZGB_INSTR_ON_TIMEOUT_GOTO, - ZGB_INSTR_WAIT, - ZGB_INSTR_WAIT_FOREVER, - ZGB_INSTR_STOP, - - - ZGB_INSTR_8_BYTES = 0x80, - ZGB_INSTR_CALL = 0x80, - ZGB_INSTR_LOG, - ZGB_INSTR_MQTT_STATE, - ZGB_INSTR_SEND, - ZGB_INSTR_WAIT_UNTIL, - ZGB_INSTR_WAIT_RECV, - ZGB_ON_RECV_UNEXPECTED, - - - ZGB_INSTR_12_BYTES = 0xF0, - ZGB_INSTR_WAIT_RECV_CALL, -}; - -#define ZI_NOOP() { .i = { ZGB_INSTR_NOOP, 0x00, 0x0000} }, -#define ZI_LABEL(x) { .i = { ZGB_INSTR_LABEL, (x), 0x0000} }, -#define ZI_GOTO(x) { .i = { ZGB_INSTR_GOTO, (x), 0x0000} }, -#define ZI_ON_ERROR_GOTO(x) { .i = { ZGB_INSTR_ON_ERROR_GOTO, (x), 0x0000} }, -#define ZI_ON_TIMEOUT_GOTO(x) { .i = { ZGB_INSTR_ON_TIMEOUT_GOTO, (x), 0x0000} }, -#define ZI_WAIT(x) { .i = { ZGB_INSTR_WAIT, 0x00, (x)} }, -#define ZI_WAIT_FOREVER() { .i = { ZGB_INSTR_WAIT_FOREVER, 0x00, 0x0000} }, -#define ZI_STOP(x) { .i = { ZGB_INSTR_STOP, (x), 0x0000} }, - -#define ZI_CALL(f,x) { .i = { ZGB_INSTR_CALL, (x), 0x0000} }, { .p = (const void*)(f) }, -#define ZI_LOG(x,m) { .i = { ZGB_INSTR_LOG, (x), 0x0000 } }, { .p = ((const void*)(m)) }, -#define ZI_MQTT_STATE(x,m) { .i = { ZGB_INSTR_MQTT_STATE, (x), 0x0000 } }, { .p = ((const void*)(m)) }, -#define ZI_ON_RECV_UNEXPECTED(f) { .i = { ZGB_ON_RECV_UNEXPECTED, 0x00, 0x0000} }, { .p = (const void*)(f) }, -#define ZI_SEND(m) { .i = { ZGB_INSTR_SEND, sizeof(m), 0x0000} }, { .p = (const void*)(m) }, -#define ZI_WAIT_RECV(x,m) { .i = { ZGB_INSTR_WAIT_RECV, sizeof(m), (x)} }, { .p = (const void*)(m) }, -#define ZI_WAIT_UNTIL(x,m) { .i = { ZGB_INSTR_WAIT_UNTIL, sizeof(m), (x)} }, { .p = (const void*)(m) }, -#define ZI_WAIT_RECV_FUNC(x,m,f) { .i = { ZGB_INSTR_WAIT_RECV_CALL, sizeof(m), (x)} }, { .p = (const void*)(m) }, { .p = (const void*)(f) }, - - -const uint8_t ZIGBEE_LABEL_START = 10; -const uint8_t ZIGBEE_LABEL_READY = 20; -const uint8_t ZIGBEE_LABEL_MAIN_LOOP = 21; -const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_CLOSE = 30; -const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60 = 31; -const uint8_t ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX = 32; - -const uint8_t ZIGBEE_LABEL_ABORT = 99; -const uint8_t ZIGBEE_LABEL_UNSUPPORTED_VERSION = 98; - -struct ZigbeeStatus { - bool active = true; - bool state_machine = false; - bool state_waiting = false; - bool state_no_timeout = false; - bool ready = false; - uint8_t on_error_goto = ZIGBEE_LABEL_ABORT; - uint8_t on_timeout_goto = ZIGBEE_LABEL_ABORT; - int16_t pc = 0; - uint32_t next_timeout = 0; - - uint8_t *recv_filter = nullptr; - bool recv_until = false; - size_t recv_filter_len = 0; - ZB_RecvMsgFunc recv_func = nullptr; - ZB_RecvMsgFunc recv_unexpected = nullptr; - - bool init_phase = true; -}; -struct ZigbeeStatus zigbee; - -SBuffer *zigbee_buffer = nullptr; - - - - - -#define Z_B0(a) (uint8_t)( ((a) ) & 0xFF ) -#define Z_B1(a) (uint8_t)( ((a) >> 8) & 0xFF ) -#define Z_B2(a) (uint8_t)( ((a) >> 16) & 0xFF ) -#define Z_B3(a) (uint8_t)( ((a) >> 24) & 0xFF ) -#define Z_B4(a) (uint8_t)( ((a) >> 32) & 0xFF ) -#define Z_B5(a) (uint8_t)( ((a) >> 40) & 0xFF ) -#define Z_B6(a) (uint8_t)( ((a) >> 48) & 0xFF ) -#define Z_B7(a) (uint8_t)( ((a) >> 56) & 0xFF ) - -#define ZBM(n,x...) const uint8_t n[] PROGMEM = { x }; - -#define USE_ZIGBEE_CHANNEL_MASK (1 << (USE_ZIGBEE_CHANNEL)) - - - -ZBM(ZBS_RESET, Z_AREQ | Z_SYS, SYS_RESET, 0x00 ) -ZBM(ZBR_RESET, Z_AREQ | Z_SYS, SYS_RESET_IND ) - -ZBM(ZBS_VERSION, Z_SREQ | Z_SYS, SYS_VERSION ) -ZBM(ZBR_VERSION, Z_SRSP | Z_SYS, SYS_VERSION ) - - -ZBM(ZBS_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_READ, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, 0x00 ) -ZBM(ZBR_ZNPHC, Z_SRSP | Z_SYS, SYS_OSAL_NV_READ, Z_Success, 0x01 , 0x55) - - -ZBM(ZBS_PAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PANID ) -ZBM(ZBR_PAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_PANID, 0x02 , - Z_B0(USE_ZIGBEE_PANID), Z_B1(USE_ZIGBEE_PANID) ) - -ZBM(ZBS_EXTPAN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_EXTENDED_PAN_ID ) -ZBM(ZBR_EXTPAN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_EXTENDED_PAN_ID, - 0x08 , - Z_B0(USE_ZIGBEE_EXTPANID), Z_B1(USE_ZIGBEE_EXTPANID), Z_B2(USE_ZIGBEE_EXTPANID), Z_B3(USE_ZIGBEE_EXTPANID), - Z_B4(USE_ZIGBEE_EXTPANID), Z_B5(USE_ZIGBEE_EXTPANID), Z_B6(USE_ZIGBEE_EXTPANID), Z_B7(USE_ZIGBEE_EXTPANID), - ) - -ZBM(ZBS_CHANN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_CHANLIST ) -ZBM(ZBR_CHANN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_CHANLIST, - 0x04 , - Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK), - ) - -ZBM(ZBS_PFGK, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEY ) -ZBM(ZBR_PFGK, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_PRECFGKEY, - 0x10 , - Z_B0(USE_ZIGBEE_PRECFGKEY_L), Z_B1(USE_ZIGBEE_PRECFGKEY_L), Z_B2(USE_ZIGBEE_PRECFGKEY_L), Z_B3(USE_ZIGBEE_PRECFGKEY_L), - Z_B4(USE_ZIGBEE_PRECFGKEY_L), Z_B5(USE_ZIGBEE_PRECFGKEY_L), Z_B6(USE_ZIGBEE_PRECFGKEY_L), Z_B7(USE_ZIGBEE_PRECFGKEY_L), - Z_B0(USE_ZIGBEE_PRECFGKEY_H), Z_B1(USE_ZIGBEE_PRECFGKEY_H), Z_B2(USE_ZIGBEE_PRECFGKEY_H), Z_B3(USE_ZIGBEE_PRECFGKEY_H), - Z_B4(USE_ZIGBEE_PRECFGKEY_H), Z_B5(USE_ZIGBEE_PRECFGKEY_H), Z_B6(USE_ZIGBEE_PRECFGKEY_H), Z_B7(USE_ZIGBEE_PRECFGKEY_H), - - ) - -ZBM(ZBS_PFGKEN, Z_SREQ | Z_SAPI, SAPI_READ_CONFIGURATION, CONF_PRECFGKEYS_ENABLE ) -ZBM(ZBR_PFGKEN, Z_SRSP | Z_SAPI, SAPI_READ_CONFIGURATION, Z_Success, CONF_PRECFGKEYS_ENABLE, - 0x01 , 0x00 ) - - - -ZBM(ZBR_W_OK, Z_SRSP | Z_SAPI, SAPI_WRITE_CONFIGURATION, Z_Success ) -ZBM(ZBR_WNV_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_WRITE, Z_Success ) - - -ZBM(ZBS_FACTRES, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 , 0x02 ) - -ZBM(ZBS_W_PAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PANID, 0x02 , Z_B0(USE_ZIGBEE_PANID), Z_B1(USE_ZIGBEE_PANID) ) - -ZBM(ZBS_W_EXTPAN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_EXTENDED_PAN_ID, 0x08 , - Z_B0(USE_ZIGBEE_EXTPANID), Z_B1(USE_ZIGBEE_EXTPANID), Z_B2(USE_ZIGBEE_EXTPANID), Z_B3(USE_ZIGBEE_EXTPANID), - Z_B4(USE_ZIGBEE_EXTPANID), Z_B5(USE_ZIGBEE_EXTPANID), Z_B6(USE_ZIGBEE_EXTPANID), Z_B7(USE_ZIGBEE_EXTPANID) - ) - -ZBM(ZBS_W_CHANN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_CHANLIST, 0x04 , - Z_B0(USE_ZIGBEE_CHANNEL_MASK), Z_B1(USE_ZIGBEE_CHANNEL_MASK), Z_B2(USE_ZIGBEE_CHANNEL_MASK), Z_B3(USE_ZIGBEE_CHANNEL_MASK), - ) - -ZBM(ZBS_W_LOGTYP, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_LOGICAL_TYPE, 0x01 , 0x00 ) - -ZBM(ZBS_W_PFGK, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEY, - 0x10 , - Z_B0(USE_ZIGBEE_PRECFGKEY_L), Z_B1(USE_ZIGBEE_PRECFGKEY_L), Z_B2(USE_ZIGBEE_PRECFGKEY_L), Z_B3(USE_ZIGBEE_PRECFGKEY_L), - Z_B4(USE_ZIGBEE_PRECFGKEY_L), Z_B5(USE_ZIGBEE_PRECFGKEY_L), Z_B6(USE_ZIGBEE_PRECFGKEY_L), Z_B7(USE_ZIGBEE_PRECFGKEY_L), - Z_B0(USE_ZIGBEE_PRECFGKEY_H), Z_B1(USE_ZIGBEE_PRECFGKEY_H), Z_B2(USE_ZIGBEE_PRECFGKEY_H), Z_B3(USE_ZIGBEE_PRECFGKEY_H), - Z_B4(USE_ZIGBEE_PRECFGKEY_H), Z_B5(USE_ZIGBEE_PRECFGKEY_H), Z_B6(USE_ZIGBEE_PRECFGKEY_H), Z_B7(USE_ZIGBEE_PRECFGKEY_H), - - ) - -ZBM(ZBS_W_PFGKEN, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_PRECFGKEYS_ENABLE, 0x01 , 0x00 ) - -ZBM(ZBS_WNV_SECMODE, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(CONF_TCLK_TABLE_START), Z_B1(CONF_TCLK_TABLE_START), - 0x00 , 0x20 , - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x5a, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6c, - 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x39, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) - -ZBM(ZBS_W_ZDODCB, Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_ZDO_DIRECT_CB, 0x01 , 0x01 ) - -ZBM(ZBS_WNV_INITZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_ITEM_INIT, ZNP_HAS_CONFIGURED & 0xFF, ZNP_HAS_CONFIGURED >> 8, - 0x01, 0x00 , 0x01 , 0x00 ) - - -ZBM(ZBR_WNV_INIT_OK, Z_SRSP | Z_SYS, SYS_OSAL_NV_ITEM_INIT ) - - -ZBM(ZBS_WNV_ZNPHC, Z_SREQ | Z_SYS, SYS_OSAL_NV_WRITE, Z_B0(ZNP_HAS_CONFIGURED), Z_B1(ZNP_HAS_CONFIGURED), - 0x00 , 0x01 , 0x55 ) - -ZBM(ZBS_STARTUPFROMAPP, Z_SREQ | Z_ZDO, ZDO_STARTUP_FROM_APP, 100, 0 ) -ZBM(ZBR_STARTUPFROMAPP, Z_SRSP | Z_ZDO, ZDO_STARTUP_FROM_APP ) -ZBM(AREQ_STARTUPFROMAPP, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND, ZDO_DEV_ZB_COORD ) - -ZBM(ZBS_GETDEVICEINFO, Z_SREQ | Z_UTIL, Z_UTIL_GET_DEVICE_INFO ) -ZBM(ZBR_GETDEVICEINFO, Z_SRSP | Z_UTIL, Z_UTIL_GET_DEVICE_INFO, Z_Success ) -# 271 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" -ZBM(ZBS_ZDO_NODEDESCREQ, Z_SREQ | Z_ZDO, ZDO_NODE_DESC_REQ, 0x00, 0x00 , 0x00, 0x00 ) -ZBM(ZBR_ZDO_NODEDESCREQ, Z_SRSP | Z_ZDO, ZDO_NODE_DESC_REQ, Z_Success ) - -ZBM(AREQ_ZDO_NODEDESCRSP, Z_AREQ | Z_ZDO, ZDO_NODE_DESC_RSP) -# 289 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_7_statemachine.ino" -ZBM(ZBS_ZDO_ACTIVEEPREQ, Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, 0x00, 0x00, 0x00, 0x00) -ZBM(ZBR_ZDO_ACTIVEEPREQ, Z_SRSP | Z_ZDO, ZDO_ACTIVE_EP_REQ, Z_Success) -ZBM(ZBR_ZDO_ACTIVEEPRSP_NONE, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 , Z_Success, - 0x00, 0x00 , 0x00 ) -ZBM(ZBR_ZDO_ACTIVEEPRSP_OK, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP, 0x00, 0x00 , Z_Success, - 0x00, 0x00 , 0x02 , 0x0B, 0x01 ) - - -ZBM(ZBS_AF_REGISTER01, Z_SREQ | Z_AF, AF_REGISTER, 0x01 , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), - 0x05, 0x00 , 0x00 , 0x00 , - 0x00 , 0x00 ) -ZBM(ZBR_AF_REGISTER, Z_SRSP | Z_AF, AF_REGISTER, Z_Success) -ZBM(ZBS_AF_REGISTER0B, Z_SREQ | Z_AF, AF_REGISTER, 0x0B , Z_B0(Z_PROF_HA), Z_B1(Z_PROF_HA), - 0x05, 0x00 , 0x00 , 0x00 , - 0x00 , 0x00 ) - -ZBM(ZBS_PERMITJOINREQ_CLOSE, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x02 , - 0x00, 0x00 , 0x00 , 0x00 ) -ZBM(ZBS_PERMITJOINREQ_OPEN_60, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x0F , - 0xFC, 0xFF , 60 , 0x00 ) -ZBM(ZBS_PERMITJOINREQ_OPEN_XX, Z_SREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, 0x0F , - 0xFC, 0xFF , 0xFF , 0x00 ) -ZBM(ZBR_PERMITJOINREQ, Z_SRSP | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_REQ, Z_Success) -ZBM(ZBR_PERMITJOIN_AREQ_CLOSE, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0x00 ) -ZBM(ZBR_PERMITJOIN_AREQ_OPEN_60, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 60 ) -ZBM(ZBR_PERMITJOIN_AREQ_OPEN_FF, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND, 0xFF ) -ZBM(ZBR_PERMITJOIN_AREQ_RSP, Z_AREQ | Z_ZDO, ZDO_MGMT_PERMIT_JOIN_RSP, 0x00, 0x00 , Z_Success ) - -static const Zigbee_Instruction zb_prog[] PROGMEM = { - ZI_LABEL(0) - ZI_NOOP() - ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) - ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT) - ZI_ON_RECV_UNEXPECTED(&Z_Recv_Default) - ZI_WAIT(10500) - ZI_ON_ERROR_GOTO(50) - - - - ZI_SEND(ZBS_RESET) - ZI_WAIT_RECV_FUNC(5000, ZBR_RESET, &Z_Reboot) - ZI_WAIT(100) - ZI_LOG(LOG_LEVEL_DEBUG, D_LOG_ZIGBEE "checking device configuration") - ZI_SEND(ZBS_ZNPHC) - ZI_WAIT_RECV(2000, ZBR_ZNPHC) - ZI_SEND(ZBS_VERSION) - ZI_WAIT_RECV_FUNC(2000, ZBR_VERSION, &Z_ReceiveCheckVersion) - ZI_SEND(ZBS_PAN) - ZI_WAIT_RECV(1000, ZBR_PAN) - ZI_SEND(ZBS_EXTPAN) - ZI_WAIT_RECV(1000, ZBR_EXTPAN) - ZI_SEND(ZBS_CHANN) - ZI_WAIT_RECV(1000, ZBR_CHANN) - ZI_SEND(ZBS_PFGK) - ZI_WAIT_RECV(1000, ZBR_PFGK) - ZI_SEND(ZBS_PFGKEN) - ZI_WAIT_RECV(1000, ZBR_PFGKEN) - - - - ZI_LABEL(ZIGBEE_LABEL_START) - ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, "Configured, starting coordinator") - - ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) - - -ZI_SEND(ZBS_STARTUPFROMAPP) - ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) - ZI_WAIT_UNTIL(10000, AREQ_STARTUPFROMAPP) - ZI_SEND(ZBS_GETDEVICEINFO) - ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo) - - ZI_SEND(ZBS_ZDO_NODEDESCREQ) - ZI_WAIT_RECV(1000, ZBR_ZDO_NODEDESCREQ) - ZI_WAIT_UNTIL(5000, AREQ_ZDO_NODEDESCRSP) - ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) - ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) - ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_NONE) - ZI_SEND(ZBS_AF_REGISTER01) - ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) - ZI_SEND(ZBS_AF_REGISTER0B) - ZI_WAIT_RECV(1000, ZBR_AF_REGISTER) - - - ZI_SEND(ZBS_ZDO_ACTIVEEPREQ) - ZI_WAIT_RECV(1000, ZBR_ZDO_ACTIVEEPREQ) - ZI_WAIT_UNTIL(1000, ZBR_ZDO_ACTIVEEPRSP_OK) - ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) - ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) - ZI_WAIT_UNTIL(1000, ZBR_PERMITJOIN_AREQ_RSP) - - - - - - - ZI_LABEL(ZIGBEE_LABEL_READY) - ZI_MQTT_STATE(ZIGBEE_STATUS_OK, "Started") - ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "Zigbee started") - ZI_CALL(&Z_State_Ready, 1) - ZI_CALL(&Z_Load_Devices, 0) - ZI_LABEL(ZIGBEE_LABEL_MAIN_LOOP) - ZI_WAIT_FOREVER() - ZI_GOTO(ZIGBEE_LABEL_READY) - - ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_CLOSE) - - ZI_SEND(ZBS_PERMITJOINREQ_CLOSE) - ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) - - - ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) - - ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60) - - ZI_SEND(ZBS_PERMITJOINREQ_OPEN_60) - ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) - - - ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) - - ZI_LABEL(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX) - - ZI_SEND(ZBS_PERMITJOINREQ_OPEN_XX) - ZI_WAIT_RECV(1000, ZBR_PERMITJOINREQ) - - - ZI_GOTO(ZIGBEE_LABEL_MAIN_LOOP) - - ZI_LABEL(50) - ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, "Reseting configuration") - - ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT) - ZI_SEND(ZBS_FACTRES) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_RESET) - ZI_WAIT_RECV(5000, ZBR_RESET) - ZI_SEND(ZBS_W_PAN) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_W_EXTPAN) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_W_CHANN) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_W_LOGTYP) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_W_PFGK) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_W_PFGKEN) - ZI_WAIT_RECV(1000, ZBR_W_OK) - ZI_SEND(ZBS_WNV_SECMODE) - ZI_WAIT_RECV(1000, ZBR_WNV_OK) - ZI_SEND(ZBS_W_ZDODCB) - ZI_WAIT_RECV(1000, ZBR_W_OK) - - ZI_SEND(ZBS_WNV_INITZNPHC) - ZI_WAIT_RECV_FUNC(1000, ZBR_WNV_INIT_OK, &Z_CheckNVWrite) - ZI_SEND(ZBS_WNV_ZNPHC) - ZI_WAIT_RECV(1000, ZBR_WNV_OK) - - - ZI_GOTO(ZIGBEE_LABEL_START) - - ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION) - ZI_MQTT_STATE(ZIGBEE_STATUS_UNSUPPORTED_VERSION, "Only ZNP 1.2 is currently supported") - ZI_GOTO(ZIGBEE_LABEL_ABORT) - - ZI_LABEL(ZIGBEE_LABEL_ABORT) - ZI_MQTT_STATE(ZIGBEE_STATUS_ABORT, "Abort") - ZI_LOG(LOG_LEVEL_ERROR, D_LOG_ZIGBEE "Abort") - ZI_STOP(ZIGBEE_LABEL_ABORT) -}; - -uint8_t ZigbeeGetInstructionSize(uint8_t instr) { - if (instr >= ZGB_INSTR_12_BYTES) { - return 3; - } else if (instr >= ZGB_INSTR_8_BYTES) { - return 2; - } else { - return 1; - } -} - -void ZigbeeGotoLabel(uint8_t label) { - - uint16_t goto_pc = 0xFFFF; - uint8_t cur_instr = 0; - uint8_t cur_d8 = 0; - uint8_t cur_instr_len = 1; - - for (uint32_t i = 0; i < sizeof(zb_prog)/sizeof(zb_prog[0]); i += cur_instr_len) { - const Zigbee_Instruction *cur_instr_line = &zb_prog[i]; - cur_instr = pgm_read_byte(&cur_instr_line->i.i); - cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); - - - if (ZGB_INSTR_LABEL == cur_instr) { - - if (label == cur_d8) { - - zigbee.pc = i; - zigbee.state_machine = true; - zigbee.state_waiting = false; - return; - } - } - - cur_instr_len = ZigbeeGetInstructionSize(cur_instr); - } - - - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Goto label not found, label=%d pc=%d"), label, zigbee.pc); - if (ZIGBEE_LABEL_ABORT != label) { - - ZigbeeGotoLabel(ZIGBEE_LABEL_ABORT); - } else { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Label Abort (%d) not present, aborting Zigbee"), ZIGBEE_LABEL_ABORT); - zigbee.state_machine = false; - zigbee.active = false; - } -} - -void ZigbeeStateMachine_Run(void) { - uint8_t cur_instr = 0; - uint8_t cur_d8 = 0; - uint16_t cur_d16 = 0; - const void* cur_ptr1 = nullptr; - const void* cur_ptr2 = nullptr; - uint32_t now = millis(); - - if (zigbee.state_waiting) { - - if ((zigbee.next_timeout) && (now > zigbee.next_timeout)) { - - if (!zigbee.state_no_timeout) { - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "timeout, goto label %d"), zigbee.on_timeout_goto); - ZigbeeGotoLabel(zigbee.on_timeout_goto); - } else { - zigbee.state_waiting = false; - } - } - } - - while ((zigbee.state_machine) && (!zigbee.state_waiting)) { - - zigbee.recv_filter = nullptr; - zigbee.recv_func = nullptr; - zigbee.recv_until = false; - zigbee.state_no_timeout = false; - - if (zigbee.pc > (sizeof(zb_prog)/sizeof(zb_prog[0]))) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Invalid pc: %d, aborting"), zigbee.pc); - zigbee.pc = -1; - } - if (zigbee.pc < 0) { - zigbee.state_machine = false; - return; - } - - - - const Zigbee_Instruction *cur_instr_line = &zb_prog[zigbee.pc]; - cur_instr = pgm_read_byte(&cur_instr_line->i.i); - cur_d8 = pgm_read_byte(&cur_instr_line->i.d8); - cur_d16 = pgm_read_word(&cur_instr_line->i.d16); - if (cur_instr >= ZGB_INSTR_8_BYTES) { - cur_instr_line++; - cur_ptr1 = cur_instr_line->p; - } - if (cur_instr >= ZGB_INSTR_12_BYTES) { - cur_instr_line++; - cur_ptr2 = cur_instr_line->p; - } - - zigbee.pc += ZigbeeGetInstructionSize(cur_instr); - - switch (cur_instr) { - case ZGB_INSTR_NOOP: - case ZGB_INSTR_LABEL: - break; - case ZGB_INSTR_GOTO: - ZigbeeGotoLabel(cur_d8); - break; - case ZGB_INSTR_ON_ERROR_GOTO: - zigbee.on_error_goto = cur_d8; - break; - case ZGB_INSTR_ON_TIMEOUT_GOTO: - zigbee.on_timeout_goto = cur_d8; - break; - case ZGB_INSTR_WAIT: - zigbee.next_timeout = now + cur_d16; - zigbee.state_waiting = true; - zigbee.state_no_timeout = true; - break; - case ZGB_INSTR_WAIT_FOREVER: - zigbee.next_timeout = 0; - zigbee.state_waiting = true; - - break; - case ZGB_INSTR_STOP: - zigbee.state_machine = false; - if (cur_d8) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Stopping (%d)"), cur_d8); - } - break; - case ZGB_INSTR_CALL: - if (cur_ptr1) { - uint32_t res; - res = (*((ZB_Func)cur_ptr1))(cur_d8); - if (res > 0) { - ZigbeeGotoLabel(res); - continue; - } else if (res == 0) { - - } else if (res == -1) { - - } else { - ZigbeeGotoLabel(zigbee.on_error_goto); - continue; - } - } - break; - case ZGB_INSTR_LOG: - AddLog_P(cur_d8, (char*) cur_ptr1); - break; - case ZGB_INSTR_MQTT_STATE: - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{\"Status\":%d,\"Message\":\"%s\"}}"), - cur_d8, (char*) cur_ptr1); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); - XdrvRulesProcess(); - break; - case ZGB_INSTR_SEND: - ZigbeeZNPSend((uint8_t*) cur_ptr1, cur_d8 ); - break; - case ZGB_INSTR_WAIT_UNTIL: - zigbee.recv_until = true; - case ZGB_INSTR_WAIT_RECV: - zigbee.recv_filter = (uint8_t *) cur_ptr1; - zigbee.recv_filter_len = cur_d8; - zigbee.next_timeout = now + cur_d16; - zigbee.state_waiting = true; - break; - case ZGB_ON_RECV_UNEXPECTED: - zigbee.recv_unexpected = (ZB_RecvMsgFunc) cur_ptr1; - break; - case ZGB_INSTR_WAIT_RECV_CALL: - zigbee.recv_filter = (uint8_t *) cur_ptr1; - zigbee.recv_filter_len = cur_d8; - zigbee.recv_func = (ZB_RecvMsgFunc) cur_ptr2; - zigbee.next_timeout = now + cur_d16; - zigbee.state_waiting = true; - break; - } - } -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" -#ifdef USE_ZIGBEE - -int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) { - - - - - - - - Z_IEEEAddress long_adr = buf.get64(3); - Z_ShortAddress short_adr = buf.get16(11); - uint8_t device_type = buf.get8(13); - uint8_t device_state = buf.get8(14); - uint8_t device_associated = buf.get8(15); - - - localIEEEAddr = long_adr; - - char hex[20]; - Uint64toHex(long_adr, hex, 64); - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" - ",\"DeviceType\":%d,\"DeviceState\":%d" - ",\"NumAssocDevices\":%d"), - ZIGBEE_STATUS_CC_INFO, hex, short_adr, device_type, device_state, - device_associated); - - if (device_associated > 0) { - uint idx = 16; - ResponseAppend_P(PSTR(",\"AssocDevicesList\":[")); - for (uint32_t i = 0; i < device_associated; i++) { - if (i > 0) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(idx)); - idx += 2; - } - ResponseAppend_P(PSTR("]")); - } - - ResponseJsonEnd(); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); - XdrvRulesProcess(); - - return res; -} - -int32_t Z_CheckNVWrite(int32_t res, class SBuffer &buf) { - - - - uint8_t status = buf.get8(2); - if ((0x00 == status) || (0x09 == status)) { - return 0; - } else { - return -2; - } -} - -const char Z_RebootReason[] PROGMEM = "Power-up|External|Watchdog"; - -int32_t Z_Reboot(int32_t res, class SBuffer &buf) { - - - - uint8_t reason = buf.get8(2); - uint8_t transport_rev = buf.get8(3); - uint8_t product_id = buf.get8(4); - uint8_t major_rel = buf.get8(5); - uint8_t minor_rel = buf.get8(6); - uint8_t hw_rev = buf.get8(7); - char reason_str[12]; - - if (reason > 3) { reason = 3; } - GetTextIndexed(reason_str, sizeof(reason_str), reason, Z_RebootReason); - - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"Message\":\"%s\",\"RestartReason\":\"%s\"" - ",\"MajorRel\":%d,\"MinorRel\":%d}}"), - ZIGBEE_STATUS_BOOT, "CC2530 booted", reason_str, - major_rel, minor_rel); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); - XdrvRulesProcess(); - - if ((0x02 == major_rel) && (0x06 == minor_rel)) { - return 0; - } else { - return ZIGBEE_LABEL_UNSUPPORTED_VERSION; - } -} - -int32_t Z_ReceiveCheckVersion(int32_t res, class SBuffer &buf) { -# 122 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_8_parsers.ino" - uint8_t major_rel = buf.get8(4); - uint8_t minor_rel = buf.get8(5); - uint8_t maint_rel = buf.get8(6); - uint32_t revision = buf.get32(7); - - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"MajorRel\":%d,\"MinorRel\":%d" - ",\"MaintRel\":%d,\"Revision\":%d}}"), - ZIGBEE_STATUS_CC_VERSION, major_rel, minor_rel, - maint_rel, revision); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); - XdrvRulesProcess(); - - if ((0x02 == major_rel) && (0x06 == minor_rel)) { - return 0; - } else { - return ZIGBEE_LABEL_UNSUPPORTED_VERSION; - } -} - -bool Z_ReceiveMatchPrefix(const class SBuffer &buf, const uint8_t *match) { - if ( (pgm_read_byte(&match[0]) == buf.get8(0)) && - (pgm_read_byte(&match[1]) == buf.get8(1)) ) { - return true; - } else { - return false; - } -} - -int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) { - - uint8_t duration = buf.get8(2); - uint8_t status_code; - const char* message; - - if (0xFF == duration) { - status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_XX; - message = PSTR("Enable Pairing mode until next boot"); - } else if (duration > 0) { - status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_60; - message = PSTR("Enable Pairing mode for %d seconds"); - } else { - status_code = ZIGBEE_STATUS_PERMITJOIN_CLOSE; - message = PSTR("Disable Pairing mode"); - } - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"Message\":\""), - status_code); - ResponseAppend_P(message, duration); - ResponseAppend_P(PSTR("\"}}")); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEE_STATE)); - XdrvRulesProcess(); - return -1; -} - - -void Z_SendActiveEpReq(uint16_t shortaddr) { - uint8_t ActiveEpReq[] = { Z_SREQ | Z_ZDO, ZDO_ACTIVE_EP_REQ, - Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; - - uint8_t NodeDescReq[] = { Z_SREQ | Z_ZDO, ZDO_NODE_DESC_REQ, - Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr) }; - - ZigbeeZNPSend(ActiveEpReq, sizeof(ActiveEpReq)); - -} - - -void Z_SendSimpleDescReq(uint16_t shortaddr, uint8_t endpoint) { - uint8_t SimpleDescReq[] = { Z_SREQ | Z_ZDO, ZDO_SIMPLE_DESC_REQ, - Z_B0(shortaddr), Z_B1(shortaddr), Z_B0(shortaddr), Z_B1(shortaddr), - endpoint }; - - ZigbeeZNPSend(SimpleDescReq, sizeof(SimpleDescReq)); -} - -const char* Z_DeviceType[] = { "Coordinator", "Router", "End Device", "Unknown" }; -int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) { - - Z_ShortAddress srcAddr = buf.get16(2); - uint8_t status = buf.get8(4); - Z_ShortAddress nwkAddr = buf.get16(5); - uint8_t logicalType = buf.get8(7); - uint8_t apsFlags = buf.get8(8); - uint8_t MACCapabilityFlags = buf.get8(9); - uint16_t manufacturerCapabilities = buf.get16(10); - uint8_t maxBufferSize = buf.get8(12); - uint16_t maxInTransferSize = buf.get16(13); - uint16_t serverMask = buf.get16(15); - uint16_t maxOutTransferSize = buf.get16(17); - uint8_t descriptorCapabilities = buf.get8(19); - - if (0 == status) { - zigbee_devices.updateLastSeen(nwkAddr); - - uint8_t deviceType = logicalType & 0x7; - if (deviceType > 3) { deviceType = 3; } - bool complexDescriptorAvailable = (logicalType & 0x08) ? 1 : 0; - - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"NodeType\":\"%s\",\"ComplexDesc\":%s}}"), - ZIGBEE_STATUS_NODE_DESC, Z_DeviceType[deviceType], - complexDescriptorAvailable ? "true" : "false" - ); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - } - - return -1; -} - -int32_t Z_ReceiveActiveEp(int32_t res, const class SBuffer &buf) { - - Z_ShortAddress srcAddr = buf.get16(2); - uint8_t status = buf.get8(4); - Z_ShortAddress nwkAddr = buf.get16(5); - uint8_t activeEpCount = buf.get8(7); - uint8_t* activeEpList = (uint8_t*) buf.charptr(8); - - - for (uint32_t i = 0; i < activeEpCount; i++) { - zigbee_devices.addEndoint(nwkAddr, activeEpList[i]); - } - - for (uint32_t i = 0; i < activeEpCount; i++) { - Z_SendSimpleDescReq(nwkAddr, activeEpList[i]); - } - - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"ActiveEndpoints\":["), - ZIGBEE_STATUS_ACTIVE_EP); - for (uint32_t i = 0; i < activeEpCount; i++) { - if (i > 0) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"0x%02X\""), activeEpList[i]); - } - ResponseAppend_P(PSTR("]}}")); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - return -1; -} - -void Z_SendAFInfoRequest(uint16_t shortaddr, uint8_t endpoint, uint16_t clusterid, uint8_t transacid) { - SBuffer buf(100); - buf.add8(Z_SREQ | Z_AF); - buf.add8(AF_DATA_REQUEST); - buf.add16(shortaddr); - buf.add8(endpoint); - buf.add8(0x01); - buf.add16(clusterid); - buf.add8(transacid); - buf.add8(0x30); - buf.add8(0x1E); - - buf.add8(3 + 2*sizeof(uint16_t)); - buf.add8(0x00); - buf.add8(transacid); - buf.add8(ZCL_READ_ATTRIBUTES); - buf.add16(0x0004); - buf.add16(0x0005); - - ZigbeeZNPSend(buf.getBuffer(), buf.len()); -} - - -int32_t Z_ReceiveSimpleDesc(int32_t res, const class SBuffer &buf) { - - Z_ShortAddress srcAddr = buf.get16(2); - uint8_t status = buf.get8(4); - Z_ShortAddress nwkAddr = buf.get16(5); - uint8_t lenDescriptor = buf.get8(7); - uint8_t endpoint = buf.get8(8); - uint16_t profileId = buf.get16(9); - uint16_t deviceId = buf.get16(11); - uint8_t deviceVersion = buf.get8(13); - uint8_t numInCluster = buf.get8(14); - uint8_t numOutCluster = buf.get8(15 + numInCluster*2); - - if (0 == status) { - zigbee_devices.addEndointProfile(nwkAddr, endpoint, profileId); - for (uint32_t i = 0; i < numInCluster; i++) { - zigbee_devices.addCluster(nwkAddr, endpoint, buf.get16(15 + i*2), false); - } - for (uint32_t i = 0; i < numOutCluster; i++) { - zigbee_devices.addCluster(nwkAddr, endpoint, buf.get16(16 + numInCluster*2 + i*2), true); - } - - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"Endpoint\":\"0x%02X\"" - ",\"ProfileId\":\"0x%04X\",\"DeviceId\":\"0x%04X\",\"DeviceVersion\":%d" - "\"InClusters\":["), - ZIGBEE_STATUS_SIMPLE_DESC, endpoint, - profileId, deviceId, deviceVersion); - for (uint32_t i = 0; i < numInCluster; i++) { - if (i > 0) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(15 + i*2)); - } - ResponseAppend_P(PSTR("],\"OutClusters\":[")); - for (uint32_t i = 0; i < numOutCluster; i++) { - if (i > 0) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(PSTR("\"0x%04X\""), buf.get16(16 + numInCluster*2 + i*2)); - } - ResponseAppend_P(PSTR("]}}")); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - - uint8_t cluster = zigbee_devices.findClusterEndpointIn(nwkAddr, 0x0000); - if (cluster) { - Z_SendAFInfoRequest(nwkAddr, cluster, 0x0000, 0x01); - } - } - return -1; -} - -int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) { - Z_ShortAddress srcAddr = buf.get16(2); - Z_ShortAddress nwkAddr = buf.get16(4); - Z_IEEEAddress ieeeAddr = buf.get64(6); - uint8_t capabilities = buf.get8(14); - - zigbee_devices.updateDevice(nwkAddr, ieeeAddr); - - char hex[20]; - Uint64toHex(ieeeAddr, hex, 64); - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" - ",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"), - ZIGBEE_STATUS_DEVICE_ANNOUNCE, hex, nwkAddr, - (capabilities & 0x04) ? "true" : "false", - (capabilities & 0x08) ? "true" : "false", - (capabilities & 0x40) ? "true" : "false" - ); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - Z_SendActiveEpReq(nwkAddr); - return -1; -} - - -int32_t Z_ReceiveTCDevInd(int32_t res, const class SBuffer &buf) { - Z_ShortAddress srcAddr = buf.get16(2); - Z_IEEEAddress ieeeAddr = buf.get64(4); - Z_ShortAddress parentNw = buf.get16(12); - - zigbee_devices.updateDevice(srcAddr, ieeeAddr); - - char hex[20]; - Uint64toHex(ieeeAddr, hex, 64); - Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" - "\"Status\":%d,\"IEEEAddr\":\"%s\",\"ShortAddr\":\"0x%04X\"" - ",\"ParentNetwork\":\"0x%04X\"}}"), - ZIGBEE_STATUS_DEVICE_INDICATION, hex, srcAddr, parentNw - ); - - MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED)); - XdrvRulesProcess(); - - return -1; -} - - - -const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000; - -void Z_AqaraOccupancy(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, const JsonObject *json) { - - const JsonVariant &val_endpoint = getCaseInsensitive(*json, PSTR(OCCUPANCY)); - if (nullptr != &val_endpoint) { - uint32_t occupancy = strToUInt(val_endpoint); - - if (occupancy) { - zigbee_devices.setTimer(shortaddr, OCCUPANCY_TIMEOUT, cluster, endpoint, 0, &Z_OccupancyCallback); - } - } -} - - - -int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t cluster, uint16_t endpoint, uint32_t value) { - const JsonObject *json = zigbee_devices.jsonGet(shortaddr); - if (json == nullptr) { return 0; } - - Z_AqaraOccupancy(shortaddr, cluster, endpoint, json); - - zigbee_devices.jsonPublishFlush(shortaddr); - return 1; -} - -int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) { - uint16_t groupid = buf.get16(2); - uint16_t clusterid = buf.get16(4); - Z_ShortAddress srcaddr = buf.get16(6); - uint8_t srcendpoint = buf.get8(8); - uint8_t dstendpoint = buf.get8(9); - uint8_t wasbroadcast = buf.get8(10); - uint8_t linkquality = buf.get8(11); - uint8_t securityuse = buf.get8(12); - uint32_t timestamp = buf.get32(13); - uint8_t seqnumber = buf.get8(17); - - bool defer_attributes = false; - - zigbee_devices.updateLastSeen(srcaddr); - ZCLFrame zcl_received = ZCLFrame::parseRawFrame(buf, 19, buf.get8(18), clusterid, groupid, - srcaddr, - srcendpoint, dstendpoint, wasbroadcast, - linkquality, securityuse, seqnumber, - timestamp); - zcl_received.log(); - char shortaddr[8]; - snprintf_P(shortaddr, sizeof(shortaddr), PSTR("0x%04X"), srcaddr); - - DynamicJsonBuffer jsonBuffer; - JsonObject& json = jsonBuffer.createObject(); - - if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) { - zcl_received.parseRawAttributes(json); - if (clusterid) { defer_attributes = true; } - } else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) { - zcl_received.parseReadAttributes(json); - } else if (zcl_received.isClusterSpecificCommand()) { - zcl_received.parseClusterSpecificCommand(json); - } - String msg(""); - msg.reserve(100); - json.printTo(msg); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":%s}"), srcaddr, msg.c_str()); - - zcl_received.postProcessAttributes(srcaddr, json); - - json[F(D_CMND_ZIGBEE_LINKQUALITY)] = linkquality; - - if (defer_attributes) { - - if (zigbee_devices.jsonIsConflict(srcaddr, json)) { - - zigbee_devices.jsonPublishFlush(srcaddr); - } else { - zigbee_devices.jsonAppend(srcaddr, json); - zigbee_devices.setTimer(srcaddr, USE_ZIGBEE_COALESCE_ATTR_TIMER, clusterid, srcendpoint, 0, &Z_PublishAttributes); - } - } else { - - zigbee_devices.jsonPublishNow(srcaddr, json); - } - return -1; -} - -typedef struct Z_Dispatcher { - const uint8_t* match; - ZB_RecvMsgFunc func; -} Z_Dispatcher; - - -ZBM(AREQ_AF_INCOMING_MESSAGE, Z_AREQ | Z_AF, AF_INCOMING_MSG) -ZBM(AREQ_END_DEVICE_ANNCE_IND, Z_AREQ | Z_ZDO, ZDO_END_DEVICE_ANNCE_IND) -ZBM(AREQ_END_DEVICE_TC_DEV_IND, Z_AREQ | Z_ZDO, ZDO_TC_DEV_IND) -ZBM(AREQ_PERMITJOIN_OPEN_XX, Z_AREQ | Z_ZDO, ZDO_PERMIT_JOIN_IND ) -ZBM(AREQ_ZDO_ACTIVEEPRSP, Z_AREQ | Z_ZDO, ZDO_ACTIVE_EP_RSP) -ZBM(AREQ_ZDO_SIMPLEDESCRSP, Z_AREQ | Z_ZDO, ZDO_SIMPLE_DESC_RSP) - -const Z_Dispatcher Z_DispatchTable[] PROGMEM = { - { AREQ_AF_INCOMING_MESSAGE, &Z_ReceiveAfIncomingMessage }, - { AREQ_END_DEVICE_ANNCE_IND, &Z_ReceiveEndDeviceAnnonce }, - { AREQ_END_DEVICE_TC_DEV_IND, &Z_ReceiveTCDevInd }, - { AREQ_PERMITJOIN_OPEN_XX, &Z_ReceivePermitJoinStatus }, - { AREQ_ZDO_NODEDESCRSP, &Z_ReceiveNodeDesc }, - { AREQ_ZDO_ACTIVEEPRSP, &Z_ReceiveActiveEp }, - { AREQ_ZDO_SIMPLEDESCRSP, &Z_ReceiveSimpleDesc }, -}; - -int32_t Z_Recv_Default(int32_t res, const class SBuffer &buf) { - - if (zigbee.init_phase) { - - return -1; - } else { - for (uint32_t i = 0; i < sizeof(Z_DispatchTable)/sizeof(Z_Dispatcher); i++) { - if (Z_ReceiveMatchPrefix(buf, Z_DispatchTable[i].match)) { - (*Z_DispatchTable[i].func)(res, buf); - } - } - return -1; - } -} - -int32_t Z_Load_Devices(uint8_t value) { - - loadZigbeeDevices(); - return 0; -} - -int32_t Z_State_Ready(uint8_t value) { - zigbee.init_phase = false; - return 0; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" -#ifdef USE_ZIGBEE - -#define XDRV_23 23 - -const uint32_t ZIGBEE_BUFFER_SIZE = 256; -const uint8_t ZIGBEE_SOF = 0xFE; -const uint8_t ZIGBEE_SOF_ALT = 0xFF; - -#include -TasmotaSerial *ZigbeeSerial = nullptr; - - -const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" - D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|" - D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" - D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|" - D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND ; - -const char kZigbeeCommands[] PROGMEM = D_PRFX_ZIGBEE "|" - D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEE_PERMITJOIN "|" - D_CMND_ZIGBEE_STATUS "|" D_CMND_ZIGBEE_RESET "|" D_CMND_ZIGBEE_SEND "|" - D_CMND_ZIGBEE_PROBE "|" D_CMND_ZIGBEE_READ "|" D_CMND_ZIGBEEZNPRECEIVE "|" - D_CMND_ZIGBEE_FORGET "|" D_CMND_ZIGBEE_SAVE "|" D_CMND_ZIGBEE_NAME "|" D_CMND_ZIGBEE_BIND ; - -void (* const ZigbeeCommand[])(void) PROGMEM = { - &CmndZbZNPSend, &CmndZbPermitJoin, - &CmndZbStatus, &CmndZbReset, &CmndZbSend, - &CmndZbProbe, &CmndZbRead, &CmndZbZNPReceive, - &CmndZbForget, &CmndZbSave, &CmndZbName, &CmndZbBind - }; - -int32_t ZigbeeProcessInput(class SBuffer &buf) { - if (!zigbee.state_machine) { return -1; } - - - bool recv_filter_match = true; - bool recv_prefix_match = false; - if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { - if (zigbee.recv_filter_len >= 2) { - recv_prefix_match = false; - if ( (pgm_read_byte(&zigbee.recv_filter[0]) == buf.get8(0)) && - (pgm_read_byte(&zigbee.recv_filter[1]) == buf.get8(1)) ) { - recv_prefix_match = true; - } - } - - for (uint32_t i = 0; i < zigbee.recv_filter_len; i++) { - if (pgm_read_byte(&zigbee.recv_filter[i]) != buf.get8(i)) { - recv_filter_match = false; - break; - } - } - - - } - - - int32_t res = -1; - - - - - - if ((zigbee.recv_filter) && (zigbee.recv_filter_len > 0)) { - if (!recv_prefix_match) { - res = -1; - } else { - if (recv_filter_match) { - res = 0; - } else { - if (zigbee.recv_until) { - res = -1; - } else { - res = -2; - } - } - } - } else { - res = -1; - } - - if (recv_prefix_match) { - if (zigbee.recv_func) { - res = (*zigbee.recv_func)(res, buf); - } - } - if (-1 == res) { - - if (zigbee.recv_unexpected) { - res = (*zigbee.recv_unexpected)(res, buf); - } - } - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "ZbProcessInput: res = %d"), res); - - - if (0 == res) { - - zigbee.state_waiting = false; - } else if (res > 0) { - ZigbeeGotoLabel(res); - } else if (-1 == res) { - - - } else { - - ZigbeeGotoLabel(zigbee.on_error_goto); - } -} - -void ZigbeeInput(void) -{ - static uint32_t zigbee_polling_window = 0; - static uint8_t fcs = ZIGBEE_SOF; - static uint32_t zigbee_frame_len = 5; -# 142 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" - while (ZigbeeSerial->available()) { - yield(); - uint8_t zigbee_in_byte = ZigbeeSerial->read(); - - - if (0 == zigbee_buffer->len()) { - zigbee_frame_len = 5; - fcs = ZIGBEE_SOF; - - - - if (ZIGBEE_SOF_ALT == zigbee_in_byte) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput forgiven first byte %02X (only for statistics)"), zigbee_in_byte); - zigbee_in_byte = ZIGBEE_SOF; - } - } - - if ((0 == zigbee_buffer->len()) && (ZIGBEE_SOF != zigbee_in_byte)) { - - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbInput discarding byte %02X"), zigbee_in_byte); - continue; - } - - if (zigbee_buffer->len() < zigbee_frame_len) { - zigbee_buffer->add8(zigbee_in_byte); - zigbee_polling_window = millis(); - fcs ^= zigbee_in_byte; - } - - if (zigbee_buffer->len() >= zigbee_frame_len) { - zigbee_polling_window = 0; - break; - } - - - if (02 == zigbee_buffer->len()) { - - uint8_t len_byte = zigbee_buffer->get8(1); - if (len_byte > 250) len_byte = 250; - - zigbee_frame_len = len_byte + 5; - } - } - - if (zigbee_buffer->len() && (millis() > (zigbee_polling_window + ZIGBEE_POLLING))) { - char hex_char[(zigbee_buffer->len() * 2) + 2]; - ToHex_P((unsigned char*)zigbee_buffer->getBuffer(), zigbee_buffer->len(), hex_char, sizeof(hex_char)); - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "Bytes follow_read_metric = %0d"), ZigbeeSerial->getLoopReadMetric()); - - if (zigbee_buffer->len() != zigbee_frame_len) { - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received frame of wrong size %s, len %d, expected %d"), hex_char, zigbee_buffer->len(), zigbee_frame_len); - } else if (0x00 != fcs) { - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_JSON_ZIGBEEZNPRECEIVED ": received bad FCS frame %s, %d"), hex_char, fcs); - } else { - - - - SBuffer znp_buffer = zigbee_buffer->subBuffer(2, zigbee_frame_len - 3); - - ToHex_P((unsigned char*)znp_buffer.getBuffer(), znp_buffer.len(), hex_char, sizeof(hex_char)); - Response_P(PSTR("{\"" D_JSON_ZIGBEEZNPRECEIVED "\":\"%s\"}"), hex_char); - if (Settings.flag3.tuya_serial_mqtt_publish) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR)); - XdrvRulesProcess(); - } else { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE "%s"), mqtt_data); - } - - ZigbeeProcessInput(znp_buffer); - } - zigbee_buffer->setLen(0); - } -} - - - -void ZigbeeInit(void) -{ - zigbee.active = false; - if ((pin[GPIO_ZIGBEE_RX] < 99) && (pin[GPIO_ZIGBEE_TX] < 99)) { - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_ZIGBEE "GPIOs Rx:%d Tx:%d"), pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX]); - - ZigbeeSerial = new TasmotaSerial(pin[GPIO_ZIGBEE_RX], pin[GPIO_ZIGBEE_TX], seriallog_level ? 1 : 2, 0, 256); - ZigbeeSerial->begin(115200); - if (ZigbeeSerial->hardwareSerial()) { - ClaimSerial(); - uint32_t aligned_buffer = ((uint32_t)serial_in_buffer + 3) & ~3; - zigbee_buffer = new PreAllocatedSBuffer(sizeof(serial_in_buffer) - 3, (char*) aligned_buffer); - } else { - zigbee_buffer = new SBuffer(ZIGBEE_BUFFER_SIZE); - } - zigbee.active = true; - zigbee.init_phase = true; - zigbee.state_machine = true; - ZigbeeSerial->flush(); - } -} - - - - - -uint32_t strToUInt(const JsonVariant &val) { - - if (val.is()) { - return val.as(); - } else { - if (val.is()) { - String sval = val.as(); - return strtoull(sval.c_str(), nullptr, 0); - } - } - return 0; -} - -const unsigned char ZIGBEE_FACTORY_RESET[] PROGMEM = - { Z_SREQ | Z_SAPI, SAPI_WRITE_CONFIGURATION, CONF_STARTUP_OPTION, 0x01 , 0x01 }; - - -void CmndZbReset(void) { - if (ZigbeeSerial) { - switch (XdrvMailbox.payload) { - case 1: - ZigbeeZNPSend(ZIGBEE_FACTORY_RESET, sizeof(ZIGBEE_FACTORY_RESET)); - eraseZigbeeDevices(); - restart_flag = 2; - ResponseCmndChar(D_JSON_ZIGBEE_CC2530 " " D_JSON_RESET_AND_RESTARTING); - break; - default: - ResponseCmndChar(D_JSON_ONE_TO_RESET); - } - } -} - -void CmndZbZNPSendOrReceive(bool send) -{ - if (ZigbeeSerial && (XdrvMailbox.data_len > 0)) { - uint8_t code; - - char *codes = RemoveSpace(XdrvMailbox.data); - int32_t size = strlen(XdrvMailbox.data); - - SBuffer buf((size+1)/2); - - while (size > 1) { - char stemp[3]; - strlcpy(stemp, codes, sizeof(stemp)); - code = strtol(stemp, nullptr, 16); - buf.add8(code); - size -= 2; - codes += 2; - } - if (send) { - ZigbeeZNPSend(buf.getBuffer(), buf.len()); - } else { - ZigbeeProcessInput(buf); - } - } - ResponseCmndDone(); -} - - -void CmndZbZNPReceive(void) -{ - CmndZbZNPSendOrReceive(false); -} - -void CmndZbZNPSend(void) -{ - CmndZbZNPSendOrReceive(true); -} - -void ZigbeeZNPSend(const uint8_t *msg, size_t len) { - if ((len < 2) || (len > 252)) { - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_JSON_ZIGBEEZNPSENT ": bad message len %d"), len); - return; - } - uint8_t data_len = len - 2; - - if (ZigbeeSerial) { - uint8_t fcs = data_len; - - ZigbeeSerial->write(ZIGBEE_SOF); - - ZigbeeSerial->write(data_len); - - for (uint32_t i = 0; i < len; i++) { - uint8_t b = pgm_read_byte(msg + i); - ZigbeeSerial->write(b); - fcs ^= b; - - } - ZigbeeSerial->write(fcs); - - } - - char hex_char[(len * 2) + 2]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZNPSENT " %s"), - ToHex_P(msg, len, hex_char, sizeof(hex_char))); -} - -void ZigbeeZCLSend(uint16_t dtsAddr, uint16_t clusterId, uint8_t endpoint, uint8_t cmdId, bool clusterSpecific, const uint8_t *msg, size_t len, bool disableDefResp, uint8_t transacId) { - SBuffer buf(25+len); - buf.add8(Z_SREQ | Z_AF); - buf.add8(AF_DATA_REQUEST); - buf.add16(dtsAddr); - buf.add8(endpoint); - buf.add8(0x01); - buf.add16(clusterId); - buf.add8(transacId); - buf.add8(0x30); - buf.add8(0x1E); - - buf.add8(3 + len); - buf.add8((disableDefResp ? 0x10 : 0x00) | (clusterSpecific ? 0x01 : 0x00)); - buf.add8(transacId); - buf.add8(cmdId); - if (len > 0) { - buf.addBuffer(msg, len); - } - - ZigbeeZNPSend(buf.getBuffer(), buf.len()); -} - -inline int8_t hexValue(char c) { - if ((c >= '0') && (c <= '9')) { - return c - '0'; - } - if ((c >= 'A') && (c <= 'F')) { - return 10 + c - 'A'; - } - if ((c >= 'a') && (c <= 'f')) { - return 10 + c - 'a'; - } - return -1; -} - -uint32_t parseHex(const char **data, size_t max_len = 8) { - uint32_t ret = 0; - for (uint32_t i = 0; i < max_len; i++) { - int8_t v = hexValue(**data); - if (v < 0) { break; } - ret = (ret << 4) | v; - *data += 1; - } - return ret; -} - -void zigbeeZCLSendStr(uint16_t dstAddr, uint8_t endpoint, const char *data) { - - uint16_t cluster = 0x0000; - uint8_t cmd = ZCL_READ_ATTRIBUTES; - bool clusterSpecific = false; - - - - cluster = parseHex(&data, 4); - - - if (('_' == *data) || ('!' == *data)) { - if ('!' == *data) { clusterSpecific = true; } - data++; - } else { - ResponseCmndChar("Wrong delimiter for payload"); - return; - } - - cmd = parseHex(&data, 2); - - - - if ('/' == *data) { data++; } - - size_t size = strlen(data); - SBuffer buf((size+2)/2); - - while (*data) { - uint8_t code = parseHex(&data, 2); - buf.add8(code); - } - - if (0 == endpoint) { - - endpoint = zigbee_devices.findClusterEndpointIn(dstAddr, cluster); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint); - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: dstAddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"), - dstAddr, cluster, endpoint, cmd, data); - - if (0 == endpoint) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint")); - return; - } - - - ZigbeeZCLSend(dstAddr, cluster, endpoint, cmd, clusterSpecific, buf.getBuffer(), buf.len()); - - if (clusterSpecific) { - zigbeeSetCommandTimer(dstAddr, cluster, endpoint); - } - ResponseCmndDone(); -} - -void CmndZbSend(void) { -# 461 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_23_zigbee_9_impl.ino" - if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } - DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data); - if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; } - - - static char delim[] = ", "; - uint16_t device = 0xFFFF; - uint8_t endpoint = 0x00; - String cmd_str = ""; - - const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); - if (nullptr != &val_device) { - device = zigbee_devices.parseDeviceParam(val_device.as()); - if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; } - } - if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; } - - const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); - if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } - const JsonVariant &val_cmd = getCaseInsensitive(json, PSTR("Send")); - if (nullptr != &val_cmd) { - - - - if (val_cmd.is()) { - - JsonObject &cmd_obj = val_cmd.as(); - int32_t cmd_size = cmd_obj.size(); - if (cmd_size > 1) { - Response_P(PSTR("Only 1 command allowed (%d)"), cmd_size); - return; - } else if (1 == cmd_size) { - - JsonObject::iterator it = cmd_obj.begin(); - String key = it->key; - JsonVariant& value = it->value; - uint32_t x = 0, y = 0, z = 0; - - const __FlashStringHelper* tasmota_cmd = zigbeeFindCommand(key.c_str()); - if (tasmota_cmd) { - cmd_str = tasmota_cmd; - } else { - Response_P(PSTR("Unrecognized zigbee command: %s"), key.c_str()); - return; - } - - - if (value.is()) { - x = value.as() ? 1 : 0; - } else if (value.is()) { - x = value.as(); - } else { - - const char *s_const = value.as(); - if (s_const != nullptr) { - char s[strlen(s_const)+1]; - strcpy(s, s_const); - if ((nullptr != s) && (0x00 != *s)) { - char *sval = strtok(s, delim); - if (sval) { - x = ZigbeeAliasOrNumber(sval); - sval = strtok(nullptr, delim); - if (sval) { - y = ZigbeeAliasOrNumber(sval); - sval = strtok(nullptr, delim); - if (sval) { - z = ZigbeeAliasOrNumber(sval); - } - } - } - } - } - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_template = %s"), cmd_str.c_str()); - cmd_str = zigbeeCmdAddParams(cmd_str.c_str(), x, y, z); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: command_final = %s"), cmd_str.c_str()); - } else { - - } - } else if (val_cmd.is()) { - - cmd_str = val_cmd.as(); - } else { - - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbCmd_actual: ZigbeeZCLSend {\"device\":\"0x%04X\",\"endpoint\":%d,\"send\":\"%s\"}"), - device, endpoint, cmd_str.c_str()); - zigbeeZCLSendStr(device, endpoint, cmd_str.c_str()); - } else { - Response_P(PSTR("Missing zigbee 'Send'")); - return; - } - -} - -ZBM(ZBS_BIND_REQ, Z_SREQ | Z_ZDO, ZDO_BIND_REQ, - 0,0, - 0,0,0,0,0,0,0,0, - 0x00, - 0x00, 0x00, - 0x03, - 0,0,0,0,0,0,0,0, - 0x01 -) - -void CmndZbBind(void) { - - - - if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } - DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data); - if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; } - - - - uint16_t device = 0xFFFF; - uint8_t endpoint = 0x00; - uint16_t cluster = 0; - uint32_t group = 0xFFFFFFFF; - - const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); - if (nullptr != &val_device) { - device = zigbee_devices.parseDeviceParam(val_device.as()); - if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; } - } - if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; } - - const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); - if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } - const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster")); - if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); } - - - - SBuffer buf(sizeof(ZBS_BIND_REQ)); - buf.add8(Z_SREQ | Z_ZDO); - buf.add8(ZDO_BIND_REQ); - buf.add16(device); - buf.add64(zigbee_devices.getDeviceLongAddr(device)); - buf.add8(endpoint); - buf.add16(cluster); - buf.add8(0x03); - buf.add64(localIEEEAddr); - buf.add8(0x01); - - ZigbeeZNPSend(buf.getBuffer(), buf.len()); - - ResponseCmndDone(); -} - - -void CmndZbProbe(void) { - if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); - if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; } - if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; } - - - Z_SendActiveEpReq(shortaddr); - ResponseCmndDone(); -} - - -void CmndZbName(void) { - - - - - - - - if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } - - - char *p; - char *str = strtok_r(XdrvMailbox.data, ", ", &p); - - - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); - if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; } - if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; } - - if (p == nullptr) { - const String * friendlyName = zigbee_devices.getFriendlyName(shortaddr); - Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, friendlyName ? friendlyName->c_str() : ""); - } else { - zigbee_devices.setFriendlyName(shortaddr, p); - Response_P(PSTR("{\"0x%04X\":{\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), shortaddr, p); - } -} - - -void CmndZbForget(void) { - if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); - if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; } - if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; } - - - if (zigbee_devices.removeDevice(shortaddr)) { - ResponseCmndDone(); - } else { - ResponseCmndChar("Unknown device"); - } -} - - -void CmndZbSave(void) { - if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } - - saveZigbeeDevices(); - - ResponseCmndDone(); -} - - -void CmndZbRead(void) { - - - - if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } - DynamicJsonBuffer jsonBuf; - JsonObject &json = jsonBuf.parseObject(XdrvMailbox.data); - if (!json.success()) { ResponseCmndChar(D_JSON_INVALID_JSON); return; } - - - uint16_t device = 0xFFFF; - uint16_t cluster = 0x0000; - uint8_t endpoint = 0x00; - size_t attrs_len = 0; - uint8_t* attrs = nullptr; - - const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device")); - if (nullptr != &val_device) { - device = zigbee_devices.parseDeviceParam(val_device.as()); - if (0xFFFF == device) { ResponseCmndChar("Invalid parameter"); return; } - } - if ((nullptr == &val_device) || (0x000 == device)) { ResponseCmndChar("Unknown device"); return; } - - const JsonVariant &val_cluster = getCaseInsensitive(json, PSTR("Cluster")); - if (nullptr != &val_cluster) { cluster = strToUInt(val_cluster); } - const JsonVariant &val_endpoint = getCaseInsensitive(json, PSTR("Endpoint")); - if (nullptr != &val_endpoint) { endpoint = strToUInt(val_endpoint); } - - const JsonVariant &val_attr = getCaseInsensitive(json, PSTR("Read")); - if (nullptr != &val_attr) { - uint16_t val = strToUInt(val_attr); - if (val_attr.is()) { - JsonArray& attr_arr = val_attr; - attrs_len = attr_arr.size() * 2; - attrs = new uint8_t[attrs_len]; - - uint32_t i = 0; - for (auto value : attr_arr) { - uint16_t val = strToUInt(value); - attrs[i++] = val & 0xFF; - attrs[i++] = val >> 8; - } - } else { - attrs_len = 2; - attrs = new uint8_t[attrs_len]; - attrs[0] = val & 0xFF; - attrs[1] = val >> 8; - } - } - - if ((0 != endpoint) && (attrs_len > 0)) { - ZigbeeZCLSend(device, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, attrs, attrs_len, false ); - ResponseCmndDone(); - } else { - ResponseCmndChar("Missing parameters"); - } - - if (attrs) { delete[] attrs; } -} - - -void CmndZbPermitJoin(void) -{ - if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } - uint32_t payload = XdrvMailbox.payload; - if (payload < 0) { payload = 0; } - if ((99 != payload) && (payload > 1)) { payload = 1; } - - if (1 == payload) { - ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_60); - } else if (99 == payload){ - ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_OPEN_XX); - } else { - ZigbeeGotoLabel(ZIGBEE_LABEL_PERMIT_JOIN_CLOSE); - } - ResponseCmndDone(); -} - -void CmndZbStatus(void) { - if (ZigbeeSerial) { - if (zigbee.init_phase) { ResponseCmndChar(D_ZIGBEE_NOT_STARTED); return; } - uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data); - if (0xFFFF == shortaddr) { ResponseCmndChar("Invalid parameter"); return; } - if (XdrvMailbox.payload > 0) { - if (0x0000 == shortaddr) { ResponseCmndChar("Unknown device"); return; } - } - - String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr); - Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str()); - } -} - - - - - -bool Xdrv23(uint8_t function) -{ - bool result = false; - - if (zigbee.active) { - switch (function) { - case FUNC_EVERY_50_MSECOND: - if (!zigbee.init_phase) { - zigbee_devices.runTimer(); - } - break; - case FUNC_LOOP: - if (ZigbeeSerial) { ZigbeeInput(); } - if (zigbee.state_machine) { - - ZigbeeStateMachine_Run(); - } - break; - case FUNC_PRE_INIT: - ZigbeeInit(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kZbCommands, ZigbeeCommand); - result = result || DecodeCommand(kZigbeeCommands, ZigbeeCommand); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_24_buzzer.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_24_buzzer.ino" -#ifdef USE_BUZZER - - - - -#define XDRV_24 24 - -struct BUZZER { - uint32_t tune = 0; - uint32_t tune_reload = 0; - bool active = true; - bool enable = false; - uint8_t inverted = 0; - uint8_t count = 0; - uint8_t mode = 0; - uint8_t set[2]; - uint8_t duration; - uint8_t state = 0; -} Buzzer; - - - -void BuzzerOff(void) -{ - DigitalWrite(GPIO_BUZZER, Buzzer.inverted); -} - - -void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32_t mode) -{ - Buzzer.set[0] = off; - Buzzer.set[1] = on; - Buzzer.duration = 1; - Buzzer.tune_reload = 0; - Buzzer.mode = mode; - - if (tune) { - uint32_t tune1 = tune; - uint32_t tune2 = tune; - for (uint32_t i = 0; i < 32; i++) { - if (!(tune2 & 0x80000000)) { - tune2 <<= 1; - } else { - Buzzer.tune_reload <<= 1; - Buzzer.tune_reload |= tune1 & 1; - tune1 >>= 1; - } - } - Buzzer.tune = Buzzer.tune_reload; - } - Buzzer.count = count * 2; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("BUZ: %d(%d),%d,%d,0x%08X(0x%08X)"), count, Buzzer.count, on, off, tune, Buzzer.tune); - - Buzzer.enable = (Buzzer.count > 0); - if (!Buzzer.enable) { - BuzzerOff(); - } -} - -void BuzzerSetStateToLed(uint32_t state) -{ - if (Buzzer.enable && (2 == Buzzer.mode)) { - Buzzer.state = (state != 0); - DigitalWrite(GPIO_BUZZER, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); - } -} - -void BuzzerBeep(uint32_t count) -{ - BuzzerBeep(count, 1, 1, 0, 0); -} - -void BuzzerEnabledBeep(uint32_t count, uint32_t duration) -{ - if (Settings.flag3.buzzer_enable) { - BuzzerBeep(count, duration, 1, 0, 0); - } -} - - - -bool BuzzerPinState(void) -{ - if (XdrvMailbox.index == GPIO_BUZZER_INV) { - Buzzer.inverted = 1; - XdrvMailbox.index -= (GPIO_BUZZER_INV - GPIO_BUZZER); - return true; - } - return false; -} - -void BuzzerInit(void) -{ - if (pin[GPIO_BUZZER] < 99) { - pinMode(pin[GPIO_BUZZER], OUTPUT); - BuzzerOff(); - } else { - Buzzer.active = false; - } -} - -void BuzzerEvery100mSec(void) -{ - if (Buzzer.enable && (Buzzer.mode != 2)) { - if (Buzzer.count) { - if (Buzzer.duration) { - Buzzer.duration--; - if (!Buzzer.duration) { - if (Buzzer.tune) { - Buzzer.state = Buzzer.tune & 1; - Buzzer.tune >>= 1; - } else { - Buzzer.tune = Buzzer.tune_reload; - Buzzer.count -= (Buzzer.tune_reload) ? 2 : 1; - Buzzer.state = Buzzer.count & 1; - if (Buzzer.mode) { - Buzzer.count |= 2; - } - } - Buzzer.duration = Buzzer.set[Buzzer.state]; - } - } - DigitalWrite(GPIO_BUZZER, (Buzzer.inverted) ? !Buzzer.state : Buzzer.state); - } else { - Buzzer.enable = false; - } - } -} - - - - - -const char kBuzzerCommands[] PROGMEM = "|" - "Buzzer" ; - -void (* const BuzzerCommand[])(void) PROGMEM = { - &CmndBuzzer }; - -void CmndBuzzer(void) -{ -# 174 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_24_buzzer.ino" - if (XdrvMailbox.data_len > 0) { - if (XdrvMailbox.payload != 0) { - uint32_t parm[4] = { 0 }; - uint32_t mode = 0; - ParseParameters(4, parm); - if (XdrvMailbox.payload <= 0) { - parm[0] = 1; - mode = -XdrvMailbox.payload; - } - for (uint32_t i = 1; i < 3; i++) { - if (parm[i] < 1) { parm[i] = 1; } - } - BuzzerBeep(parm[0], parm[1], parm[2], parm[3], mode); - } else { - BuzzerBeep(0); - } - } else { - BuzzerBeep(1); - } - ResponseCmndDone(); -} - - - - - -bool Xdrv24(uint8_t function) -{ - bool result = false; - - if (Buzzer.active) { - switch (function) { - case FUNC_EVERY_100_MSECOND: - BuzzerEvery100mSec(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kBuzzerCommands, BuzzerCommand); - break; - case FUNC_PRE_INIT: - BuzzerInit(); - break; - case FUNC_PIN_STATE: - result = BuzzerPinState(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_25_A4988_Stepper.ino" -# 21 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_25_A4988_Stepper.ino" -#ifdef USE_A4988_STEPPER - - - - -#define XDRV_25 25 - -#include - -short A4988_dir_pin = pin[GPIO_MAX]; -short A4988_stp_pin = pin[GPIO_MAX]; -short A4988_ms1_pin = pin[GPIO_MAX]; -short A4988_ms2_pin = pin[GPIO_MAX]; -short A4988_ms3_pin = pin[GPIO_MAX]; -short A4988_ena_pin = pin[GPIO_MAX]; -int A4988_spr = 0; -float A4988_rpm = 0; -short A4988_mis = 0; - -A4988_Stepper* myA4988 = nullptr; - -void A4988Init(void) -{ - A4988_dir_pin = pin[GPIO_A4988_DIR]; - A4988_stp_pin = pin[GPIO_A4988_STP]; - A4988_ena_pin = pin[GPIO_A4988_ENA]; - A4988_ms1_pin = pin[GPIO_A4988_MS1]; - A4988_ms2_pin = pin[GPIO_A4988_MS2]; - A4988_ms3_pin = pin[GPIO_A4988_MS3]; - A4988_spr = 200; - A4988_rpm = 30; - A4988_mis = 1; - - myA4988 = new A4988_Stepper( A4988_spr - , A4988_rpm - , A4988_mis - , A4988_dir_pin - , A4988_stp_pin - , A4988_ena_pin - , A4988_ms1_pin - , A4988_ms2_pin - , A4988_ms3_pin ); -} - -const char kA4988Commands[] PROGMEM = "Motor|" - "Move|Rotate|Turn|MIS|SPR|RPM"; - -void (* const A4988Command[])(void) PROGMEM = { - &CmndDoMove,&CmndDoRotate,&CmndDoTurn,&CmndSetMIS,&CmndSetSPR,&CmndSetRPM}; - -void CmndDoMove(void) { - if (XdrvMailbox.data_len > 0) { - long stepsPlease = strtoul(XdrvMailbox.data,nullptr,10); - myA4988->doMove(stepsPlease); - ResponseCmndDone(); - } -} - -void CmndDoRotate(void) { - if (XdrvMailbox.data_len > 0) { - long degrsPlease = strtoul(XdrvMailbox.data,nullptr,10); - myA4988->doRotate(degrsPlease); - ResponseCmndDone(); - } -} - -void CmndDoTurn(void) { - if (XdrvMailbox.data_len > 0) { - float turnsPlease = strtod(XdrvMailbox.data,nullptr); - myA4988->doTurn(turnsPlease); - ResponseCmndDone(); - } -} - -void CmndSetMIS(void) { - if ((pin[GPIO_A4988_MS1] < 99) && (pin[GPIO_A4988_MS2] < 99) && (pin[GPIO_A4988_MS3] < 99) && (XdrvMailbox.data_len > 0)) { - short newMIS = strtoul(XdrvMailbox.data,nullptr,10); - myA4988->setMIS(newMIS); - ResponseCmndDone(); - } -} - -void CmndSetSPR(void) { - if (XdrvMailbox.data_len > 0) { - int newSPR = strtoul(XdrvMailbox.data,nullptr,10); - myA4988->setSPR(newSPR); - ResponseCmndDone(); - } -} - -void CmndSetRPM(void) { - if (XdrvMailbox.data_len > 0) { - short newRPM = strtoul(XdrvMailbox.data,nullptr,10); - myA4988->setRPM(newRPM); - ResponseCmndDone(); - } -} - - - - -bool Xdrv25(uint8_t function) -{ - bool result = false; - if ((pin[GPIO_A4988_DIR] < 99) && (pin[GPIO_A4988_STP] < 99)) { - switch (function) { - case FUNC_INIT: - A4988Init(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kA4988Commands, A4988Command); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_26_ariluxrf.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_26_ariluxrf.ino" -#ifdef USE_LIGHT -#ifdef USE_ARILUX_RF - - - - -#define XDRV_26 26 - -const uint32_t ARILUX_RF_TIME_AVOID_DUPLICATE = 1000; - -const uint8_t ARILUX_RF_MAX_CHANGES = 51; -const uint32_t ARILUX_RF_SEPARATION_LIMIT = 4300; -const uint32_t ARILUX_RF_RECEIVE_TOLERANCE = 60; - -struct ARILUX { - unsigned int rf_timings[ARILUX_RF_MAX_CHANGES]; - - unsigned long rf_received_value = 0; - unsigned long rf_last_received_value = 0; - unsigned long rf_last_time = 0; - unsigned long rf_lasttime = 0; - - unsigned int rf_change_count = 0; - unsigned int rf_repeat_count = 0; - - uint8_t rf_toggle = 0; -} Arilux; - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 -#ifndef USE_WS2812_DMA -void AriluxRfInterrupt(void) ICACHE_RAM_ATTR; -#endif -#endif - -void AriluxRfInterrupt(void) -{ - unsigned long time = micros(); - unsigned int duration = time - Arilux.rf_lasttime; - - if (duration > ARILUX_RF_SEPARATION_LIMIT) { - if (abs(duration - Arilux.rf_timings[0]) < 200) { - Arilux.rf_repeat_count++; - if (Arilux.rf_repeat_count == 2) { - unsigned long code = 0; - const unsigned int delay = Arilux.rf_timings[0] / 31; - const unsigned int delayTolerance = delay * ARILUX_RF_RECEIVE_TOLERANCE / 100; - for (unsigned int i = 1; i < Arilux.rf_change_count -1; i += 2) { - code <<= 1; - if (abs(Arilux.rf_timings[i] - (delay *3)) < delayTolerance && abs(Arilux.rf_timings[i +1] - delay) < delayTolerance) { - code |= 1; - } - } - if (Arilux.rf_change_count > 49) { - Arilux.rf_received_value = code; - } - Arilux.rf_repeat_count = 0; - } - } - Arilux.rf_change_count = 0; - } - if (Arilux.rf_change_count >= ARILUX_RF_MAX_CHANGES) { - Arilux.rf_change_count = 0; - Arilux.rf_repeat_count = 0; - } - Arilux.rf_timings[Arilux.rf_change_count++] = duration; - Arilux.rf_lasttime = time; -} - -void AriluxRfHandler(void) -{ - unsigned long now = millis(); - if (Arilux.rf_received_value && !((Arilux.rf_received_value == Arilux.rf_last_received_value) && (now - Arilux.rf_last_time < ARILUX_RF_TIME_AVOID_DUPLICATE))) { - Arilux.rf_last_received_value = Arilux.rf_received_value; - Arilux.rf_last_time = now; - - uint16_t hostcode = Arilux.rf_received_value >> 8 & 0xFFFF; - if (Settings.rf_code[1][6] == Settings.rf_code[1][7]) { - Settings.rf_code[1][6] = hostcode >> 8 & 0xFF; - Settings.rf_code[1][7] = hostcode & 0xFF; - } - uint16_t stored_hostcode = Settings.rf_code[1][6] << 8 | Settings.rf_code[1][7]; - - DEBUG_DRIVER_LOG(PSTR(D_LOG_RFR D_HOST D_CODE " 0x%04X, " D_RECEIVED " 0x%06X"), stored_hostcode, Arilux.rf_received_value); - - if (hostcode == stored_hostcode) { - char command[33]; - char value = '-'; - command[0] = '\0'; - uint8_t keycode = Arilux.rf_received_value & 0xFF; - switch (keycode) { - case 1: - case 3: - snprintf_P(command, sizeof(command), PSTR(D_CMND_POWER " %d"), (1 == keycode) ? 1 : 0); - break; - case 2: - Arilux.rf_toggle++; - Arilux.rf_toggle &= 0x3; - snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), 200 + Arilux.rf_toggle); - break; - case 4: - value = '+'; - case 7: - snprintf_P(command, sizeof(command), PSTR(D_CMND_SPEED " %c"), value); - break; - case 5: - value = '+'; - case 8: - snprintf_P(command, sizeof(command), PSTR(D_CMND_SCHEME " %c"), value); - break; - case 6: - value = '+'; - case 9: - snprintf_P(command, sizeof(command), PSTR(D_CMND_DIMMER " %c"), value); - break; - default: { - if ((keycode >= 10) && (keycode <= 21)) { - snprintf_P(command, sizeof(command), PSTR(D_CMND_COLOR " %d"), keycode -9); - } - } - } - if (strlen(command)) { - ExecuteCommand(command, SRC_LIGHT); - } - } - } - Arilux.rf_received_value = 0; -} - -void AriluxRfInit(void) -{ - if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { - if (Settings.last_module != Settings.module) { - Settings.rf_code[1][6] = 0; - Settings.rf_code[1][7] = 0; - Settings.last_module = Settings.module; - } - Arilux.rf_received_value = 0; - - digitalWrite(pin[GPIO_ARIRFSEL], 0); - attachInterrupt(pin[GPIO_ARIRFRCV], AriluxRfInterrupt, CHANGE); - } -} - -void AriluxRfDisable(void) -{ - if ((pin[GPIO_ARIRFRCV] < 99) && (pin[GPIO_ARIRFSEL] < 99)) { - detachInterrupt(pin[GPIO_ARIRFRCV]); - digitalWrite(pin[GPIO_ARIRFSEL], 1); - } -} - - - - - -bool Xdrv26(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_50_MSECOND: - if (pin[GPIO_ARIRFRCV] < 99) { AriluxRfHandler(); } - break; - case FUNC_EVERY_SECOND: - if (10 == uptime) { AriluxRfInit(); } - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_27_shutter.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_27_shutter.ino" -#ifdef USE_SHUTTER - - - - -#define XDRV_27 27 - -#define D_SHUTTER "SHUTTER" - -const uint16_t MOTOR_STOP_TIME = 500; -const uint8_t steps_per_second = 20; - -uint8_t calibrate_pos[6] = {0,30,50,70,90,100}; -uint16_t messwerte[5] = {30,50,70,90,100}; -uint16_t last_execute_step; - -enum ShutterModes { SHT_OFF_OPEN__OFF_CLOSE, SHT_OFF_ON__OPEN_CLOSE, SHT_PULSE_OPEN__PULSE_CLOSE, SHT_OFF_ON__OPEN_CLOSE_STEPPER,}; -enum ShutterButtonStates { SHT_NOT_PRESSED, SHT_PRESSED_MULTI, SHT_PRESSED_HOLD, SHT_PRESSED_IMMEDIATE, SHT_PRESSED_MULTI_SIMULTANEOUS, SHT_PRESSED_HOLD_SIMULTANEOUS, SHT_PRESSED_EXT_HOLD_SIMULTANEOUS,}; - -const char kShutterCommands[] PROGMEM = D_PRFX_SHUTTER "|" - D_CMND_SHUTTER_OPEN "|" D_CMND_SHUTTER_CLOSE "|" D_CMND_SHUTTER_STOP "|" D_CMND_SHUTTER_POSITION "|" - D_CMND_SHUTTER_OPENTIME "|" D_CMND_SHUTTER_CLOSETIME "|" D_CMND_SHUTTER_RELAY "|" - D_CMND_SHUTTER_SETHALFWAY "|" D_CMND_SHUTTER_SETCLOSE "|" D_CMND_SHUTTER_INVERT "|" D_CMND_SHUTTER_CLIBRATION "|" - D_CMND_SHUTTER_MOTORDELAY "|" D_CMND_SHUTTER_FREQUENCY "|" D_CMND_SHUTTER_BUTTON "|" D_CMND_SHUTTER_LOCK "|" D_CMND_SHUTTER_ENABLEENDSTOPTIME; - -void (* const ShutterCommand[])(void) PROGMEM = { - &CmndShutterOpen, &CmndShutterClose, &CmndShutterStop, &CmndShutterPosition, - &CmndShutterOpenTime, &CmndShutterCloseTime, &CmndShutterRelay, - &CmndShutterSetHalfway, &CmndShutterSetClose, &CmndShutterInvert, &CmndShutterCalibration , &CmndShutterMotorDelay, - &CmndShutterFrequency, &CmndShutterButton, &CmndShutterLock, &CmndShutterEnableEndStopTime}; - - const char JSON_SHUTTER_POS[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Position\":%d,\"Direction\":%d}"; - const char JSON_SHUTTER_BUTTON[] PROGMEM = "\"" D_PRFX_SHUTTER "%d\":{\"Button%d\":%d}"; - -#include - -Ticker TickerShutter; - -struct SHUTTER { - power_t mask = 0; - power_t old_power = 0; - power_t switched_relay = 0; - uint32_t time[MAX_SHUTTERS]; - int32_t open_max[MAX_SHUTTERS]; - int32_t target_position[MAX_SHUTTERS]; - int32_t start_position[MAX_SHUTTERS]; - int32_t real_position[MAX_SHUTTERS]; - uint16_t open_time[MAX_SHUTTERS]; - uint16_t close_time[MAX_SHUTTERS]; - uint16_t close_velocity[MAX_SHUTTERS]; - int8_t direction[MAX_SHUTTERS]; - uint8_t mode = 0; - int16_t motordelay[MAX_SHUTTERS]; - int16_t pwm_frequency; - uint16_t max_pwm_frequency = 1000; - uint16_t max_close_pwm_frequency[MAX_SHUTTERS]; - uint8_t skip_relay_change; - int32_t accelerator[MAX_SHUTTERS]; -} Shutter; - -void ShutterLogPos(uint32_t i) -{ - char stemp2[10]; - dtostrfd((float)Shutter.time[i] / steps_per_second, 2, stemp2); - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter%d Real %d, Start %d, Stop %d, Dir %d, Delay %d, Rtc %s [s], Freq %d"), - i+1, Shutter.real_position[i], Shutter.start_position[i], Shutter.target_position[i], Shutter.direction[i], Shutter.motordelay[i], stemp2, Shutter.pwm_frequency); -} - -void ShutterRtc50mS(void) -{ - for (uint32_t i = 0; i < shutters_present; i++) { - Shutter.time[i]++; - if (Shutter.accelerator[i]) { - Shutter.pwm_frequency += Shutter.accelerator[i]; - Shutter.pwm_frequency = tmax(0,tmin(Shutter.direction[i]==1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i],Shutter.pwm_frequency)); - analogWriteFreq(Shutter.pwm_frequency); - analogWrite(pin[GPIO_PWM1+i], 50); - } - } -} - -#define SHT_DIV_ROUND(__A,__B) (((__A) + (__B)/2) / (__B)) - -int32_t ShutterPercentToRealPosition(uint32_t percent, uint32_t index) -{ - if (0 == percent) return 0; - if (100 == percent) return Shutter.open_max[index]; - if (Settings.shutter_set50percent[index] != 50) { - return (percent <= 5) ? Settings.shuttercoeff[2][index] * percent : Settings.shuttercoeff[1][index] * percent + Settings.shuttercoeff[0][index]; - } else { - uint32_t realpos; - - for (uint32_t j = 0; j < 5; j++) { - if (0 == Settings.shuttercoeff[j][index]) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SHT: RESET/INIT CALIBRATION MATRIX DIV 0")); - for (uint32_t k = 0; k < 5; k++) { - Settings.shuttercoeff[k][index] = SHT_DIV_ROUND(calibrate_pos[k+1] * 1000, calibrate_pos[5]); - } - } - } - for (uint32_t i = 0; i < 5; i++) { - if ((percent * 10) >= Settings.shuttercoeff[i][index]) { - realpos = SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i+1], 100); - - } else { - if (0 == i) { - realpos = SHT_DIV_ROUND(SHT_DIV_ROUND(percent * Shutter.open_max[index] * calibrate_pos[i+1], Settings.shuttercoeff[i][index]), 10); - } else { - - - realpos += SHT_DIV_ROUND(SHT_DIV_ROUND((percent*10 - Settings.shuttercoeff[i-1][index] ) * Shutter.open_max[index] * (calibrate_pos[i+1] - calibrate_pos[i]), Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]), 100); - } - break; - } - } - return realpos; - } -} - -uint8_t ShutterRealToPercentPosition(int32_t realpos, uint32_t index) -{ - if (0 >= realpos) return 0; - if (Shutter.open_max[index] <= realpos) return 100; - if (Settings.shutter_set50percent[index] != 50) { - return (Settings.shuttercoeff[2][index] * 5 > realpos) ? SHT_DIV_ROUND(realpos, Settings.shuttercoeff[2][index]) : SHT_DIV_ROUND(realpos-Settings.shuttercoeff[0][index], Settings.shuttercoeff[1][index]); - } else { - uint16_t realpercent; - - for (uint32_t i = 0; i < 5; i++) { - if (realpos >= Shutter.open_max[index] * calibrate_pos[i+1] / 100) { - realpercent = SHT_DIV_ROUND(Settings.shuttercoeff[i][index], 10); - - } else { - if (0 == i) { - realpercent = SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i], 100)) * 10 * Settings.shuttercoeff[i][index], calibrate_pos[i+1]), Shutter.open_max[index]); - } else { - - - - realpercent += SHT_DIV_ROUND(SHT_DIV_ROUND((realpos - SHT_DIV_ROUND(Shutter.open_max[index] * calibrate_pos[i], 100)) * 10 * (Settings.shuttercoeff[i][index] - Settings.shuttercoeff[i-1][index]), (calibrate_pos[i+1] - calibrate_pos[i])), Shutter.open_max[index]) ; - } - break; - } - } - return realpercent; - } -} - -void ShutterInit(void) -{ - shutters_present = 0; - Shutter.mask = 0; - - Shutter.old_power = power; - bool relay_in_interlock = false; - - - if (Settings.shutter_startrelay[MAX_SHUTTERS] == 0) { - Shutter.max_pwm_frequency = Settings.shuttercoeff[4][3] > 0 ? Settings.shuttercoeff[4][3] : Shutter.max_pwm_frequency; - } - for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { - - Settings.shutter_startrelay[i] = (Settings.shutter_startrelay[i] == 0 && i == 0? 1 : Settings.shutter_startrelay[i]); - if (Settings.shutter_startrelay[i] && (Settings.shutter_startrelay[i] < 9)) { - shutters_present++; - - - Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1) ; - - for (uint32_t j = 0; j < MAX_INTERLOCKS * Settings.flag.interlock; j++) { - - if (Settings.interlock[j] && (Settings.interlock[j] & Shutter.mask)) { - - relay_in_interlock = true; - } - } - if (relay_in_interlock) { - if (Settings.pulse_timer[i] > 0) { - Shutter.mode = SHT_PULSE_OPEN__PULSE_CLOSE; - } else { - Shutter.mode = SHT_OFF_OPEN__OFF_CLOSE; - } - } else { - Shutter.mode = SHT_OFF_ON__OPEN_CLOSE; - if ((pin[GPIO_PWM1+i] < 99) && (pin[GPIO_CNTR1+i] < 99)) { - Shutter.mode = SHT_OFF_ON__OPEN_CLOSE_STEPPER; - Shutter.pwm_frequency = 0; - analogWriteFreq(Shutter.pwm_frequency); - analogWrite(pin[GPIO_PWM1+i], 50); - } - } - - TickerShutter.attach_ms(50, ShutterRtc50mS ); - - Settings.shutter_set50percent[i] = (Settings.shutter_set50percent[i] > 0) ? Settings.shutter_set50percent[i] : 50; - - - Shutter.open_time[i] = (Settings.shutter_opentime[i] > 0) ? Settings.shutter_opentime[i] : 100; - Shutter.close_time[i] = (Settings.shutter_closetime[i] > 0) ? Settings.shutter_closetime[i] : 100; - - - Shutter.open_max[i] = 200 * Shutter.open_time[i]; - Shutter.close_velocity[i] = Shutter.open_max[i] / Shutter.close_time[i] / 2 ; - Shutter.max_close_pwm_frequency[i] = Shutter.max_pwm_frequency*Shutter.open_time[i] / Shutter.close_time[i]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d Closefreq: %d"),i, Shutter.max_close_pwm_frequency[i]); - - - if (Settings.shutter_set50percent[i] != 50) { - Settings.shuttercoeff[1][i] = Shutter.open_max[i] * (100 - Settings.shutter_set50percent[i] ) / 5000; - Settings.shuttercoeff[0][i] = Shutter.open_max[i] - (Settings.shuttercoeff[1][i] * 100); - Settings.shuttercoeff[2][i] = (Settings.shuttercoeff[0][i] + 5 * Settings.shuttercoeff[1][i]) / 5; - } - Shutter.mask |= 3 << (Settings.shutter_startrelay[i] -1); - - Shutter.real_position[i] = ShutterPercentToRealPosition(Settings.shutter_position[i], i); - - Shutter.start_position[i] = Shutter.target_position[i] = Shutter.real_position[i]; - Shutter.motordelay[i] = Settings.shutter_motordelay[i]; - - char shutter_open_chr[10]; - dtostrfd((float)Shutter.open_time[i] / 10 , 1, shutter_open_chr); - char shutter_close_chr[10]; - dtostrfd((float)Shutter.close_time[i] / 10, 1, shutter_close_chr); - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Shutter %d (Relay:%d): Init. Pos: %d [%d %%], Open Vel.: 100, Close Vel.: %d , Max Way: %d, Opentime %s [s], Closetime %s [s], CoeffCalc: c0: %d, c1 %d, c2: %d, c3: %d, c4: %d, binmask %d, is inverted %d, is locked %d, end stop time enabled %d, shuttermode %d, motordelay %d"), - i+1, Settings.shutter_startrelay[i], Shutter.real_position[i], Settings.shutter_position[i], Shutter.close_velocity[i], Shutter.open_max[i], shutter_open_chr, shutter_close_chr, - Settings.shuttercoeff[0][i], Settings.shuttercoeff[1][i], Settings.shuttercoeff[2][i], Settings.shuttercoeff[3][i], Settings.shuttercoeff[4][i], - Shutter.mask, (Settings.shutter_options[i]&1) ? 1 : 0, (Settings.shutter_options[i]&2) ? 1 : 0, (Settings.shutter_options[i]&4) ? 1 : 0, Shutter.mode, Shutter.motordelay[i]); - - } else { - - break; - } - ShutterLimitRealAndTargetPositions(i); - Settings.shutter_accuracy = 1; - } -} - -void ShutterReportPosition(bool always) -{ - uint32_t shutter_moving = 0; - Response_P(PSTR("{")); - for (uint32_t i = 0; i < shutters_present; i++) { - - uint32_t position = ShutterRealToPercentPosition(Shutter.real_position[i], i); - if (Shutter.direction[i] != 0) { - shutter_moving = 1; - ShutterLogPos(i); - } - if (i) { ResponseAppend_P(PSTR(",")); } - ResponseAppend_P(JSON_SHUTTER_POS, i+1, (Settings.shutter_options[i] & 1) ? 100-position : position, Shutter.direction[i]); - } - ResponseJsonEnd(); - if (always || (1 == shutter_moving)) { - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_PRFX_SHUTTER)); - } - if (rules_flag.shutter_moving > shutter_moving) { - rules_flag.shutter_moved = 1; - } else { - rules_flag.shutter_moved = 0; - } - rules_flag.shutter_moving = shutter_moving; - -} - -void ShutterLimitRealAndTargetPositions(uint32_t i) { - if (Shutter.real_position[i]<0) Shutter.real_position[i] = 0; - if (Shutter.real_position[i]>Shutter.open_max[i]) Shutter.real_position[i] = Shutter.open_max[i]; - if (Shutter.target_position[i]<0) Shutter.target_position[i] = 0; - if (Shutter.target_position[i]>Shutter.open_max[i]) Shutter.target_position[i] = Shutter.open_max[i]; -} - -void ShutterUpdatePosition(void) -{ - - char scommand[CMDSZ]; - char stopic[TOPSZ]; - - for (uint32_t i = 0; i < shutters_present; i++) { - if (Shutter.direction[i] != 0) { - int32_t stop_position_delta = 20; - if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { - - - Shutter.real_position[i] = ShutterCounterBasedPosition(i); - - int32_t max_frequency = Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i]; - int32_t max_freq_change_per_sec = Shutter.max_pwm_frequency*steps_per_second / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); - int32_t min_runtime_ms = Shutter.pwm_frequency*1000 / max_freq_change_per_sec; - int32_t velocity = Shutter.direction[i] == 1 ? 100 : Shutter.close_velocity[i]; - int32_t minstopway = min_runtime_ms * velocity / 100 * Shutter.pwm_frequency / max_frequency * Shutter.direction[i] ; - - int32_t next_possible_stop = Shutter.real_position[i] + minstopway ; - stop_position_delta =200 * Shutter.pwm_frequency/max_frequency + Shutter.direction[i] * (next_possible_stop - Shutter.target_position[i]); - - - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: time: %d, velocity %d, minstopway %d,cur_freq %d, max_frequency %d, act_freq_change %d, min_runtime_ms %d, act.pos %d, next_stop %d, target: %d"),Shutter.time[i],velocity,minstopway, - Shutter.pwm_frequency,max_frequency, Shutter.accelerator[i],min_runtime_ms,Shutter.real_position[i], next_possible_stop,Shutter.target_position[i]); - - if (Shutter.accelerator[i] < 0 || next_possible_stop * Shutter.direction[i] > Shutter.target_position[i] * Shutter.direction[i] ) { - - Shutter.accelerator[i] = - tmin(tmax(max_freq_change_per_sec*(100-(Shutter.direction[i]*(Shutter.target_position[i]-next_possible_stop) ))/2000 , max_freq_change_per_sec*9/200), max_freq_change_per_sec*12/200); - - } else if ( Shutter.accelerator[i] > 0 && Shutter.pwm_frequency == max_frequency) { - Shutter.accelerator[i] = 0; - } - } else { - Shutter.real_position[i] = Shutter.start_position[i] + ( (Shutter.time[i] - Shutter.motordelay[i]) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); - } - if ( Shutter.real_position[i] * Shutter.direction[i] + stop_position_delta >= Shutter.target_position[i] * Shutter.direction[i] ) { - - - uint8_t cur_relay = Settings.shutter_startrelay[i] + (Shutter.direction[i] == 1 ? 0 : 1) ; - int16_t missing_steps; - - switch (Shutter.mode) { - case SHT_PULSE_OPEN__PULSE_CLOSE: - - if (SRC_PULSETIMER == last_source || SRC_SHUTTER == last_source || SRC_WEBGUI == last_source) { - ExecuteCommandPower(cur_relay, 1, SRC_SHUTTER); - } else { - last_source = SRC_SHUTTER; - } - break; - case SHT_OFF_ON__OPEN_CLOSE_STEPPER: - missing_steps = ((Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) - RtcSettings.pulse_counter[i]; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Remain steps %d, counter %d, freq %d"), missing_steps, RtcSettings.pulse_counter[i] ,Shutter.pwm_frequency); - Shutter.accelerator[i] = 0; - Shutter.pwm_frequency = Shutter.pwm_frequency > 250 ? 250 : Shutter.pwm_frequency; - analogWriteFreq(Shutter.pwm_frequency); - analogWrite(pin[GPIO_PWM1+i], 50); - Shutter.pwm_frequency = 0; - analogWriteFreq(Shutter.pwm_frequency); - while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter.target_position[i]-Shutter.start_position[i])*Shutter.direction[i]*Shutter.max_pwm_frequency/2000) { - delay(1); - } - analogWrite(pin[GPIO_PWM1+i], 0); - Shutter.real_position[i] = ShutterCounterBasedPosition(i); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT:Real %d, pulsecount %d, start %d"), Shutter.real_position[i],RtcSettings.pulse_counter[i], Shutter.start_position[i]); - - if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { - ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); - ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); - } - break; - case SHT_OFF_ON__OPEN_CLOSE: - if ((1 << (Settings.shutter_startrelay[i]-1)) & power) { - ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); - ExecuteCommandPower(Settings.shutter_startrelay[i]+1, 0, SRC_SHUTTER); - } - break; - case SHT_OFF_OPEN__OFF_CLOSE: - - if ((1 << (cur_relay-1)) & power) { - - ExecuteCommandPower(cur_relay, 0, SRC_SHUTTER); - } - break; - } - ShutterLimitRealAndTargetPositions(i); - Settings.shutter_position[i] = ShutterRealToPercentPosition(Shutter.real_position[i], i); - - ShutterLogPos(i); - - Shutter.start_position[i] = Shutter.real_position[i]; - - - snprintf_P(scommand, sizeof(scommand),PSTR(D_SHUTTER "%d"), i+1); - GetTopic_P(stopic, STAT, mqtt_topic, scommand); - Response_P("%d", (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]); - MqttPublish(stopic, Settings.flag.mqtt_power_retain); - - Shutter.direction[i] = 0; - ShutterReportPosition(true); - XdrvRulesProcess(); - } - } - } -} - -bool ShutterState(uint32_t device) -{ - device--; - device &= 3; - return (Settings.flag3.shutter_mode && - (Shutter.mask & (1 << (Settings.shutter_startrelay[device]-1))) ); -} - -void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) -{ - - if ( ( (1 == direction) && ((Shutter.open_max[i] - Shutter.real_position[i]) / 100 <= 2) ) - || ( (-1 == direction) && (Shutter.real_position[i] / Shutter.close_velocity[i] <= 2)) ) { - Shutter.skip_relay_change = 1; - } else { - if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { - Shutter.pwm_frequency = 0; - analogWriteFreq(Shutter.pwm_frequency); - analogWrite(pin[GPIO_PWM1+i], 0); - - if (pin[GPIO_CNTR1+i] < 99) { - RtcSettings.pulse_counter[i] = 0; - } - Shutter.accelerator[i] = Shutter.max_pwm_frequency / (Shutter.motordelay[i]>0 ? Shutter.motordelay[i] : 1); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Ramp up: %d"), Shutter.accelerator[i]); - } - Shutter.target_position[i] = target_pos; - Shutter.start_position[i] = Shutter.real_position[i]; - Shutter.time[i] = 0; - Shutter.skip_relay_change = 0; - Shutter.direction[i] = direction; - - } - -} - -void ShutterWaitForMotorStop(uint32_t i) -{ - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Wait for Motorstop..")); - if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) { - if (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode) { - - while (Shutter.pwm_frequency > 0) { - Shutter.accelerator[i] = 0; - Shutter.pwm_frequency = tmax(Shutter.pwm_frequency-((Shutter.direction[i] == 1 ? Shutter.max_pwm_frequency : Shutter.max_close_pwm_frequency[i])/(Shutter.motordelay[i]+1)) , 0); - analogWriteFreq(Shutter.pwm_frequency); - analogWrite(pin[GPIO_PWM1+i], 50); - delay(50); - } - analogWrite(pin[GPIO_PWM1+i], 0); - Shutter.real_position[i] = ShutterCounterBasedPosition(i); - } else { - ExecuteCommandPower(Settings.shutter_startrelay[i], 0, SRC_SHUTTER); - delay(MOTOR_STOP_TIME); - } - } else { - delay(MOTOR_STOP_TIME); - } -} - -int32_t ShutterCounterBasedPosition(uint32_t i) -{ - return ((int32_t)RtcSettings.pulse_counter[i]*Shutter.direction[i]*2000 / Shutter.max_pwm_frequency)+Shutter.start_position[i]; -} - -void ShutterRelayChanged(void) -{ - - - - - char stemp1[10]; - - for (uint32_t i = 0; i < shutters_present; i++) { - power_t powerstate_local = (power >> (Settings.shutter_startrelay[i] -1)) & 3; - - uint8 manual_relays_changed = ((Shutter.switched_relay >> (Settings.shutter_startrelay[i] -1)) & 3) && SRC_SHUTTER != last_source && SRC_PULSETIMER != last_source ; - - if (manual_relays_changed) { - - ShutterLimitRealAndTargetPositions(i); - if (Shutter.mode == SHT_OFF_ON__OPEN_CLOSE || Shutter.mode == SHT_OFF_ON__OPEN_CLOSE_STEPPER) { - ShutterWaitForMotorStop(i); - switch (powerstate_local) { - case 1: - ShutterStartInit(i, 1, Shutter.open_max[i]); - break; - case 3: - ShutterStartInit(i, -1, 0); - break; - default: - - Shutter.target_position[i] = Shutter.real_position[i]; - } - } else { - if (Shutter.direction[i] != 0 && (!powerstate_local || (powerstate_local && Shutter.mode == SHT_PULSE_OPEN__PULSE_CLOSE))) { - Shutter.target_position[i] = Shutter.real_position[i]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Switch OFF motor. Target: %ld, source: %s, powerstate_local %ld, Shutter.switched_relay %d, manual change %d"), i+1, Shutter.target_position[i], GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource), powerstate_local,Shutter.switched_relay,manual_relays_changed); - } else { - last_source = SRC_SHUTTER; - if (powerstate_local == 2) { - - ShutterWaitForMotorStop(i); - ShutterStartInit(i, -1, 0); - } else { - - ShutterWaitForMotorStop(i); - ShutterStartInit(i, 1, Shutter.open_max[i]); - } - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Shutter %d: Target: %ld, powerstatelocal %d"), i+1, Shutter.target_position[i], powerstate_local); - } - } - } -} - -bool ShutterButtonIsSimultaneousHold(uint32_t button_index, uint32_t shutter_index) { - - uint32 min_shutterbutton_hold_timer = -1; - for (uint32_t i = 0; i < MAX_KEYS; i++) { - if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (Button.hold_timer[i] < min_shutterbutton_hold_timer)) - min_shutterbutton_hold_timer = Button.hold_timer[i]; - } - return (min_shutterbutton_hold_timer > (Button.hold_timer[button_index]>>1)); -} - -void ShutterButtonHandler(void) -{ - uint8_t buttonState = SHT_NOT_PRESSED; - uint8_t button = XdrvMailbox.payload; - uint8_t press_index; - uint32_t button_index = XdrvMailbox.index; - uint8_t shutter_index = Settings.shutter_button[button_index] & 0x03; - - uint16_t loops_per_second = 1000 / Settings.button_debounce; - - if ((PRESSED == button) && (NOT_PRESSED == Button.last_state[button_index])) { - if (Settings.flag.button_single) { - buttonState = SHT_PRESSED_MULTI; - press_index = 1; - } else { - if ((Shutter.direction[shutter_index]) && (Button.press_counter[button_index]==0)) { - buttonState = SHT_PRESSED_IMMEDIATE; - press_index = 1; - Button.press_counter[button_index] = 99; - } else - Button.press_counter[button_index] = (Button.window_timer[button_index]) ? Button.press_counter[button_index] +1 : 1; - Button.window_timer[button_index] = loops_per_second / 2; - } - blinks = 201; - } - - if (NOT_PRESSED == button) { - Button.hold_timer[button_index] = 0; - } else { - Button.hold_timer[button_index]++; - if (!Settings.flag.button_single) { - if (Settings.param[P_HOLD_IGNORE] > 0) { - if (Button.hold_timer[button_index] > loops_per_second * Settings.param[P_HOLD_IGNORE] / 10) { - Button.hold_timer[button_index] = 0; - Button.press_counter[button_index] = 0; - } - } - if ((Button.press_counter[button_index]<99) && (Button.hold_timer[button_index] == loops_per_second * Settings.param[P_HOLD_TIME] / 10)) { - - if (ShutterButtonIsSimultaneousHold(button_index, shutter_index)) { - - for (uint32_t i = 0; i < MAX_KEYS; i++) - if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index)) - Button.press_counter[i] = 99; - press_index = 0; - buttonState = SHT_PRESSED_HOLD_SIMULTANEOUS; - } - if (Button.press_counter[button_index]<99) { - press_index = 0; - buttonState = SHT_PRESSED_HOLD; - } - Button.press_counter[button_index] = 0; - } - if ((Button.press_counter[button_index]==0) && (Button.hold_timer[button_index] == loops_per_second * IMMINENT_RESET_FACTOR * Settings.param[P_HOLD_TIME] / 10)) { - - if (ShutterButtonIsSimultaneousHold(button_index, shutter_index)) { - - press_index = 0; - buttonState = SHT_PRESSED_EXT_HOLD_SIMULTANEOUS; - } - } - } - } - - if (!Settings.flag.button_single) { - if (Button.window_timer[button_index]) { - Button.window_timer[button_index]--; - } else { - if (!restart_flag && !Button.hold_timer[button_index] && (Button.press_counter[button_index] > 0)) { - if (Button.press_counter[button_index]<99) { - - uint32 min_shutterbutton_press_counter = -1; - for (uint32_t i = 0; i < MAX_KEYS; i++) { - if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index) && (Button.press_counter[i] < min_shutterbutton_press_counter)) - min_shutterbutton_press_counter = Button.press_counter[i]; - } - if (min_shutterbutton_press_counter == Button.press_counter[button_index]) { - - press_index = Button.press_counter[button_index]; - for (uint32_t i = 0; i < MAX_KEYS; i++) - if ((Settings.shutter_button[i] & (1<<31)) && ((Settings.shutter_button[i] & 0x03) == shutter_index)) - Button.press_counter[i] = 99; - buttonState = SHT_PRESSED_MULTI_SIMULTANEOUS; - } - if ((buttonState != SHT_PRESSED_MULTI_SIMULTANEOUS) && (Button.press_counter[button_index]<99)) { - - press_index = Button.press_counter[button_index]; - buttonState = SHT_PRESSED_MULTI; - } - } - Button.press_counter[button_index] = 0; - } - } - } - - if (buttonState != SHT_NOT_PRESSED) { - if (buttonState == SHT_PRESSED_MULTI_SIMULTANEOUS) { - if ((press_index>=5) && (press_index<=7) && (!Settings.flag.button_restrict)) { - - char scmnd[20]; - GetTextIndexed(scmnd, sizeof(scmnd), press_index -3, kCommands); - ExecuteCommand(scmnd, SRC_BUTTON); - return; - } - } else if (buttonState == SHT_PRESSED_EXT_HOLD_SIMULTANEOUS) { - - if (!Settings.flag.button_restrict) { - char scmnd[20]; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_RESET " 1")); - ExecuteCommand(scmnd, SRC_BUTTON); - return; - } - } else if (buttonState <= SHT_PRESSED_IMMEDIATE) { - if (Settings.shutter_startrelay[shutter_index] && Settings.shutter_startrelay[shutter_index] <9) { - uint8_t pos_press_index = (buttonState == SHT_PRESSED_HOLD) ? 3 : (press_index-1); - if (pos_press_index>3) pos_press_index=3; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: shutter %d, button %d = %d (single=1, double=2, tripple=3, hold=4)"), shutter_index+1, button_index+1, pos_press_index+1); - XdrvMailbox.index = shutter_index +1; - last_source = SRC_BUTTON; - XdrvMailbox.data_len = 0; - char databuf[1] = ""; - XdrvMailbox.data = databuf; - XdrvMailbox.command = NULL; - if (buttonState == SHT_PRESSED_IMMEDIATE) { - XdrvMailbox.payload = XdrvMailbox.index; - CmndShutterStop(); - } - else { - uint8_t position = (Settings.shutter_button[button_index]>>(6*pos_press_index + 2)) & 0x03f; - if (position) { - if (Shutter.direction[shutter_index]) { - XdrvMailbox.payload = XdrvMailbox.index; - CmndShutterStop(); - } else { - XdrvMailbox.payload = position = (position-1)<<1; - CmndShutterPosition(); - if (Settings.shutter_button[button_index] & ((0x01<<26)< 0) && (XdrvMailbox.index <= shutters_present)) { - if (!(Settings.shutter_options[XdrvMailbox.index-1] & 2)) { - if ((1 == XdrvMailbox.index) && (XdrvMailbox.payload != -99)) { - XdrvMailbox.index = XdrvMailbox.payload; - } - uint32_t i = XdrvMailbox.index -1; - if (Shutter.direction[i] != 0) { - - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Stop moving %d: dir: %d"), XdrvMailbox.index, Shutter.direction[i]); - - int32_t temp_realpos = Shutter.start_position[i] + ( (Shutter.time[i]+10) * (Shutter.direction[i] > 0 ? 100 : -Shutter.close_velocity[i])); - XdrvMailbox.payload = ShutterRealToPercentPosition(temp_realpos, i); - - last_source = SRC_WEBGUI; - CmndShutterPosition(); - } else { - if (XdrvMailbox.command) - ResponseCmndDone(); - } - } else { - if (XdrvMailbox.command) - ResponseCmndIdxChar("Locked"); - } - } -} - -void CmndShutterPosition(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (!(Settings.shutter_options[XdrvMailbox.index-1] & 2)) { - uint32_t index = XdrvMailbox.index-1; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: Pos. in: payload %s (%d), payload %d, idx %d, src %d"), XdrvMailbox.data , XdrvMailbox.data_len, XdrvMailbox.payload , XdrvMailbox.index, last_source ); - - - if ((XdrvMailbox.data_len > 1) && (XdrvMailbox.payload <= 0)) { - - if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_UP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_OPEN) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLEUP))) { - CmndShutterOpen(); - return; - } - if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_DOWN) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_CLOSE) || ((Shutter.direction[index]==0) && !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLEDOWN))) { - CmndShutterClose(); - return; - } - if (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_STOP) || ((Shutter.direction[index]) && (!strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLEUP) || !strcasecmp(XdrvMailbox.data,D_CMND_SHUTTER_TOGGLEDOWN)))) { - XdrvMailbox.payload = -99; - CmndShutterStop(); - return; - } - } - - int8_t target_pos_percent = (XdrvMailbox.payload < 0) ? 0 : ((XdrvMailbox.payload > 100) ? 100 : XdrvMailbox.payload); - - target_pos_percent = ((Settings.shutter_options[index] & 1) && (SRC_WEBGUI != last_source)) ? 100 - target_pos_percent : target_pos_percent; - if (XdrvMailbox.payload != -99) { - - Shutter.target_position[index] = ShutterPercentToRealPosition(target_pos_percent, index); - Shutter.accelerator[index] = Shutter.max_pwm_frequency / ((Shutter.motordelay[index] > 0) ? Shutter.motordelay[index] : 1); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SHT: lastsource %d:, real %d, target %d, payload %d"), last_source, Shutter.real_position[index] ,Shutter.target_position[index],target_pos_percent); - } - if ( (target_pos_percent >= 0) && (target_pos_percent <= 100) && abs(Shutter.target_position[index] - Shutter.real_position[index] ) / Shutter.close_velocity[index] > 2) { - if (Settings.shutter_options[index] & 4) { - if (0 == target_pos_percent) Shutter.target_position[index] -= 1 * 2000; - if (100 == target_pos_percent) Shutter.target_position[index] += 1 * 2000; - } - int8_t new_shutterdirection = Shutter.real_position[index] < Shutter.target_position[index] ? 1 : -1; - if (Shutter.direction[index] == -new_shutterdirection) { - - if (SHT_PULSE_OPEN__PULSE_CLOSE == Shutter.mode) { - - ExecuteCommandPower(Settings.shutter_startrelay[index] + ((new_shutterdirection == 1) ? 0 : 1), 1, SRC_SHUTTER); - delay(100); - } else { - if (SHT_OFF_OPEN__OFF_CLOSE == Shutter.mode) { - ExecuteCommandPower(Settings.shutter_startrelay[index] + ((new_shutterdirection == 1) ? 1 : 0), 0, SRC_SHUTTER); - ShutterWaitForMotorStop(index); - } - } - } - if (Shutter.direction[index] != new_shutterdirection) { - if ((SHT_OFF_ON__OPEN_CLOSE == Shutter.mode) || (SHT_OFF_ON__OPEN_CLOSE_STEPPER == Shutter.mode)) { - - ShutterWaitForMotorStop(index); - ExecuteCommandPower(Settings.shutter_startrelay[index], 0, SRC_SHUTTER); - ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); - if (Shutter.skip_relay_change == 0) { - - ExecuteCommandPower(Settings.shutter_startrelay[index] +1, new_shutterdirection == 1 ? 0 : 1, SRC_SHUTTER); - - ExecuteCommandPower(Settings.shutter_startrelay[index], 1, SRC_SHUTTER); - } - } else { - - AddLog_P2(LOG_LEVEL_INFO, PSTR("SHT: Start in dir %d"), Shutter.direction[index]); - ShutterStartInit(index, new_shutterdirection, Shutter.target_position[index]); - if (Shutter.skip_relay_change == 0) { - ExecuteCommandPower(Settings.shutter_startrelay[index] + (new_shutterdirection == 1 ? 0 : 1), 1, SRC_SHUTTER); - } - - } - Shutter.switched_relay = 0; - } - } else { - target_pos_percent = ShutterRealToPercentPosition(Shutter.real_position[index], index); - ShutterReportPosition(true); - } - XdrvMailbox.index = index +1; - if (XdrvMailbox.command) - ResponseCmndIdxNumber((Settings.shutter_options[index] & 1) ? 100 - target_pos_percent : target_pos_percent); - } else { - ShutterReportPosition(true); - if (XdrvMailbox.command) - ResponseCmndIdxChar("Locked"); - } - } -} - -void CmndShutterOpenTime(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.data_len > 0) { - Settings.shutter_opentime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data)); - ShutterInit(); - } - char time_chr[10]; - dtostrfd((float)(Settings.shutter_opentime[XdrvMailbox.index -1]) / 10, 1, time_chr); - ResponseCmndIdxChar(time_chr); - } -} - -void CmndShutterCloseTime(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.data_len > 0) { - Settings.shutter_closetime[XdrvMailbox.index -1] = (uint16_t)(10 * CharToFloat(XdrvMailbox.data)); - ShutterInit(); - } - char time_chr[10]; - dtostrfd((float)(Settings.shutter_closetime[XdrvMailbox.index -1]) / 10, 1, time_chr); - ResponseCmndIdxChar(time_chr); - } -} - -void CmndShutterMotorDelay(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.data_len > 0) { - Settings.shutter_motordelay[XdrvMailbox.index -1] = (uint16_t)(steps_per_second * CharToFloat(XdrvMailbox.data)); - ShutterInit(); - } - char time_chr[10]; - dtostrfd((float)(Settings.shutter_motordelay[XdrvMailbox.index -1]) / steps_per_second, 2, time_chr); - ResponseCmndIdxChar(time_chr); - } -} - -void CmndShutterRelay(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 64)) { - Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; - if (XdrvMailbox.payload > 0) { - Shutter.mask |= 3 << (XdrvMailbox.payload - 1); - } else { - Shutter.mask ^= 3 << (Settings.shutter_startrelay[XdrvMailbox.index -1] - 1); - } - Settings.shutter_startrelay[XdrvMailbox.index -1] = XdrvMailbox.payload; - ShutterInit(); - - } - ResponseCmndIdxNumber(Settings.shutter_startrelay[XdrvMailbox.index -1]); - } -} - -void CmndShutterButton(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_SHUTTERS)) { - uint32_t setting = 0; -# 915 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_27_shutter.ino" - if (XdrvMailbox.data_len > 0) { - uint32_t i = 0; - uint32_t button_index = 0; - bool done = false; - bool isShortCommand = false; - char *str_ptr; - - char data_copy[strlen(XdrvMailbox.data) +1]; - strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); - - for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < (1+4+4+1); str = strtok_r(nullptr, " ", &str_ptr), i++) { - int field; - if (str[0] == '-') { - field = -1; - } else { - field = atoi(str); - } - switch (i) { - case 0: - if ((field >= -1) && (field<=4)) { - button_index = (field<=0)?(-1):field; - done = (button_index==-1); - } else - done = true; - break; - case 1: - if (!strcmp_P(str, PSTR("up"))) { - setting |= (((100>>1)+1)<<2) | (((50>>1)+1)<<8) | (((75>>1)+1)<<14) | (((100>>1)+1)<<20); - isShortCommand = true; - break; - } else if (!strcmp_P(str, PSTR("down"))) { - setting |= (((0>>1)+1)<<2) | (((50>>1)+1)<<8) | (((25>>1)+1)<<14) | (((0>>1)+1)<<20); - isShortCommand = true; - break; - } else if (!strcmp_P(str, PSTR("updown"))) { - setting |= (((100>>1)+1)<<2) | (((0>>1)+1)<<8) | (((50>>1)+1)<<14); - isShortCommand = true; - break; - } - case 2: - if (isShortCommand) { - if ((field==1) && (setting & (0x3F<<(2+6*3)))) - - setting |= (0x3<<29); - done = true; - break; - } - case 3: - case 4: - if ((field >= -1) && (field<=100)) - setting |= (((field>>1)+1)<<(i*6 + (2-6))); - break; - case 5: - case 6: - case 7: - case 8: - case 9: - if (field==1) - setting |= (1<<(i + (26-5))); - break; - } - if (done) break; - } - - if (button_index) { - if (button_index==-1) { - - for (uint32_t i=0 ; i < MAX_KEYS ; i++) - if ((Settings.shutter_button[i]&0x3) == (XdrvMailbox.index-1)) - Settings.shutter_button[i] = 0; - } else { - if (setting) { - - setting |= (1<<31); - setting |= (XdrvMailbox.index-1) & 0x3; - } - Settings.shutter_button[button_index-1] = setting; - } - } - } - char setting_chr[30*MAX_KEYS] = "-", *setting_chr_ptr = setting_chr; - for (uint32_t i=0 ; i < MAX_KEYS ; i++) { - setting = Settings.shutter_button[i]; - if ((setting&(1<<31)) && ((setting&0x3) == (XdrvMailbox.index-1))) { - if (*setting_chr_ptr == 0) - setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR("|")); - setting_chr_ptr += snprintf_P(setting_chr_ptr, 2, PSTR("%d"), i+1); - - for (uint32_t j=0 ; j < 4 ; j++) { - int8_t pos = (((setting>> (2+6*j))&(0x3f))-1)<<1; - if (pos>=0) - setting_chr_ptr += snprintf_P(setting_chr_ptr, 5, PSTR(" %d"), pos); - else - setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" -")); - } - for (uint32_t j=0 ; j < 5 ; j++) { - bool mqtt = ((setting>>(26+j))&(0x01)!=0); - if (mqtt) - setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" 1")); - else - setting_chr_ptr += sprintf_P(setting_chr_ptr, PSTR(" -")); - } - } - } - ResponseCmndIdxChar(setting_chr); - } -} - -void CmndShutterSetHalfway(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - Settings.shutter_set50percent[XdrvMailbox.index -1] = (Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 100 - XdrvMailbox.payload : XdrvMailbox.payload; - ShutterInit(); - } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 100 - Settings.shutter_set50percent[XdrvMailbox.index -1] : Settings.shutter_set50percent[XdrvMailbox.index -1]); - } -} - -void CmndShutterFrequency(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= 20000)) { - Shutter.max_pwm_frequency = XdrvMailbox.payload; - if (shutters_present < 4) { - Settings.shuttercoeff[4][3] = Shutter.max_pwm_frequency; - } - ShutterInit(); - ResponseCmndNumber(XdrvMailbox.payload); - } else { - ResponseCmndNumber(Shutter.max_pwm_frequency); - } -} - -void CmndShutterSetClose(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - Shutter.real_position[XdrvMailbox.index -1] = 0; - ShutterStartInit(XdrvMailbox.index -1, 0, 0); - Settings.shutter_position[XdrvMailbox.index -1] = 0; - ResponseCmndIdxChar(D_CONFIGURATION_RESET); - } -} - -void CmndShutterInvert(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.payload == 0) { - Settings.shutter_options[XdrvMailbox.index -1] &= ~(1); - } else if (XdrvMailbox.payload == 1) { - Settings.shutter_options[XdrvMailbox.index -1] |= (1); - } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 1) ? 1 : 0); - } -} - -void CmndShutterCalibration(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.data_len > 0) { - uint32_t i = 0; - char *str_ptr; - - char data_copy[strlen(XdrvMailbox.data) +1]; - strncpy(data_copy, XdrvMailbox.data, sizeof(data_copy)); - - for (char *str = strtok_r(data_copy, " ", &str_ptr); str && i < 5; str = strtok_r(nullptr, " ", &str_ptr), i++) { - int field = atoi(str); - - - if ((field <= 0) || (field > 30000) || ( (i>0) && (field <= messwerte[i-1]) ) ) { - break; - } - messwerte[i] = field; - } - for (i = 0; i < 5; i++) { - Settings.shuttercoeff[i][XdrvMailbox.index -1] = SHT_DIV_ROUND((uint32_t)messwerte[i] * 1000, messwerte[4]); - AddLog_P2(LOG_LEVEL_INFO, PSTR("Settings.shuttercoeff: %d, i: %d, value: %d, messwert %d"), i,XdrvMailbox.index -1,Settings.shuttercoeff[i][XdrvMailbox.index -1], messwerte[i]); - } - ShutterInit(); - ResponseCmndIdxChar(XdrvMailbox.data); - } else { - char setting_chr[30] = "0"; - snprintf_P(setting_chr, sizeof(setting_chr), PSTR("%d %d %d %d %d"), Settings.shuttercoeff[0][XdrvMailbox.index -1], Settings.shuttercoeff[1][XdrvMailbox.index -1], Settings.shuttercoeff[2][XdrvMailbox.index -1], Settings.shuttercoeff[3][XdrvMailbox.index -1], Settings.shuttercoeff[4][XdrvMailbox.index -1]); - ResponseCmndIdxChar(setting_chr); - } - } -} - -void CmndShutterLock(void) { - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.payload == 0) { - Settings.shutter_options[XdrvMailbox.index -1] &= ~(2); - } else if (XdrvMailbox.payload == 1) { - Settings.shutter_options[XdrvMailbox.index -1] |= (2); - } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 2) ? 1 : 0); - } -} - -void CmndShutterEnableEndStopTime(void) { - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= shutters_present)) { - if (XdrvMailbox.payload == 0) { - Settings.shutter_options[XdrvMailbox.index -1] &= ~(4); - } else if (XdrvMailbox.payload == 1) { - Settings.shutter_options[XdrvMailbox.index -1] |= (4); - } - ResponseCmndIdxNumber((Settings.shutter_options[XdrvMailbox.index -1] & 4) ? 1 : 0); - } -} - - - - - -bool Xdrv27(uint8_t function) -{ - bool result = false; - - if (Settings.flag3.shutter_mode) { - switch (function) { - case FUNC_PRE_INIT: - ShutterInit(); - break; - case FUNC_EVERY_50_MSECOND: - ShutterUpdatePosition(); - break; - case FUNC_EVERY_SECOND: - - ShutterReportPosition(false); - break; - - case FUNC_COMMAND: - result = DecodeCommand(kShutterCommands, ShutterCommand); - break; - case FUNC_JSON_APPEND: - for (uint8_t i = 0; i < shutters_present; i++) { - uint8_t position = (Settings.shutter_options[i] & 1) ? 100 - Settings.shutter_position[i]: Settings.shutter_position[i]; - ResponseAppend_P(","); - ResponseAppend_P(JSON_SHUTTER_POS, i+1, position, Shutter.direction[i]); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (0 == i)) { - DomoticzSensor(DZ_SHUTTER, position); - } -#endif - } - break; - case FUNC_SET_POWER: - char stemp1[10]; - - Shutter.switched_relay = XdrvMailbox.index ^ Shutter.old_power; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Switched relay: %d by %s"), Shutter.switched_relay,GetTextIndexed(stemp1, sizeof(stemp1), last_source, kCommandSource)); - ShutterRelayChanged(); - Shutter.old_power = XdrvMailbox.index; - break; - case FUNC_SET_DEVICE_POWER: - if (Shutter.skip_relay_change ) { - uint8_t i; - for (i = 0; i < devices_present; i++) { - if (Shutter.switched_relay &1) { - break; - } - Shutter.switched_relay >>= 1; - } - - result = true; - Shutter.skip_relay_change = 0; - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Skipping switch off relay %d"),i); - ExecuteCommandPower(i+1, 0, SRC_SHUTTER); - } - break; - case FUNC_BUTTON_PRESSED: - if (Settings.shutter_button[XdrvMailbox.index] & (1<<31)) { - ShutterButtonHandler(); - result = true; - } - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_28_pcf8574.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_28_pcf8574.ino" -#ifdef USE_I2C -#ifdef USE_PCF8574 - - - - - - - -#define XDRV_28 28 -#define XI2C_02 2 - -#define PCF8574_ADDR1 0x20 -#define PCF8574_ADDR2 0x38 - -struct PCF8574 { - int error; - uint8_t pin[64]; - uint8_t address[MAX_PCF8574]; - uint8_t pin_mask[MAX_PCF8574] = { 0 }; - uint8_t max_connected_ports = 0; - uint8_t max_devices = 0; - char stype[9]; - bool type = false; -} Pcf8574; - -void Pcf8574SwitchRelay(void) -{ - for (uint32_t i = 0; i < devices_present; i++) { - uint8_t relay_state = bitRead(XdrvMailbox.index, i); - - - - if (Pcf8574.max_devices > 0 && Pcf8574.pin[i] < 99) { - uint8_t board = Pcf8574.pin[i]>>3; - uint8_t oldpinmask = Pcf8574.pin_mask[board]; - uint8_t _val = bitRead(rel_inverted, i) ? !relay_state : relay_state; - - - - if (_val) { - Pcf8574.pin_mask[board] |= _val << (Pcf8574.pin[i]&0x7); - } else { - Pcf8574.pin_mask[board] &= ~(1 << (Pcf8574.pin[i]&0x7)); - } - if (oldpinmask != Pcf8574.pin_mask[board]) { - Wire.beginTransmission(Pcf8574.address[board]); - Wire.write(Pcf8574.pin_mask[board]); - Pcf8574.error = Wire.endTransmission(); - } - - } - } -} - -void Pcf8574Init(void) -{ - uint8_t pcf8574_address = PCF8574_ADDR1; - while ((Pcf8574.max_devices < MAX_PCF8574) && (pcf8574_address < PCF8574_ADDR2 +8)) { - - - - if (I2cSetDevice(pcf8574_address)) { - Pcf8574.type = true; - - Pcf8574.address[Pcf8574.max_devices] = pcf8574_address; - Pcf8574.max_devices++; - - strcpy(Pcf8574.stype, "PCF8574"); - if (pcf8574_address >= PCF8574_ADDR2) { - strcpy(Pcf8574.stype, "PCF8574A"); - } - I2cSetActiveFound(pcf8574_address, Pcf8574.stype); - } - - pcf8574_address++; -#ifdef USE_MCP230xx_ADDR - if (USE_MCP230xx_ADDR == pcf8574_address) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Addr: 0x%x reserved for MCP320xx, skipping PCF8574 probe"), pcf8574_address); - pcf8574_address++; - } -#endif - if ((PCF8574_ADDR1 +7) == pcf8574_address) { - pcf8574_address = PCF8574_ADDR2 +1; - } - } - if (Pcf8574.type) { - for (uint32_t i = 0; i < sizeof(Pcf8574.pin); i++) { - Pcf8574.pin[i] = 99; - } - devices_present = devices_present - Pcf8574.max_connected_ports; - Pcf8574.max_connected_ports = 0; - for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PCF: Device %d config 0x%02x"), idx +1, Settings.pcf8574_config[idx]); - - for (uint32_t i = 0; i < 8; i++) { - uint8_t _result = Settings.pcf8574_config[idx] >> i &1; - - if (_result > 0) { - Pcf8574.pin[devices_present] = i + 8 * idx; - bitWrite(rel_inverted, devices_present, Settings.flag3.pcf8574_ports_inverted); - devices_present++; - Pcf8574.max_connected_ports++; - } - } - } - AddLog_P2(LOG_LEVEL_INFO, PSTR("PCF: Total devices %d, PCF8574 output ports %d"), Pcf8574.max_devices, Pcf8574.max_connected_ports); - } -} - - - - - -#ifdef USE_WEBSERVER - -#define WEB_HANDLE_PCF8574 "pcf" - -const char HTTP_BTN_MENU_PCF8574[] PROGMEM = - "

"; - -const char HTTP_FORM_I2C_PCF8574_1[] PROGMEM = - "
 " D_PCF8574_PARAMETERS " " - "
" - "

" D_INVERT_PORTS "


"; - -const char HTTP_FORM_I2C_PCF8574_2[] PROGMEM = - "" D_DEVICE " %d " D_PORT " %d"; - -void HandlePcf8574(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_CONFIGURE_PCF8574)); - - if (WebServer->hasArg("save")) { - Pcf8574SaveSettings(); - WebRestart(1); - return; - } - - WSContentStart_P(D_CONFIGURE_PCF8574); - WSContentSendStyle(); - WSContentSend_P(HTTP_FORM_I2C_PCF8574_1, (Settings.flag3.pcf8574_ports_inverted) ? " checked" : ""); - WSContentSend_P(HTTP_TABLE100); - for (uint32_t idx = 0; idx < Pcf8574.max_devices; idx++) { - for (uint32_t idx2 = 0; idx2 < 8; idx2++) { - uint8_t helper = 1 << idx2; - WSContentSend_P(HTTP_FORM_I2C_PCF8574_2, - idx +1, idx2, - idx2 + 8*idx, - idx2 + 8*idx, - ((helper & Settings.pcf8574_config[idx]) >> idx2 == 0) ? " selected " : " ", - ((helper & Settings.pcf8574_config[idx]) >> idx2 == 1) ? " selected " : " " - ); - } - } - WSContentSend_P(PSTR("")); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void Pcf8574SaveSettings(void) -{ - char stemp[7]; - char tmp[100]; - - - - Settings.flag3.pcf8574_ports_inverted = WebServer->hasArg("b1"); - for (byte idx = 0; idx < Pcf8574.max_devices; idx++) { - byte count=0; - byte n = Settings.pcf8574_config[idx]; - while(n!=0) { - n = n&(n-1); - count++; - } - if (count <= devices_present) { - devices_present = devices_present - count; - } - for (byte i = 0; i < 8; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("i2cs%d"), i+8*idx); - WebGetArg(stemp, tmp, sizeof(tmp)); - byte _value = (!strlen(tmp)) ? 0 : atoi(tmp); - if (_value) { - Settings.pcf8574_config[idx] = Settings.pcf8574_config[idx] | 1 << i; - devices_present++; - Pcf8574.max_connected_ports++; - } else { - Settings.pcf8574_config[idx] = Settings.pcf8574_config[idx] & ~(1 << i ); - } - } - - - - } -} -#endif - - - - - -bool Xdrv28(uint8_t function) -{ - if (!I2cEnabled(XI2C_02)) { return false; } - - bool result = false; - - if (FUNC_PRE_INIT == function) { - Pcf8574Init(); - } - else if (Pcf8574.type) { - switch (function) { - case FUNC_SET_POWER: - Pcf8574SwitchRelay(); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_ADD_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_PCF8574); - break; - case FUNC_WEB_ADD_HANDLER: - WebServer->on("/" WEB_HANDLE_PCF8574, HandlePcf8574); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_29_deepsleep.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_29_deepsleep.ino" -#ifdef USE_DEEPSLEEP -# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_29_deepsleep.ino" -#define XDRV_29 29 - -#define D_PRFX_DEEPSLEEP "DeepSleep" -#define D_CMND_DEEPSLEEP_TIME "Time" - -const uint32_t DEEPSLEEP_MAX = 10 * 366 * 24 * 60 * 60; -const uint32_t DEEPSLEEP_MAX_CYCLE = 60 * 60; -const uint32_t DEEPSLEEP_MIN_TIME = 5; -const uint32_t DEEPSLEEP_START_COUNTDOWN = 4; - -const char kDeepsleepCommands[] PROGMEM = D_PRFX_DEEPSLEEP "|" - D_CMND_DEEPSLEEP_TIME ; - -void (* const DeepsleepCommand[])(void) PROGMEM = { - &CmndDeepsleepTime }; - -uint32_t deepsleep_sleeptime = 0; -uint8_t deepsleep_flag = 0; - -bool DeepSleepEnabled(void) -{ - if ((Settings.deepsleep < 10) || (Settings.deepsleep > DEEPSLEEP_MAX)) { - Settings.deepsleep = 0; - return false; - } - - if (pin[GPIO_DEEPSLEEP] < 99) { - pinMode(pin[GPIO_DEEPSLEEP], INPUT_PULLUP); - return (digitalRead(pin[GPIO_DEEPSLEEP])); - } - - return true; -} - -void DeepSleepReInit(void) -{ - if ((ResetReason() == REASON_DEEP_SLEEP_AWAKE) && DeepSleepEnabled()) { - if ((RtcSettings.ultradeepsleep > DEEPSLEEP_MAX_CYCLE) && (RtcSettings.ultradeepsleep < 1700000000)) { - - RtcSettings.ultradeepsleep = RtcSettings.ultradeepsleep - DEEPSLEEP_MAX_CYCLE; - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Remain DeepSleep %d"), RtcSettings.ultradeepsleep); - RtcSettingsSave(); - RtcRebootReset(); - ESP.deepSleep(100 * RtcSettings.deepsleep_slip * (DEEPSLEEP_MAX_CYCLE < RtcSettings.ultradeepsleep ? DEEPSLEEP_MAX_CYCLE : RtcSettings.ultradeepsleep), WAKE_RF_DEFAULT); - yield(); - - } - } - - RtcSettings.ultradeepsleep = 0; -} - -void DeepSleepPrepare(void) -{ - - - - - if ((RtcSettings.nextwakeup == 0) || - (RtcSettings.deepsleep_slip < 9000) || - (RtcSettings.deepsleep_slip > 11000) || - (RtcSettings.nextwakeup > (UtcTime() + Settings.deepsleep))) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR("DSL: Reset wrong settings wakeup: %ld, slip %ld"), RtcSettings.nextwakeup, RtcSettings.deepsleep_slip ); - RtcSettings.nextwakeup = 0; - RtcSettings.deepsleep_slip = 10000; - } - - - - int16_t timeslip = (int16_t)(RtcSettings.nextwakeup + millis() / 1000 - UtcTime()) * 10; - - - - timeslip = (timeslip < -(int32_t)Settings.deepsleep) ? 0 : (timeslip > (int32_t)Settings.deepsleep) ? 0 : 1; - if (timeslip) { - RtcSettings.deepsleep_slip = (Settings.deepsleep + RtcSettings.nextwakeup - UtcTime()) * RtcSettings.deepsleep_slip / tmax((Settings.deepsleep - (millis() / 1000)),5); - - RtcSettings.deepsleep_slip = tmin(tmax(RtcSettings.deepsleep_slip, 9000), 11000); - RtcSettings.nextwakeup += Settings.deepsleep; - } - - - - if (RtcSettings.nextwakeup <= (UtcTime() - DEEPSLEEP_MIN_TIME)) { - - RtcSettings.nextwakeup += (((UtcTime() + DEEPSLEEP_MIN_TIME - RtcSettings.nextwakeup) / Settings.deepsleep) + 1) * Settings.deepsleep; - } - - String dt = GetDT(RtcSettings.nextwakeup + LocalTime() - UtcTime()); - - - deepsleep_sleeptime = tmin((uint32_t)DEEPSLEEP_MAX_CYCLE ,RtcSettings.nextwakeup - UtcTime()); - - - Response_P(PSTR("{\"" D_PRFX_DEEPSLEEP "\":{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%d}}"), (char*)dt.c_str(), RtcSettings.nextwakeup); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_STATUS)); - - - -} - -void DeepSleepStart(void) -{ - AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Sleeping")); - - WifiShutdown(); - RtcSettings.ultradeepsleep = RtcSettings.nextwakeup - UtcTime(); - RtcSettingsSave(); - - ESP.deepSleep(100 * RtcSettings.deepsleep_slip * deepsleep_sleeptime); - yield(); -} - -void DeepSleepEverySecond(void) -{ - if (!deepsleep_flag) { return; } - - if (DeepSleepEnabled()) { - if (DEEPSLEEP_START_COUNTDOWN == deepsleep_flag) { - SettingsSaveAll(); - DeepSleepPrepare(); - } - deepsleep_flag--; - if (deepsleep_flag <= 0) { - DeepSleepStart(); - } - } else { - deepsleep_flag = 0; - } -} - - - - - -void CmndDeepsleepTime(void) -{ - if ((0 == XdrvMailbox.payload) || - ((XdrvMailbox.payload > 10) && (XdrvMailbox.payload < DEEPSLEEP_MAX))) { - Settings.deepsleep = XdrvMailbox.payload; - RtcSettings.nextwakeup = 0; - deepsleep_flag = (0 == XdrvMailbox.payload) ? 0 : DEEPSLEEP_START_COUNTDOWN; - if (deepsleep_flag) { - if (!Settings.tele_period) { - Settings.tele_period = TELE_PERIOD; - } - } - } - Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, Settings.deepsleep); -} - - - - - -bool Xdrv29(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_SECOND: - DeepSleepEverySecond(); - break; - case FUNC_AFTER_TELEPERIOD: - if (DeepSleepEnabled() && !deepsleep_flag && (Settings.tele_period == 10 || Settings.tele_period == 300 || UpTime() > Settings.tele_period)) { - deepsleep_flag = DEEPSLEEP_START_COUNTDOWN; - } - break; - case FUNC_COMMAND: - result = DecodeCommand(kDeepsleepCommands, DeepsleepCommand); - break; - case FUNC_PRE_INIT: - DeepSleepReInit(); - break; - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" -#ifdef USE_LIGHT -#ifdef USE_EXS_DIMMER - - - - - - - -#define XDRV_30 30 - -#define EXS_GATE_1_ON 0x20 -#define EXS_GATE_1_OFF 0x21 -#define EXS_DIMM_1_ON 0x22 -#define EXS_DIMM_1_OFF 0x23 -#define EXS_DIMM_1_TBL 0x24 -#define EXS_DIMM_1_VAL 0x25 -#define EXS_GATE_2_ON 0x30 -#define EXS_GATE_2_OFF 0x31 -#define EXS_DIMM_2_ON 0x32 -#define EXS_DIMM_2_OFF 0x33 -#define EXS_DIMM_2_TBL 0x34 -#define EXS_DIMM_2_VAL 0x35 -#define EXS_GATES_ON 0x40 -#define EXS_GATES_OFF 0x41 -#define EXS_DIMMS_ON 0x50 -#define EXS_DIMMS_OFF 0x51 -#define EXS_CH_LOCK 0x60 -#define EXS_GET_VALUES 0xFA -#define EXS_WRITE_EE 0xFC -#define EXS_READ_EE 0xFD -#define EXS_GET_VERSION 0xFE -#define EXS_RESET 0xFF - -#define EXS_BUFFER_SIZE 256 -#define EXS_ACK_TIMEOUT 200 - -#include - -TasmotaSerial *ExsSerial = nullptr; - -typedef struct -{ - uint8_t on = 0; - uint8_t bright_tbl = 0; - uint8_t dimm = 0; - uint8_t impuls_start = 0; - uint32_t impuls_len = 0; -} CHANNEL; - -typedef struct -{ - uint8_t version_major = 0; - uint8_t version_minor = 0; - CHANNEL channel[2]; - uint8_t gate_lock = 0; -} DIMMER; - -struct EXS -{ - uint8_t *buffer = nullptr; - int byte_counter = 0; - int cmd_status = 0; - uint8_t power = 0; - uint8_t dimm[2] = {0, 0}; - DIMMER dimmer; -} Exs; - - - - - -uint8_t crc8(const uint8_t *p, uint8_t len) -{ - const uint8_t table[] = { - 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, - 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D}; - - const uint8_t table_rev[] = { - 0x00, 0x70, 0xE0, 0x90, 0xC1, 0xB1, 0x21, 0x51, - 0x83, 0xF3, 0x63, 0x13, 0x42, 0x32, 0xA2, 0xD2}; - - uint8_t offset; - uint8_t temp, crc8_temp; - uint8_t crc8 = 0; - - for (int i = 0; i < len; i++) - { - temp = *(p + i); - offset = temp ^ crc8; - offset >>= 4; - crc8_temp = crc8 & 0x0f; - crc8 = crc8_temp ^ table_rev[offset]; - offset = crc8 ^ temp; - offset &= 0x0f; - crc8_temp = crc8 & 0xf0; - crc8 = crc8_temp ^ table[offset]; - } - return crc8 ^ 0x55; -} - -void ExsSerialSend(const uint8_t data[] = nullptr, uint16_t len = 0) -{ - int retries = 3; - char rc; - -#ifdef EXS_DEBUG - snprintf_P(log_data, sizeof(log_data), PSTR("EXS: Tx Packet: \"")); - for (uint32_t i = 0; i < len; i++) - { - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, data[i]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); - AddLog(LOG_LEVEL_DEBUG_MORE); -#endif - - while (retries) - { - retries--; - - ExsSerial->write(data, len); - ExsSerial->flush(); - - - uint32_t snd_time = millis(); - while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) && - (!ExsSerial->available())) - ; - - if (!ExsSerial->available()) - { - -#ifdef EXS_DEBUG - AddLog_P(LOG_LEVEL_DEBUG, PSTR("ESX: serial send timeout")); -#endif - continue; - } - - rc = ExsSerial->read(); - if (rc == 0xFF) - break; - } -} - -void ExsSendCmd(uint8_t cmd, uint8_t value) -{ - uint8_t buffer[8]; - uint16_t len; - - buffer[0] = 0x7b; - buffer[3] = cmd; - - switch (cmd) - { - case EXS_GATE_1_ON: - case EXS_GATE_1_OFF: - case EXS_DIMM_1_ON: - case EXS_DIMM_1_OFF: - case EXS_GATE_2_ON: - case EXS_GATE_2_OFF: - case EXS_DIMM_2_ON: - case EXS_DIMM_2_OFF: - case EXS_GATES_ON: - case EXS_GATES_OFF: - case EXS_DIMMS_ON: - case EXS_DIMMS_OFF: - case EXS_GET_VALUES: - case EXS_GET_VERSION: - case EXS_RESET: - buffer[2] = 1; - len = 4; - break; - - case EXS_CH_LOCK: - case EXS_DIMM_1_TBL: - case EXS_DIMM_1_VAL: - case EXS_DIMM_2_TBL: - case EXS_DIMM_2_VAL: - buffer[2] = 2; - buffer[4] = value; - len = 5; - break; - } - buffer[1] = crc8(&buffer[3], buffer[2]); - - ExsSerialSend(buffer, len); -} - -uint8_t ExsSetPower(uint8_t device, uint8_t power) -{ - Exs.dimmer.channel[device].dimm = power; - ExsSendCmd(EXS_DIMM_1_ON + 0x10 * device + power ^ 1, 0); -} - -uint8_t ExsSetBri(uint8_t device, uint8_t bri) -{ - Exs.dimmer.channel[device].bright_tbl = bri; - ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * device, bri); -} - -uint8_t ExsSyncState(uint8_t device) -{ -#ifdef EXS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Channel %d Power Want %d, Is %d"), - device, bitRead(Exs.power, device), Exs.dimmer.channel[device].dimm); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Set Channel %d Brightness Want %d, Is %d"), - device, Exs.dimm[device], Exs.dimmer.channel[device].bright_tbl); -#endif - - if (bitRead(Exs.power, device) && - Exs.dimm[device] != Exs.dimmer.channel[device].bright_tbl) { - ExsSetBri(device, Exs.dimm[device]); - } - - if (!Exs.dimm[device]) { - Exs.dimmer.channel[device].dimm = 0; - } else if (Exs.dimmer.channel[device].dimm != bitRead(Exs.power, device)) { - ExsSetPower(device, bitRead(Exs.power, device)); - } -} - -bool ExsSyncState() -{ -#ifdef EXS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Serial %p, Cmd %d"), ExsSerial, Exs.cmd_status); -#endif - - if (!ExsSerial || Exs.cmd_status != 0) - return false; - - ExsSyncState(0); - ExsSyncState(1); -} - -void ExsDebugState() -{ -#ifdef EXS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: MCU v%d.%d, c0: On:%d,Dim:%d,Tbl:%d(%d%%), c1: On:%d,Dim:%d,Tbl:%d(%d%%), ChLock: %d"), - Exs.dimmer.version_major, Exs.dimmer.version_minor, - Exs.dimmer.channel[0].on, Exs.dimmer.channel[0].dimm, - Exs.dimmer.channel[0].bright_tbl, - changeUIntScale(Exs.dimmer.channel[0].bright_tbl, 0, 255, 0, 100), - Exs.dimmer.channel[1].on, Exs.dimmer.channel[1].dimm, - Exs.dimmer.channel[1].bright_tbl, - changeUIntScale(Exs.dimmer.channel[1].bright_tbl, 0, 255, 0, 100), - Exs.dimmer.gate_lock); -#endif -} - -void ExsPacketProcess(void) -{ - uint8_t len = Exs.buffer[1]; - uint8_t cmd = Exs.buffer[2]; - - switch (cmd) - { - case EXS_GET_VALUES: -# 294 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" - if (len > 9) - { - Exs.dimmer.version_major = Exs.buffer[3]; - Exs.dimmer.version_minor = Exs.buffer[4]; - - - Exs.dimmer.channel[0].on = Exs.buffer[6]; - Exs.dimmer.channel[0].dimm = Exs.buffer[6]; - Exs.dimmer.channel[0].bright_tbl = Exs.buffer[7]; - - - Exs.dimmer.channel[1].on = Exs.buffer[9]; - Exs.dimmer.channel[1].dimm = Exs.buffer[9]; - Exs.dimmer.channel[1].bright_tbl = Exs.buffer[10]; - - Exs.dimmer.gate_lock = Exs.buffer[11]; - } - else -# 327 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_30_exs_dimmer.ino" - { - Exs.dimmer.version_major = 1; - Exs.dimmer.version_minor = 0; - - - Exs.dimmer.channel[0].on = Exs.buffer[4] - 48; - Exs.dimmer.channel[0].dimm = Exs.buffer[4] - 48; - Exs.dimmer.channel[0].bright_tbl = Exs.buffer[5] - 48; - - - Exs.dimmer.channel[1].on = Exs.buffer[7] - 48; - Exs.dimmer.channel[1].dimm = Exs.buffer[7] - 48; - Exs.dimmer.channel[1].bright_tbl = Exs.buffer[8] - 48; - - Exs.dimmer.gate_lock = Exs.buffer[9] - 48; - } - - ExsDebugState(); - ExsSyncState(); - ExsDebugState(); - break; - default: - break; - } -} - - - -bool ExsModuleSelected(void) -{ - Settings.light_correction = 0; - Settings.flag.mqtt_serial = 0; - Settings.flag3.pwm_multi_channels = 1; - SetSeriallog(LOG_LEVEL_NONE); - - devices_present = +2; - light_type = LT_SERIAL2; - return true; -} - -bool ExsSetChannels(void) -{ -#ifdef EXS_DEBUG - snprintf_P(log_data, sizeof(log_data), PSTR("EXS: SetChannels: \"")); - for (int i = 0; i < XdrvMailbox.data_len; i++) - { - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, ((uint8_t *)XdrvMailbox.data)[i]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); - AddLog(LOG_LEVEL_DEBUG_MORE); -#endif - - Exs.dimm[0] = ((uint8_t *)XdrvMailbox.data)[0]; - Exs.dimm[1] = ((uint8_t *)XdrvMailbox.data)[1]; - return ExsSyncState(); -} - -bool ExsSetPower(void) -{ - AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Set Power, Device %d, Power 0x%02x"), - active_device, XdrvMailbox.index); - - Exs.power = XdrvMailbox.index; - return ExsSyncState(); -} - -void EsxMcuStart(void) -{ - int retries = 3; - -#ifdef EXS_DEBUG - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Request MCU configuration, PIN %d to Low"), pin[GPIO_EXS_ENABLE]); -#endif - - pinMode(pin[GPIO_EXS_ENABLE], OUTPUT); - digitalWrite(pin[GPIO_EXS_ENABLE], LOW); - - delay(1); - - while (ExsSerial->available()) - { - - ExsSerial->read(); - } -} - -void ExsInit(void) -{ -#ifdef EXS_DEBUG - AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Starting Tx %d Rx %d"), pin[GPIO_TXD], pin[GPIO_RXD]); -#endif - - Exs.buffer = (uint8_t *)malloc(EXS_BUFFER_SIZE); - if (Exs.buffer != nullptr) - { - ExsSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2); - if (ExsSerial->begin(9600)) - { - if (ExsSerial->hardwareSerial()) - { - ClaimSerial(); - } - ExsSerial->flush(); - EsxMcuStart(); - ExsSendCmd(EXS_CH_LOCK, 0); - ExsSendCmd(EXS_GET_VALUES, 0); - } - } -} - -void ExsSerialInput(void) -{ - while (ExsSerial->available()) - { - yield(); - uint8_t serial_in_byte = ExsSerial->read(); - - AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Serial In Byte 0x%02x"), serial_in_byte); - - if (Exs.cmd_status == 0 && - serial_in_byte == 0x7B) - { - Exs.cmd_status = 1; - Exs.byte_counter = 0; - } - else if (Exs.byte_counter >= EXS_BUFFER_SIZE) - { - Exs.cmd_status = 0; - } - else if (Exs.cmd_status == 1) - { - Exs.buffer[Exs.byte_counter++] = serial_in_byte; - - if (Exs.byte_counter > 2 && Exs.byte_counter == Exs.buffer[1] + 2) - { - uint8_t crc = crc8(&Exs.buffer[2], Exs.buffer[1]); - - - Exs.cmd_status = 0; - -#ifdef EXS_DEBUG - snprintf_P(log_data, sizeof(log_data), PSTR("EXS: RX Packet: \"")); - for (uint32_t i = 0; i < Exs.byte_counter; i++) - { - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, Exs.buffer[i]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s\", CRC: 0x%02x"), log_data, crc); - AddLog(LOG_LEVEL_DEBUG_MORE); -#endif - - if (Exs.buffer[0] == crc) - { - ExsSerial->write(0xFF); - ExsPacketProcess(); - } - else - { - ExsSerial->write(0x00); - } - - } - } - } -} - - - - - -#ifdef EXS_MCU_CMNDS - -#define D_PRFX_EXS "Exs" -#define D_CMND_EXS_DIMM "Dimm" -#define D_CMND_EXS_DIMM_TBL "DimmTbl" -#define D_CMND_EXS_DIMM_VAL "DimmVal" -#define D_CMND_EXS_DIMMS "Dimms" -#define D_CMND_EXS_CH_LOCK "ChLock" -#define D_CMND_EXS_STATE "State" - -const char kExsCommands[] PROGMEM = D_PRFX_EXS "|" - D_CMND_EXS_DIMM "|" D_CMND_EXS_DIMM_TBL "|" D_CMND_EXS_DIMM_VAL "|" - D_CMND_EXS_DIMMS "|" D_CMND_EXS_CH_LOCK "|" - D_CMND_EXS_STATE; - -void (* const ExsCommand[])(void) PROGMEM = { - &CmndExsDimm, &CmndExsDimmTbl, &CmndExsDimmVal, - &CmndExsDimms, &CmndExsChLock, - &CmndExsState }; - -void CmndExsDimm(void) -{ - if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && - (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1)) { - ExsSendCmd(EXS_DIMM_1_ON + 0x10 * (XdrvMailbox.index - 1) + - XdrvMailbox.payload ^ 1, 0); - } - CmndExsState(); -} - -void CmndExsDimmTbl(void) -{ - if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && - (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) { - ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * (XdrvMailbox.index - 1), - XdrvMailbox.payload); - } - CmndExsState(); -} - -void CmndExsDimmVal(void) -{ - if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) && - (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) { - ExsSendCmd(EXS_DIMM_1_VAL + 0x10 * (XdrvMailbox.index - 1), - XdrvMailbox.payload); - } - CmndExsState(); -} - -void CmndExsDimms(void) -{ - if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) { - ExsSendCmd(EXS_DIMMS_ON + XdrvMailbox.payload ^ 1, 0); - } - CmndExsState(); -} - -void CmndExsChLock(void) -{ - if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) { - ExsSendCmd(EXS_CH_LOCK, XdrvMailbox.payload); - } - CmndExsState(); -} - -void CmndExsState(void) -{ - ExsSendCmd(EXS_GET_VALUES, 0); - - - uint32_t snd_time = millis(); - while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) && - (!ExsSerial->available())) - ; - ExsSerialInput(); - - Response_P(PSTR("{\"" D_CMND_EXS_STATE "\":{")); - ResponseAppend_P(PSTR("\"McuVersion\":\"%d.%d\"," - "\"Channels\":["), - Exs.dimmer.version_major, Exs.dimmer.version_minor); - - for (uint32_t i = 0; i < 2; i++) { - if (i != 0) { - ResponseAppend_P(PSTR(",")); - } - ResponseAppend_P(PSTR("{\"On\":\"%d\"," - "\"BrightProz\":\"%d\"," - "\"BrightTab\":\"%d\"," - "\"Dimm\":\"%d\"}"), - Exs.dimmer.channel[i].on, - changeUIntScale(Exs.dimmer.channel[i].bright_tbl, 0, 255, 0, 100), - Exs.dimmer.channel[i].bright_tbl, - Exs.dimmer.channel[i].dimm); - } - ResponseAppend_P(PSTR("],")); - ResponseAppend_P(PSTR("\"GateLock\":\"%d\""), Exs.dimmer.gate_lock); - ResponseJsonEndEnd(); -} - -#endif - - - - - -bool Xdrv30(uint8_t function) -{ - bool result = false; - - if (EXS_DIMMER == my_module_type) - { - switch (function) - { - case FUNC_LOOP: - if (ExsSerial) - ExsSerialInput(); - break; - case FUNC_MODULE_INIT: - result = ExsModuleSelected(); - break; - case FUNC_INIT: - ExsInit(); - break; - case FUNC_SET_DEVICE_POWER: - result = ExsSetPower(); - break; - case FUNC_SET_CHANNELS: - result = ExsSetChannels(); - break; -#ifdef EXS_MCU_CMNDS - case FUNC_COMMAND: - result = DecodeCommand(kExsCommands, ExsCommand); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_31_tasmota_slave.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_31_tasmota_slave.ino" -#ifdef USE_TASMOTA_SLAVE - - - - -#define XDRV_31 31 - -#define CONST_STK_CRC_EOP 0x20 - -#define CMND_STK_GET_SYNC 0x30 -#define CMND_STK_SET_DEVICE 0x42 -#define CMND_STK_SET_DEVICE_EXT 0x45 -#define CMND_STK_ENTER_PROGMODE 0x50 -#define CMND_STK_LEAVE_PROGMODE 0x51 -#define CMND_STK_LOAD_ADDRESS 0x55 -#define CMND_STK_PROG_PAGE 0x64 - - - - - -#define CMND_START 0xFC -#define CMND_END 0xFD - -#define CMND_FEATURES 0x01 -#define CMND_JSON 0x02 -#define CMND_FUNC_EVERY_SECOND 0x03 -#define CMND_FUNC_EVERY_100_MSECOND 0x04 -#define CMND_SLAVE_SEND 0x05 -#define CMND_PUBLISH_TELE 0x06 -#define CMND_EXECUTE_CMND 0x07 - -#define PARAM_DATA_START 0xFE -#define PARAM_DATA_END 0xFF - -#include - - - - - -class SimpleHexParse { - public: - SimpleHexParse(void); - uint8_t parseLine(char *hexline); - uint8_t ptr_l = 0; - uint8_t ptr_h = 0; - bool PageIsReady = false; - bool firstrun = true; - bool EndOfFile = false; - uint8_t FlashPage[128]; - uint8_t FlashPageIdx = 0; - uint8_t layoverBuffer[16]; - uint8_t layoverIdx = 0; - uint8_t getByte(char *hexline, uint8_t idx); -}; - -SimpleHexParse::SimpleHexParse(void) -{ - -} - -uint8_t SimpleHexParse::parseLine(char *hexline) -{ - if (layoverIdx) { - memcpy(&FlashPage[0], &layoverBuffer[0], layoverIdx); - FlashPageIdx = layoverIdx; - layoverIdx = 0; - } - uint8_t len = getByte(hexline, 1); - uint8_t addr_h = getByte(hexline, 2); - uint8_t addr_l = getByte(hexline, 3); - uint8_t rectype = getByte(hexline, 4); - for (uint8_t idx = 0; idx < len; idx++) { - if (FlashPageIdx < 128) { - FlashPage[FlashPageIdx] = getByte(hexline, idx+5); - FlashPageIdx++; - } else { - layoverBuffer[layoverIdx] = getByte(hexline, idx+5); - layoverIdx++; - } - } - if (1 == rectype) { - EndOfFile = true; - while (FlashPageIdx < 128) { - FlashPage[FlashPageIdx] = 0xFF; - FlashPageIdx++; - } - } - if (FlashPageIdx == 128) { - if (firstrun) { - firstrun = false; - } else { - ptr_l += 0x40; - if (ptr_l == 0) { - ptr_l = 0; - ptr_h++; - } - } - firstrun = false; - PageIsReady = true; - } - return 0; -} - -uint8_t SimpleHexParse::getByte(char* hexline, uint8_t idx) -{ - char buff[3]; - buff[3] = '\0'; - memcpy(&buff, &hexline[(idx*2)-1], 2); - return strtol(buff, 0, 16); -} - - - - - -struct TSLAVE { - uint32_t spi_hex_size = 0; - uint32_t spi_sector_counter = 0; - uint8_t spi_sector_cursor = 0; - uint8_t inverted = LOW; - bool type = false; - bool flashing = false; - bool SerialEnabled = false; - uint8_t waitstate = 0; - bool unsupported = false; -} TSlave; - -typedef union { - uint32_t data; - struct { - uint32_t func_json_append : 1; - uint32_t func_every_second : 1; - uint32_t func_every_100_msecond : 1; - uint32_t func_slave_send : 1; - uint32_t spare4 : 1; - uint32_t spare5 : 1; - uint32_t spare6 : 1; - uint32_t spare7 : 1; - uint32_t spare8 : 1; - uint32_t spare9 : 1; - uint32_t spare10 : 1; - uint32_t spare11 : 1; - uint32_t spare12 : 1; - uint32_t spare13 : 1; - uint32_t spare14 : 1; - uint32_t spare15 : 1; - uint32_t spare16 : 1; - uint32_t spare17 : 1; - uint32_t spare18 : 1; - uint32_t spare19 : 1; - uint32_t spare20 : 1; - uint32_t spare21 : 1; - uint32_t spare22 : 1; - uint32_t spare23 : 1; - uint32_t spare24 : 1; - uint32_t spare25 : 1; - uint32_t spare26 : 1; - uint32_t spare27 : 1; - uint32_t spare28 : 1; - uint32_t spare29 : 1; - uint32_t spare30 : 1; - uint32_t spare31 : 1; - }; -} TSlaveFeatureCfg; - - - - - - -struct TSLAVE_FEATURES { - uint32_t features_version; - TSlaveFeatureCfg features; -} TSlaveSettings; - -struct TSLAVE_COMMAND { - uint8_t command; - uint8_t parameter; - uint8_t unused2; - uint8_t unused3; -} TSlaveCommand; - -TasmotaSerial *TasmotaSlave_Serial; - -uint32_t TasmotaSlave_FlashStart(void) -{ - return (ESP.getSketchSize() / SPI_FLASH_SEC_SIZE) + 2; -} - -uint8_t TasmotaSlave_UpdateInit(void) -{ - TSlave.spi_hex_size = 0; - TSlave.spi_sector_counter = TasmotaSlave_FlashStart(); - TSlave.spi_sector_cursor = 0; - return 0; -} - -void TasmotaSlave_Reset(void) -{ - if (TSlave.SerialEnabled) { - digitalWrite(pin[GPIO_TASMOTASLAVE_RST], !TSlave.inverted); - delay(1); - digitalWrite(pin[GPIO_TASMOTASLAVE_RST], TSlave.inverted); - delay(1); - digitalWrite(pin[GPIO_TASMOTASLAVE_RST], !TSlave.inverted); - delay(5); - } -} - -uint8_t TasmotaSlave_waitForSerialData(int dataCount, int timeout) -{ - int timer = 0; - while (timer < timeout) { - if (TasmotaSlave_Serial->available() >= dataCount) { - return 1; - } - delay(1); - timer++; - } - return 0; -} - -uint8_t TasmotaSlave_sendBytes(uint8_t* bytes, int count) -{ - TasmotaSlave_Serial->write(bytes, count); - TasmotaSlave_waitForSerialData(2, 250); - uint8_t sync = TasmotaSlave_Serial->read(); - uint8_t ok = TasmotaSlave_Serial->read(); - if ((sync == 0x14) && (ok == 0x10)) { - return 1; - } - return 0; -} - -uint8_t TasmotaSlave_execCmd(uint8_t cmd) -{ - uint8_t bytes[] = { cmd, CONST_STK_CRC_EOP }; - return TasmotaSlave_sendBytes(bytes, 2); -} - -uint8_t TasmotaSlave_execParam(uint8_t cmd, uint8_t* params, int count) -{ - uint8_t bytes[32]; - bytes[0] = cmd; - int i = 0; - while (i < count) { - bytes[i + 1] = params[i]; - i++; - } - bytes[i + 1] = CONST_STK_CRC_EOP; - return TasmotaSlave_sendBytes(bytes, i + 2); -} - -uint8_t TasmotaSlave_exitProgMode(void) -{ - return TasmotaSlave_execCmd(CMND_STK_LEAVE_PROGMODE); -} - -uint8_t TasmotaSlave_SetupFlash(void) -{ - uint8_t ProgParams[] = {0x86, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00}; - uint8_t ExtProgParams[] = {0x05, 0x04, 0xd7, 0xc2, 0x00}; - TasmotaSlave_Serial->begin(USE_TASMOTA_SLAVE_FLASH_SPEED); - if (TasmotaSlave_Serial->hardwareSerial()) { - ClaimSerial(); - } - - TasmotaSlave_Reset(); - - uint8_t timeout = 0; - uint8_t no_error = 0; - while (50 > timeout) { - if (TasmotaSlave_execCmd(CMND_STK_GET_SYNC)) { - timeout = 200; - no_error = 1; - } - timeout++; - delay(1); - } - if (no_error) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Found bootloader")); - } else { - no_error = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Bootloader could not be found")); - } - if (no_error) { - if (TasmotaSlave_execParam(CMND_STK_SET_DEVICE, ProgParams, sizeof(ProgParams))) { - } else { - no_error = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Could not configure device for programming (1)")); - } - } - if (no_error) { - if (TasmotaSlave_execParam(CMND_STK_SET_DEVICE_EXT, ExtProgParams, sizeof(ExtProgParams))) { - } else { - no_error = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Could not configure device for programming (2)")); - } - } - if (no_error) { - if (TasmotaSlave_execCmd(CMND_STK_ENTER_PROGMODE)) { - } else { - no_error = 0; - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Failed to put bootloader into programming mode")); - } - } - return no_error; -} - -uint8_t TasmotaSlave_loadAddress(uint8_t adrHi, uint8_t adrLo) -{ - uint8_t params[] = { adrLo, adrHi }; - return TasmotaSlave_execParam(CMND_STK_LOAD_ADDRESS, params, sizeof(params)); -} - -void TasmotaSlave_FlashPage(uint8_t addr_h, uint8_t addr_l, uint8_t* data) -{ - uint8_t Header[] = {CMND_STK_PROG_PAGE, 0x00, 0x80, 0x46}; - TasmotaSlave_loadAddress(addr_h, addr_l); - TasmotaSlave_Serial->write(Header, 4); - for (int i = 0; i < 128; i++) { - TasmotaSlave_Serial->write(data[i]); - } - TasmotaSlave_Serial->write(CONST_STK_CRC_EOP); - TasmotaSlave_waitForSerialData(2, 250); - TasmotaSlave_Serial->read(); - TasmotaSlave_Serial->read(); -} - -void TasmotaSlave_Flash(void) -{ - bool reading = true; - uint32_t read = 0; - uint32_t processed = 0; - char thishexline[50]; - uint8_t position = 0; - char* flash_buffer; - - SimpleHexParse hexParse = SimpleHexParse(); - - if (!TasmotaSlave_SetupFlash()) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Flashing aborted!")); - TSlave.flashing = false; - restart_flag = 2; - return; - } - - flash_buffer = new char[SPI_FLASH_SEC_SIZE]; - uint32_t flash_start = TasmotaSlave_FlashStart() * SPI_FLASH_SEC_SIZE; - while (reading) { - ESP.flashRead(flash_start + read, (uint32_t*)flash_buffer, SPI_FLASH_SEC_SIZE); - read = read + SPI_FLASH_SEC_SIZE; - if (read >= TSlave.spi_hex_size) { - reading = false; - } - for (uint32_t ca = 0; ca < SPI_FLASH_SEC_SIZE; ca++) { - processed++; - if ((processed <= TSlave.spi_hex_size) && (!hexParse.EndOfFile)) { - if (':' == flash_buffer[ca]) { - position = 0; - } - if (0x0D == flash_buffer[ca]) { - thishexline[position] = 0; - hexParse.parseLine(thishexline); - if (hexParse.PageIsReady) { - TasmotaSlave_FlashPage(hexParse.ptr_h, hexParse.ptr_l, hexParse.FlashPage); - hexParse.PageIsReady = false; - hexParse.FlashPageIdx = 0; - } - } else { - if (0x0A != flash_buffer[ca]) { - thishexline[position] = flash_buffer[ca]; - position++; - } - } - } - } - } - TasmotaSlave_exitProgMode(); - AddLog_P2(LOG_LEVEL_INFO, PSTR("TasmotaSlave: Flash done!")); - TSlave.flashing = false; - restart_flag = 2; -} - -void TasmotaSlave_SetFlagFlashing(bool value) -{ - TSlave.flashing = value; -} - -bool TasmotaSlave_GetFlagFlashing(void) -{ - return TSlave.flashing; -} - -void TasmotaSlave_WriteBuffer(uint8_t *buf, size_t size) -{ - if (0 == TSlave.spi_sector_cursor) { - ESP.flashEraseSector(TSlave.spi_sector_counter); - } - TSlave.spi_sector_cursor++; - ESP.flashWrite((TSlave.spi_sector_counter * SPI_FLASH_SEC_SIZE) + ((TSlave.spi_sector_cursor-1)*2048), (uint32_t*)buf, size); - TSlave.spi_hex_size = TSlave.spi_hex_size + size; - if (2 == TSlave.spi_sector_cursor) { - TSlave.spi_sector_cursor = 0; - TSlave.spi_sector_counter++; - } -} - -void TasmotaSlave_Init(void) -{ - if (TSlave.type) { - return; - } - if (10 > TSlave.waitstate) { - TSlave.waitstate++; - return; - } - if (!TSlave.SerialEnabled) { - if ((pin[GPIO_TASMOTASLAVE_RXD] < 99) && (pin[GPIO_TASMOTASLAVE_TXD] < 99) && - ((pin[GPIO_TASMOTASLAVE_RST] < 99) || (pin[GPIO_TASMOTASLAVE_RST_INV] < 99))) { - TasmotaSlave_Serial = new TasmotaSerial(pin[GPIO_TASMOTASLAVE_RXD], pin[GPIO_TASMOTASLAVE_TXD], 1, 0, 200); - if (TasmotaSlave_Serial->begin(USE_TASMOTA_SLAVE_SERIAL_SPEED)) { - if (TasmotaSlave_Serial->hardwareSerial()) { - ClaimSerial(); - } - TasmotaSlave_Serial->setTimeout(50); - if (pin[GPIO_TASMOTASLAVE_RST_INV] < 99) { - pin[GPIO_TASMOTASLAVE_RST] = pin[GPIO_TASMOTASLAVE_RST_INV]; - pin[GPIO_TASMOTASLAVE_RST_INV] = 99; - TSlave.inverted = HIGH; - } - pinMode(pin[GPIO_TASMOTASLAVE_RST], OUTPUT); - TSlave.SerialEnabled = true; - TasmotaSlave_Reset(); - AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Enabled")); - } - } - } - if (TSlave.SerialEnabled) { - TasmotaSlave_sendCmnd(CMND_FEATURES, 0); - char buffer[32]; - TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_START), buffer, sizeof(buffer)); - uint8_t len = TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_END), buffer, sizeof(buffer)); - memcpy(&TSlaveSettings, &buffer, sizeof(TSlaveSettings)); - if (20191129 == TSlaveSettings.features_version) { - TSlave.type = true; - AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Version %u"), TSlaveSettings.features_version); - } else { - if ((!TSlave.unsupported) && (TSlaveSettings.features_version > 0)) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("Tasmota Slave Version %u not supported!"), TSlaveSettings.features_version); - TSlave.unsupported = true; - } - } - } -} - -void TasmotaSlave_Show(void) -{ - if ((TSlave.type) && (TSlaveSettings.features.func_json_append)) { - char buffer[100]; - TasmotaSlave_sendCmnd(CMND_JSON, 0); - TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_START), buffer, sizeof(buffer)-1); - uint8_t len = TasmotaSlave_Serial->readBytesUntil(char(PARAM_DATA_END), buffer, sizeof(buffer)-1); - buffer[len] = '\0'; - ResponseAppend_P(PSTR(",\"TasmotaSlave\":%s"), buffer); - } -} - -void TasmotaSlave_sendCmnd(uint8_t cmnd, uint8_t param) -{ - TSlaveCommand.command = cmnd; - TSlaveCommand.parameter = param; - char buffer[sizeof(TSlaveCommand)+2]; - buffer[0] = CMND_START; - memcpy(&buffer[1], &TSlaveCommand, sizeof(TSlaveCommand)); - buffer[sizeof(TSlaveCommand)+1] = CMND_END; - for (uint8_t ca = 0; ca < sizeof(buffer); ca++) { - TasmotaSlave_Serial->write(buffer[ca]); - } -} - -#define D_PRFX_SLAVE "Slave" -#define D_CMND_SLAVE_RESET "Reset" -#define D_CMND_SLAVE_SEND "Send" - -const char kTasmotaSlaveCommands[] PROGMEM = D_PRFX_SLAVE "|" - D_CMND_SLAVE_RESET "|" D_CMND_SLAVE_SEND; - -void (* const TasmotaSlaveCommand[])(void) PROGMEM = { - &CmndTasmotaSlaveReset, &CmndTasmotaSlaveSend }; - -void CmndTasmotaSlaveReset(void) -{ - TasmotaSlave_Reset(); - TSlave.type = false; - TSlave.waitstate = 7; - TSlave.unsupported = false; - ResponseCmndDone(); -} - -void CmndTasmotaSlaveSend(void) -{ - if (0 < XdrvMailbox.data_len) { - TasmotaSlave_sendCmnd(CMND_SLAVE_SEND, XdrvMailbox.data_len); - TasmotaSlave_Serial->write(char(PARAM_DATA_START)); - for (uint8_t idx = 0; idx < XdrvMailbox.data_len; idx++) { - TasmotaSlave_Serial->write(XdrvMailbox.data[idx]); - } - TasmotaSlave_Serial->write(char(PARAM_DATA_END)); - } - ResponseCmndDone(); -} - -void TasmotaSlave_ProcessIn(void) -{ - uint8_t cmnd = TasmotaSlave_Serial->read(); - switch (cmnd) { - case CMND_START: - TasmotaSlave_waitForSerialData(sizeof(TSlaveCommand),50); - uint8_t buffer[sizeof(TSlaveCommand)]; - for (uint8_t idx = 0; idx < sizeof(TSlaveCommand); idx++) { - buffer[idx] = TasmotaSlave_Serial->read(); - } - TasmotaSlave_Serial->read(); - memcpy(&TSlaveCommand, &buffer, sizeof(TSlaveCommand)); - char inbuf[TSlaveCommand.parameter+1]; - TasmotaSlave_waitForSerialData(TSlaveCommand.parameter, 50); - TasmotaSlave_Serial->read(); - for (uint8_t idx = 0; idx < TSlaveCommand.parameter; idx++) { - inbuf[idx] = TasmotaSlave_Serial->read(); - } - TasmotaSlave_Serial->read(); - inbuf[TSlaveCommand.parameter] = '\0'; - - if (CMND_PUBLISH_TELE == TSlaveCommand.command) { - Response_P(PSTR("{\"TasmotaSlave\":")); - ResponseAppend_P("%s", inbuf); - ResponseJsonEnd(); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); - XdrvRulesProcess(); - } - if (CMND_EXECUTE_CMND == TSlaveCommand.command) { - ExecuteCommand(inbuf, SRC_IGNORE); - } - break; - default: - break; - } -} - - - - - - -bool Xdrv31(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_100_MSECOND: - if (TSlave.type) { - if (TasmotaSlave_Serial->available()) { - TasmotaSlave_ProcessIn(); - } - if (TSlaveSettings.features.func_every_100_msecond) { - TasmotaSlave_sendCmnd(CMND_FUNC_EVERY_100_MSECOND, 0); - } - } - break; - case FUNC_EVERY_SECOND: - if ((TSlave.type) && (TSlaveSettings.features.func_every_second)) { - TasmotaSlave_sendCmnd(CMND_FUNC_EVERY_SECOND, 0); - } - TasmotaSlave_Init(); - break; - case FUNC_JSON_APPEND: - if ((TSlave.type) && (TSlaveSettings.features.func_json_append)) { - TasmotaSlave_Show(); - } - break; - case FUNC_COMMAND: - result = DecodeCommand(kTasmotaSlaveCommands, TasmotaSlaveCommand); - break; - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_32_hotplug.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_32_hotplug.ino" -#ifdef USE_HOTPLUG - - - - - - - -#define XDRV_32 32 - -const uint32_t HOTPLUG_MAX = 254; - -const char kHotPlugCommands[] PROGMEM = "|" - D_CMND_HOTPLUG; - -void (* const HotPlugCommand[])(void) PROGMEM = { - &CmndHotPlugTime }; - -struct { - - bool enabled = false; - uint8_t timeout = 0; -} Hotplug; - -void HotPlugInit(void) -{ - - if (Settings.hotplug_scan == 0xFF) { Settings.hotplug_scan = 0; } - if (Settings.hotplug_scan != 0) { - Hotplug.enabled = true; - Hotplug.timeout = 1; - } else - Hotplug.enabled = false; -} - -void HotPlugEverySecond(void) -{ - if (Hotplug.enabled) { - if (Hotplug.timeout == 0) { - XsnsCall(FUNC_HOTPLUG_SCAN); - Hotplug.timeout = Settings.hotplug_scan; - } - Hotplug.timeout--; - } -} - - - - - -void CmndHotPlugTime(void) -{ - if (XdrvMailbox.payload <= HOTPLUG_MAX) { - Settings.hotplug_scan = XdrvMailbox.payload; - HotPlugInit(); - } - ResponseCmndNumber(Settings.hotplug_scan); -} - - - - - -bool Xdrv32(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_SECOND: - HotPlugEverySecond(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kHotPlugCommands, HotPlugCommand); - break; - case FUNC_PRE_INIT: - HotPlugInit(); - break; - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_33_nrf24l01.ino" -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_33_nrf24l01.ino" -#ifdef USE_SPI -#ifdef USE_NRF24 - - - - - - - -#define XDRV_33 33 - -#define MOSI 13 -#define MISO 12 -#define SCK 14 - -#include -#include - -const char NRF24type[] PROGMEM = "NRF24"; - -const char HTTP_NRF24[] PROGMEM = - "{s}%sL01%c: " "{m}started{e}"; - -struct { - uint8_t chipType = 0; -} NRF24; - - - -RF24 NRF24radio; - -bool NRF24initRadio() -{ - NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]); - NRF24radio.powerUp(); - - if(NRF24radio.isChipConnected()){ - DEBUG_DRIVER_LOG(PSTR("NRF24 chip connected")); - return true; - } - DEBUG_DRIVER_LOG(PSTR("NRF24 chip NOT !!!! connected")); - return false; -} - -bool NRF24Detect(void) -{ - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_DC]<99)){ - SPI.pins(SCK,MOSI,MISO,-1); - if(NRF24initRadio()){ - NRF24.chipType = 32; - AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF24L01 initialized")); - if(NRF24radio.isPVariant()){ - NRF24.chipType = 43; - AddLog_P2(LOG_LEVEL_INFO,PSTR("NRF24L01+ detected")); - } - return true; - } - } - return false; -} - - - - - -bool Xdrv33(uint8_t function) -{ - bool result = false; - - if (FUNC_INIT == function) { - result = NRF24Detect(); - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_99_debug.ino" -# 22 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_99_debug.ino" -#ifdef DEBUG_THEO -#ifndef USE_DEBUG_DRIVER -#define USE_DEBUG_DRIVER -#endif -#endif - -#ifdef USE_DEBUG_DRIVER - - - - - - -#define XDRV_99 99 - -#ifndef CPU_LOAD_CHECK -#define CPU_LOAD_CHECK 1 -#endif - - - - - -#define D_CMND_CFGDUMP "CfgDump" -#define D_CMND_CFGPEEK "CfgPeek" -#define D_CMND_CFGPOKE "CfgPoke" -#define D_CMND_CFGSHOW "CfgShow" -#define D_CMND_CFGXOR "CfgXor" -#define D_CMND_CPUCHECK "CpuChk" -#define D_CMND_EXCEPTION "Exception" -#define D_CMND_FLASHDUMP "FlashDump" -#define D_CMND_FLASHMODE "FlashMode" -#define D_CMND_FREEMEM "FreeMem" -#define D_CMND_HELP "Help" -#define D_CMND_RTCDUMP "RtcDump" -#define D_CMND_SETSENSOR "SetSensor" -#define D_CMND_I2CWRITE "I2CWrite" -#define D_CMND_I2CREAD "I2CRead" -#define D_CMND_I2CSTRETCH "I2CStretch" -#define D_CMND_I2CCLOCK "I2CClock" - -const char kDebugCommands[] PROGMEM = "|" - D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" -#ifdef USE_WEBSERVER - D_CMND_CFGXOR "|" -#endif - D_CMND_CPUCHECK "|" -#ifdef DEBUG_THEO - D_CMND_EXCEPTION "|" -#endif - D_CMND_FLASHDUMP "|" D_CMND_FLASHMODE "|" D_CMND_FREEMEM"|" D_CMND_HELP "|" D_CMND_RTCDUMP "|" D_CMND_SETSENSOR "|" -#ifdef USE_I2C - D_CMND_I2CWRITE "|" D_CMND_I2CREAD "|" D_CMND_I2CSTRETCH "|" D_CMND_I2CCLOCK -#endif - ; - -void (* const DebugCommand[])(void) PROGMEM = { - &CmndCfgDump, &CmndCfgPeek, &CmndCfgPoke, -#ifdef USE_WEBSERVER - &CmndCfgXor, -#endif - &CmndCpuCheck, -#ifdef DEBUG_THEO - &CmndException, -#endif - &CmndFlashDump, &CmndFlashMode, &CmndFreemem, &CmndHelp, &CmndRtcDump, &CmndSetSensor, -#ifdef USE_I2C - &CmndI2cWrite, &CmndI2cRead, &CmndI2cStretch, &CmndI2cClock -#endif - }; - -uint32_t CPU_loops = 0; -uint32_t CPU_last_millis = 0; -uint32_t CPU_last_loop_time = 0; -uint8_t CPU_load_check = 0; -uint8_t CPU_show_freemem = 0; - - - -#ifdef DEBUG_THEO -void ExceptionTest(uint8_t type) -{ -# 145 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_99_debug.ino" - if (1 == type) { - char svalue[10]; - snprintf_P(svalue, sizeof(svalue), PSTR("%s"), 7); - } -# 159 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_99_debug.ino" - if (2 == type) { - while(1) delay(1000); - } -} - -#endif - - - -void CpuLoadLoop(void) -{ - CPU_last_loop_time = millis(); - if (CPU_load_check && CPU_last_millis) { - CPU_loops ++; - if ((CPU_last_millis + (CPU_load_check *1000)) <= CPU_last_loop_time) { -#if defined(F_CPU) && (F_CPU == 160000000L) - int CPU_load = 100 - ( (CPU_loops*(1 + 30*sleep)) / (CPU_load_check *800) ); - CPU_loops = CPU_loops / CPU_load_check; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(160MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); -#else - int CPU_load = 100 - ( (CPU_loops*(1 + 30*sleep)) / (CPU_load_check *400) ); - CPU_loops = CPU_loops / CPU_load_check; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, CPU %d%%(80MHz), Loops/sec %d"), ESP.getFreeHeap(), CPU_load, CPU_loops); -#endif - CPU_last_millis = CPU_last_loop_time; - CPU_loops = 0; - } - } -} - - - -#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_1) - - - -extern "C" { -#include - extern cont_t g_cont; -} - -void DebugFreeMem(void) -{ - register uint32_t *sp asm("a1"); - - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_cont.stack), XdrvMailbox.data); -} - -#else - - - - -extern "C" { -#include - extern cont_t* g_pcont; -} - -void DebugFreeMem(void) -{ - register uint32_t *sp asm("a1"); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "FreeRam %d, FreeStack %d (%s)"), ESP.getFreeHeap(), 4 * (sp - g_pcont->stack), XdrvMailbox.data); -} - -#endif - - - -void DebugRtcDump(char* parms) -{ - #define CFG_COLS 16 - - uint16_t idx; - uint16_t maxrow; - uint16_t row; - uint16_t col; - char *p; -# 246 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_99_debug.ino" - uint8_t buffer[768]; - - system_rtc_mem_read(0, (uint32_t*)&buffer, sizeof(buffer)); - - maxrow = ((sizeof(buffer)+CFG_COLS)/CFG_COLS); - - uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; - uint16_t mrow = strtol(p, &p, 10); - - - - if (0 == mrow) { - mrow = 8; - } - if (srow > maxrow) { - srow = maxrow - mrow; - } - if (mrow < (maxrow - srow)) { - maxrow = srow + mrow; - } - - for (row = srow; row < maxrow; row++) { - idx = row * CFG_COLS; - snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); - for (col = 0; col < CFG_COLS; col++) { - if (!(col%4)) { - snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); - for (col = 0; col < CFG_COLS; col++) { - - - - snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); - AddLog(LOG_LEVEL_INFO); - } -} - - - -void DebugCfgDump(char* parms) -{ - #define CFG_COLS 16 - - uint16_t idx; - uint16_t maxrow; - uint16_t row; - uint16_t col; - char *p; - - uint8_t *buffer = (uint8_t *) &Settings; - maxrow = ((sizeof(SYSCFG)+CFG_COLS)/CFG_COLS); - - uint16_t srow = strtol(parms, &p, 16) / CFG_COLS; - uint16_t mrow = strtol(p, &p, 10); - - - - if (0 == mrow) { - mrow = 8; - } - if (srow > maxrow) { - srow = maxrow - mrow; - } - if (mrow < (maxrow - srow)) { - maxrow = srow + mrow; - } - - for (row = srow; row < maxrow; row++) { - idx = row * CFG_COLS; - snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx); - for (col = 0; col < CFG_COLS; col++) { - if (!(col%4)) { - snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); - for (col = 0; col < CFG_COLS; col++) { - - - - snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' '); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data); - AddLog(LOG_LEVEL_INFO); - delay(1); - } -} - -void DebugCfgPeek(char* parms) -{ - char *p; - - uint16_t address = strtol(parms, &p, 16); - if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4; - address = (address >> 2) << 2; - - uint8_t *buffer = (uint8_t *) &Settings; - uint8_t data8 = buffer[address]; - uint16_t data16 = (buffer[address +1] << 8) + buffer[address]; - uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + data16; - - snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), address); - for (uint32_t i = 0; i < 4; i++) { - snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[address +i]); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data); - for (uint32_t i = 0; i < 4; i++) { - snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[address +i] > 0x20) && (buffer[address +i] < 0x7F)) ? (char)buffer[address +i] : ' '); - } - snprintf_P(log_data, sizeof(log_data), PSTR("%s| 0x%02X (%d), 0x%04X (%d), 0x%0LX (%lu)"), log_data, data8, data8, data16, data16, data32, data32); - AddLog(LOG_LEVEL_INFO); -} - -void DebugCfgPoke(char* parms) -{ - char *p; - - uint16_t address = strtol(parms, &p, 16); - if (address > sizeof(SYSCFG)) address = sizeof(SYSCFG) -4; - address = (address >> 2) << 2; - - uint32_t data = strtol(p, &p, 16); - - uint8_t *buffer = (uint8_t *) &Settings; - uint32_t data32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; - - uint8_t *nbuffer = (uint8_t *) &data; - for (uint32_t i = 0; i < 4; i++) { buffer[address +i] = nbuffer[+i]; } - - uint32_t ndata32 = (buffer[address +3] << 24) + (buffer[address +2] << 16) + (buffer[address +1] << 8) + buffer[address]; - - AddLog_P2(LOG_LEVEL_INFO, PSTR("%03X: 0x%0LX (%lu) poked to 0x%0LX (%lu)"), address, data32, data32, ndata32, ndata32); -} - -void SetFlashMode(uint8_t mode) -{ - uint8_t *_buffer; - uint32_t address; - - address = 0; - _buffer = new uint8_t[FLASH_SECTOR_SIZE]; - - if (ESP.flashRead(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE)) { - if (_buffer[2] != mode) { - _buffer[2] = mode; - if (ESP.flashEraseSector(address / FLASH_SECTOR_SIZE)) { - ESP.flashWrite(address, (uint32_t*)_buffer, FLASH_SECTOR_SIZE); - } - } - } - delete[] _buffer; -} - - - - - -void CmndHelp(void) -{ - AddLog_P(LOG_LEVEL_INFO, PSTR("HLP: "), kDebugCommands); - ResponseCmndDone(); -} - -void CmndRtcDump(void) -{ - DebugRtcDump(XdrvMailbox.data); - ResponseCmndDone(); -} - -void CmndCfgDump(void) -{ - DebugCfgDump(XdrvMailbox.data); - ResponseCmndDone(); -} - -void CmndCfgPeek(void) -{ - DebugCfgPeek(XdrvMailbox.data); - ResponseCmndDone(); -} - -void CmndCfgPoke(void) -{ - DebugCfgPoke(XdrvMailbox.data); - ResponseCmndDone(); -} - -#ifdef USE_WEBSERVER -void CmndCfgXor(void) -{ - if (XdrvMailbox.data_len > 0) { - Web.config_xor_on_set = XdrvMailbox.payload; - } - ResponseCmndNumber(Web.config_xor_on_set); -} -#endif - -#ifdef DEBUG_THEO -void CmndException(void) -{ - if (XdrvMailbox.data_len > 0) { ExceptionTest(XdrvMailbox.payload); } - ResponseCmndDone(); -} -#endif - -void CmndCpuCheck(void) -{ - if (XdrvMailbox.data_len > 0) { - CPU_load_check = XdrvMailbox.payload; - CPU_last_millis = CPU_last_loop_time; - } - ResponseCmndNumber(CPU_load_check); -} - -void CmndFreemem(void) -{ - if (XdrvMailbox.data_len > 0) { - CPU_show_freemem = XdrvMailbox.payload; - } - ResponseCmndNumber(CPU_show_freemem); -} - -void CmndSetSensor(void) -{ - if (XdrvMailbox.index < MAX_XSNS_DRIVERS) { - if (XdrvMailbox.payload >= 0) { - bitWrite(Settings.sensors[XdrvMailbox.index / 32], XdrvMailbox.index % 32, XdrvMailbox.payload &1); - if (1 == XdrvMailbox.payload) { - restart_flag = 2; - } - } - Response_P(PSTR("{\"" D_CMND_SETSENSOR "\":")); - XsnsSensorState(); - ResponseJsonEnd(); - } -} - -void CmndFlashMode(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) { - SetFlashMode(XdrvMailbox.payload); - } - ResponseCmndNumber(ESP.getFlashChipMode()); -} - -uint32_t DebugSwap32(uint32_t x) { - return ((x << 24) & 0xff000000 ) | - ((x << 8) & 0x00ff0000 ) | - ((x >> 8) & 0x0000ff00 ) | - ((x >> 24) & 0x000000ff ); -} - -void CmndFlashDump(void) -{ - - - - const uint32_t flash_start = 0x40200000; - const uint8_t bytes_per_cols = 0x20; - const uint32_t max = (SPIFFS_END + 5) * SPI_FLASH_SEC_SIZE; - - uint32_t start = flash_start; - uint32_t rows = 8; - - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= (max - bytes_per_cols))) { - start += (XdrvMailbox.payload &0x7FFFFFFC); - - char *p; - uint32_t is_payload = strtol(XdrvMailbox.data, &p, 16); - rows = strtol(p, &p, 10); - if (0 == rows) { rows = 8; } - } - uint32_t end = start + (rows * bytes_per_cols); - if ((end - flash_start) > max) { - end = flash_start + max; - } - - for (uint32_t pos = start; pos < end; pos += bytes_per_cols) { - uint32_t* values = (uint32_t*)(pos); - AddLog_P2(LOG_LEVEL_INFO, PSTR("%06X: %08X %08X %08X %08X %08X %08X %08X %08X"), pos - flash_start, - DebugSwap32(values[0]), DebugSwap32(values[1]), DebugSwap32(values[2]), DebugSwap32(values[3]), - DebugSwap32(values[4]), DebugSwap32(values[5]), DebugSwap32(values[6]), DebugSwap32(values[7])); - } - ResponseCmndDone(); -} - -#ifdef USE_I2C -void CmndI2cWrite(void) -{ - - if (i2c_flg) { - char* parms = XdrvMailbox.data; - uint8_t buffer[100]; - uint32_t index = 0; - - char *p; - char *data = strtok_r(parms, " ,", &p); - while (data != NULL && index < sizeof(buffer)) { - buffer[index++] = strtol(data, nullptr, 16); - data = strtok_r(nullptr, " ,", &p); - } - - if (index > 1) { - AddLogBuffer(LOG_LEVEL_INFO, buffer, index); - - Wire.beginTransmission(buffer[0]); - for (uint32_t i = 1; i < index; i++) { - Wire.write(buffer[i]); - } - int result = Wire.endTransmission(); - AddLog_P2(LOG_LEVEL_INFO, PSTR("I2C: Result %d"), result); - } - } - ResponseCmndDone(); -} - -void CmndI2cRead(void) -{ - - if (i2c_flg) { - char* parms = XdrvMailbox.data; - uint8_t buffer[100]; - uint32_t index = 0; - - char *p; - char *data = strtok_r(parms, " ,", &p); - while (data != NULL && index < sizeof(buffer)) { - buffer[index++] = strtol(data, nullptr, 16); - data = strtok_r(nullptr, " ,", &p); - } - - if (index > 0) { - uint8_t size = 1; - if (index > 1) { - size = buffer[1]; - } - Wire.requestFrom(buffer[0], size); - index = 0; - while (Wire.available() && index < sizeof(buffer)) { - buffer[index++] = Wire.read(); - } - if (index > 0) { - AddLogBuffer(LOG_LEVEL_INFO, buffer, index); - } - } - } - ResponseCmndDone(); -} - -void CmndI2cStretch(void) -{ - if (i2c_flg && (XdrvMailbox.payload > 0)) { - Wire.setClockStretchLimit(XdrvMailbox.payload); - } - ResponseCmndDone(); -} - -void CmndI2cClock(void) -{ - if (i2c_flg && (XdrvMailbox.payload > 0)) { - Wire.setClock(XdrvMailbox.payload); - } - ResponseCmndDone(); -} -#endif - - - - - -bool Xdrv99(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_LOOP: - CpuLoadLoop(); - break; - case FUNC_FREE_MEM: - if (CPU_show_freemem) { DebugFreeMem(); } - break; - case FUNC_PRE_INIT: - CPU_last_millis = millis(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kDebugCommands, DebugCommand); - break; - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_interface.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdrv_interface.ino" -#ifdef XFUNC_PTR_IN_ROM -bool (* const xdrv_func_ptr[])(uint8_t) PROGMEM = { -#else -bool (* const xdrv_func_ptr[])(uint8_t) = { -#endif - -#ifdef XDRV_01 - &Xdrv01, -#endif - -#ifdef XDRV_02 - &Xdrv02, -#endif - -#ifdef XDRV_03 - &Xdrv03, -#endif - -#ifdef XDRV_04 - &Xdrv04, -#endif - -#ifdef XDRV_05 - &Xdrv05, -#endif - -#ifdef XDRV_06 - &Xdrv06, -#endif - -#ifdef XDRV_07 - &Xdrv07, -#endif - -#ifdef XDRV_08 - &Xdrv08, -#endif - -#ifdef XDRV_09 - &Xdrv09, -#endif - -#ifdef XDRV_10 - &Xdrv10, -#endif - -#ifdef XDRV_11 - &Xdrv11, -#endif - -#ifdef XDRV_12 - &Xdrv12, -#endif - -#ifdef XDRV_13 - &Xdrv13, -#endif - -#ifdef XDRV_14 - &Xdrv14, -#endif - -#ifdef XDRV_15 - &Xdrv15, -#endif - -#ifdef XDRV_16 - &Xdrv16, -#endif - -#ifdef XDRV_17 - &Xdrv17, -#endif - -#ifdef XDRV_18 - &Xdrv18, -#endif - -#ifdef XDRV_19 - &Xdrv19, -#endif - -#ifdef XDRV_20 - &Xdrv20, -#endif - -#ifdef XDRV_21 - &Xdrv21, -#endif - -#ifdef XDRV_22 - &Xdrv22, -#endif - -#ifdef XDRV_23 - &Xdrv23, -#endif - -#ifdef XDRV_24 - &Xdrv24, -#endif - -#ifdef XDRV_25 - &Xdrv25, -#endif - -#ifdef XDRV_26 - &Xdrv26, -#endif - -#ifdef XDRV_27 - &Xdrv27, -#endif - -#ifdef XDRV_28 - &Xdrv28, -#endif - -#ifdef XDRV_29 - &Xdrv29, -#endif - -#ifdef XDRV_30 - &Xdrv30, -#endif - -#ifdef XDRV_31 - &Xdrv31, -#endif - -#ifdef XDRV_32 - &Xdrv32, -#endif - -#ifdef XDRV_33 - &Xdrv33, -#endif - -#ifdef XDRV_34 - &Xdrv34, -#endif - -#ifdef XDRV_35 - &Xdrv35, -#endif - -#ifdef XDRV_36 - &Xdrv36, -#endif - -#ifdef XDRV_37 - &Xdrv37, -#endif - -#ifdef XDRV_38 - &Xdrv38, -#endif - -#ifdef XDRV_39 - &Xdrv39, -#endif - -#ifdef XDRV_40 - &Xdrv40, -#endif - -#ifdef XDRV_41 - &Xdrv41, -#endif - -#ifdef XDRV_42 - &Xdrv42, -#endif - -#ifdef XDRV_43 - &Xdrv43, -#endif - -#ifdef XDRV_44 - &Xdrv44, -#endif - -#ifdef XDRV_45 - &Xdrv45, -#endif - -#ifdef XDRV_46 - &Xdrv46, -#endif - -#ifdef XDRV_47 - &Xdrv47, -#endif - -#ifdef XDRV_48 - &Xdrv48, -#endif - -#ifdef XDRV_49 - &Xdrv49, -#endif - -#ifdef XDRV_50 - &Xdrv50, -#endif - -#ifdef XDRV_51 - &Xdrv51, -#endif - -#ifdef XDRV_52 - &Xdrv52, -#endif - -#ifdef XDRV_53 - &Xdrv53, -#endif - -#ifdef XDRV_54 - &Xdrv54, -#endif - -#ifdef XDRV_55 - &Xdrv55, -#endif - -#ifdef XDRV_56 - &Xdrv56, -#endif - -#ifdef XDRV_57 - &Xdrv57, -#endif - -#ifdef XDRV_58 - &Xdrv58, -#endif - -#ifdef XDRV_59 - &Xdrv59, -#endif - -#ifdef XDRV_60 - &Xdrv60, -#endif - -#ifdef XDRV_61 - &Xdrv61, -#endif - -#ifdef XDRV_62 - &Xdrv62, -#endif - -#ifdef XDRV_63 - &Xdrv63, -#endif - -#ifdef XDRV_64 - &Xdrv64, -#endif - -#ifdef XDRV_65 - &Xdrv65, -#endif - -#ifdef XDRV_66 - &Xdrv66, -#endif - -#ifdef XDRV_67 - &Xdrv67, -#endif - -#ifdef XDRV_68 - &Xdrv68, -#endif - -#ifdef XDRV_69 - &Xdrv69, -#endif - -#ifdef XDRV_70 - &Xdrv70, -#endif - -#ifdef XDRV_71 - &Xdrv71, -#endif - -#ifdef XDRV_72 - &Xdrv72, -#endif - -#ifdef XDRV_73 - &Xdrv73, -#endif - -#ifdef XDRV_74 - &Xdrv74, -#endif - -#ifdef XDRV_75 - &Xdrv75, -#endif - -#ifdef XDRV_76 - &Xdrv76, -#endif - -#ifdef XDRV_77 - &Xdrv77, -#endif - -#ifdef XDRV_78 - &Xdrv78, -#endif - -#ifdef XDRV_79 - &Xdrv79, -#endif - -#ifdef XDRV_80 - &Xdrv80, -#endif - -#ifdef XDRV_81 - &Xdrv81, -#endif - -#ifdef XDRV_82 - &Xdrv82, -#endif - -#ifdef XDRV_83 - &Xdrv83, -#endif - -#ifdef XDRV_84 - &Xdrv84, -#endif - -#ifdef XDRV_85 - &Xdrv85, -#endif - -#ifdef XDRV_86 - &Xdrv86, -#endif - -#ifdef XDRV_87 - &Xdrv87, -#endif - -#ifdef XDRV_88 - &Xdrv88, -#endif - -#ifdef XDRV_89 - &Xdrv89, -#endif - -#ifdef XDRV_90 - &Xdrv90, -#endif - -#ifdef XDRV_91 - &Xdrv91, -#endif - -#ifdef XDRV_92 - &Xdrv92, -#endif - -#ifdef XDRV_93 - &Xdrv93, -#endif - -#ifdef XDRV_94 - &Xdrv94, -#endif - -#ifdef XDRV_95 - &Xdrv95, -#endif - -#ifdef XDRV_96 - &Xdrv96, -#endif - -#ifdef XDRV_97 - &Xdrv97, -#endif - -#ifdef XDRV_98 - &Xdrv98, -#endif - -#ifdef XDRV_99 - &Xdrv99 -#endif -}; - -const uint8_t xdrv_present = sizeof(xdrv_func_ptr) / sizeof(xdrv_func_ptr[0]); - - - - - -#ifdef XFUNC_PTR_IN_ROM -const uint8_t kXdrvList[] PROGMEM = { -#else -const uint8_t kXdrvList[] = { -#endif - -#ifdef XDRV_01 - XDRV_01, -#endif - -#ifdef XDRV_02 - XDRV_02, -#endif - -#ifdef XDRV_03 - XDRV_03, -#endif - -#ifdef XDRV_04 - XDRV_04, -#endif - -#ifdef XDRV_05 - XDRV_05, -#endif - -#ifdef XDRV_06 - XDRV_06, -#endif - -#ifdef XDRV_07 - XDRV_07, -#endif - -#ifdef XDRV_08 - XDRV_08, -#endif - -#ifdef XDRV_09 - XDRV_09, -#endif - -#ifdef XDRV_10 - XDRV_10, -#endif - -#ifdef XDRV_11 - XDRV_11, -#endif - -#ifdef XDRV_12 - XDRV_12, -#endif - -#ifdef XDRV_13 - XDRV_13, -#endif - -#ifdef XDRV_14 - XDRV_14, -#endif - -#ifdef XDRV_15 - XDRV_15, -#endif - -#ifdef XDRV_16 - XDRV_16, -#endif - -#ifdef XDRV_17 - XDRV_17, -#endif - -#ifdef XDRV_18 - XDRV_18, -#endif - -#ifdef XDRV_19 - XDRV_19, -#endif - -#ifdef XDRV_20 - XDRV_20, -#endif - -#ifdef XDRV_21 - XDRV_21, -#endif - -#ifdef XDRV_22 - XDRV_22, -#endif - -#ifdef XDRV_23 - XDRV_23, -#endif - -#ifdef XDRV_24 - XDRV_24, -#endif - -#ifdef XDRV_25 - XDRV_25, -#endif - -#ifdef XDRV_26 - XDRV_26, -#endif - -#ifdef XDRV_27 - XDRV_27, -#endif - -#ifdef XDRV_28 - XDRV_28, -#endif - -#ifdef XDRV_29 - XDRV_29, -#endif - -#ifdef XDRV_30 - XDRV_30, -#endif - -#ifdef XDRV_31 - XDRV_31, -#endif - -#ifdef XDRV_32 - XDRV_32, -#endif - -#ifdef XDRV_33 - XDRV_33, -#endif - -#ifdef XDRV_34 - XDRV_34, -#endif - -#ifdef XDRV_35 - XDRV_35, -#endif - -#ifdef XDRV_36 - XDRV_36, -#endif - -#ifdef XDRV_37 - XDRV_37, -#endif - -#ifdef XDRV_38 - XDRV_38, -#endif - -#ifdef XDRV_39 - XDRV_39, -#endif - -#ifdef XDRV_40 - XDRV_40, -#endif - -#ifdef XDRV_41 - XDRV_41, -#endif - -#ifdef XDRV_42 - XDRV_42, -#endif - -#ifdef XDRV_43 - XDRV_43, -#endif - -#ifdef XDRV_44 - XDRV_44, -#endif - -#ifdef XDRV_45 - XDRV_45, -#endif - -#ifdef XDRV_46 - XDRV_46, -#endif - -#ifdef XDRV_47 - XDRV_47, -#endif - -#ifdef XDRV_48 - XDRV_48, -#endif - -#ifdef XDRV_49 - XDRV_49, -#endif - -#ifdef XDRV_50 - XDRV_50, -#endif - -#ifdef XDRV_51 - XDRV_51, -#endif - -#ifdef XDRV_52 - XDRV_52, -#endif - -#ifdef XDRV_53 - XDRV_53, -#endif - -#ifdef XDRV_54 - XDRV_54, -#endif - -#ifdef XDRV_55 - XDRV_55, -#endif - -#ifdef XDRV_56 - XDRV_56, -#endif - -#ifdef XDRV_57 - XDRV_57, -#endif - -#ifdef XDRV_58 - XDRV_58, -#endif - -#ifdef XDRV_59 - XDRV_59, -#endif - -#ifdef XDRV_60 - XDRV_60, -#endif - -#ifdef XDRV_61 - XDRV_61, -#endif - -#ifdef XDRV_62 - XDRV_62, -#endif - -#ifdef XDRV_63 - XDRV_63, -#endif - -#ifdef XDRV_64 - XDRV_64, -#endif - -#ifdef XDRV_65 - XDRV_65, -#endif - -#ifdef XDRV_66 - XDRV_66, -#endif - -#ifdef XDRV_67 - XDRV_67, -#endif - -#ifdef XDRV_68 - XDRV_68, -#endif - -#ifdef XDRV_69 - XDRV_69, -#endif - -#ifdef XDRV_70 - XDRV_70, -#endif - -#ifdef XDRV_71 - XDRV_71, -#endif - -#ifdef XDRV_72 - XDRV_72, -#endif - -#ifdef XDRV_73 - XDRV_73, -#endif - -#ifdef XDRV_74 - XDRV_74, -#endif - -#ifdef XDRV_75 - XDRV_75, -#endif - -#ifdef XDRV_76 - XDRV_76, -#endif - -#ifdef XDRV_77 - XDRV_77, -#endif - -#ifdef XDRV_78 - XDRV_78, -#endif - -#ifdef XDRV_79 - XDRV_79, -#endif - -#ifdef XDRV_80 - XDRV_80, -#endif - -#ifdef XDRV_81 - XDRV_81, -#endif - -#ifdef XDRV_82 - XDRV_82, -#endif - -#ifdef XDRV_83 - XDRV_83, -#endif - -#ifdef XDRV_84 - XDRV_84, -#endif - -#ifdef XDRV_85 - XDRV_85, -#endif - -#ifdef XDRV_86 - XDRV_86, -#endif - -#ifdef XDRV_87 - XDRV_87, -#endif - -#ifdef XDRV_88 - XDRV_88, -#endif - -#ifdef XDRV_89 - XDRV_89, -#endif - -#ifdef XDRV_90 - XDRV_90, -#endif - -#ifdef XDRV_91 - XDRV_91, -#endif - -#ifdef XDRV_92 - XDRV_92, -#endif - -#ifdef XDRV_93 - XDRV_93, -#endif - -#ifdef XDRV_94 - XDRV_94, -#endif - -#ifdef XDRV_95 - XDRV_95, -#endif - -#ifdef XDRV_96 - XDRV_96, -#endif - -#ifdef XDRV_97 - XDRV_97, -#endif - -#ifdef XDRV_98 - XDRV_98, -#endif - -#ifdef XDRV_99 - XDRV_99 -#endif -}; - - - -void XsnsDriverState(void) -{ - ResponseAppend_P(PSTR(",\"Drivers\":\"")); - for (uint32_t i = 0; i < sizeof(kXdrvList); i++) { -#ifdef XFUNC_PTR_IN_ROM - uint32_t driverid = pgm_read_byte(kXdrvList + i); -#else - uint32_t driverid = kXdrvList[i]; -#endif - ResponseAppend_P(PSTR("%s%d"), (i) ? "," : "", driverid); - } - ResponseAppend_P(PSTR("\"")); -} - - - -bool XdrvRulesProcess(void) -{ - return XdrvCallDriver(10, FUNC_RULES_PROCESS); -} - -#ifdef USE_DEBUG_DRIVER -void ShowFreeMem(const char *where) -{ - char stemp[30]; - snprintf_P(stemp, sizeof(stemp), where); - XdrvMailbox.data = stemp; - XdrvCall(FUNC_FREE_MEM); -} -#endif - - - - - -bool XdrvCallDriver(uint32_t driver, uint8_t Function) -{ - for (uint32_t x = 0; x < xdrv_present; x++) { -#ifdef XFUNC_PTR_IN_ROM - uint32_t listed = pgm_read_byte(kXdrvList + x); -#else - uint32_t listed = kXdrvList[x]; -#endif - if (driver == listed) { - return xdrv_func_ptr[x](Function); - } - } - return false; -} - - - - - -bool XdrvCall(uint8_t Function) -{ - bool result = false; - - DEBUG_TRACE_LOG(PSTR("DRV: %d"), Function); - - for (uint32_t x = 0; x < xdrv_present; x++) { - result = xdrv_func_ptr[x](Function); - - if (result && ((FUNC_COMMAND == Function) || - (FUNC_COMMAND_DRIVER == Function) || - (FUNC_MQTT_DATA == Function) || - (FUNC_RULES_PROCESS == Function) || - (FUNC_BUTTON_PRESSED == Function) || - (FUNC_SERIAL == Function) || - (FUNC_MODULE_INIT == Function) || - (FUNC_SET_CHANNELS == Function) || - (FUNC_PIN_STATE == Function) || - (FUNC_SET_DEVICE_POWER == Function) - )) { - break; - } - } - - return result; -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_01_lcd.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_01_lcd.ino" -#ifdef USE_I2C -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_LCD - -#define XDSP_01 1 -#define XI2C_03 3 - -#define LCD_ADDRESS1 0x27 -#define LCD_ADDRESS2 0x3F - -#include -#include - -LiquidCrystal_I2C *lcd; - - - -void LcdInitMode(void) -{ - lcd->init(); - lcd->clear(); -} - -void LcdInit(uint8_t mode) -{ - switch(mode) { - case DISPLAY_INIT_MODE: - LcdInitMode(); -#ifdef USE_DISPLAY_MODES1TO5 - DisplayClearScreenBuffer(); -#endif - break; - case DISPLAY_INIT_PARTIAL: - case DISPLAY_INIT_FULL: - break; - } -} - -void LcdInitDriver(void) -{ - if (!Settings.display_model) { - if (I2cSetDevice(LCD_ADDRESS1)) { - Settings.display_address[0] = LCD_ADDRESS1; - Settings.display_model = XDSP_01; - } - else if (I2cSetDevice(LCD_ADDRESS2)) { - Settings.display_address[0] = LCD_ADDRESS2; - Settings.display_model = XDSP_01; - } - } - - if (XDSP_01 == Settings.display_model) { - I2cSetActiveFound(Settings.display_address[0], "LCD"); - - Settings.display_width = Settings.display_cols[0]; - Settings.display_height = Settings.display_rows; - lcd = new LiquidCrystal_I2C(Settings.display_address[0], Settings.display_cols[0], Settings.display_rows); - -#ifdef USE_DISPLAY_MODES1TO5 - DisplayAllocScreenBuffer(); -#endif - - LcdInitMode(); - } -} - -void LcdDrawStringAt(void) -{ - if (dsp_flag) { - dsp_x--; - dsp_y--; - } - lcd->setCursor(dsp_x, dsp_y); - lcd->print(dsp_str); -} - -void LcdDisplayOnOff(uint8_t on) -{ - if (on) { - lcd->backlight(); - } else { - lcd->noBacklight(); - } -} - - - -#ifdef USE_DISPLAY_MODES1TO5 - -void LcdCenter(uint8_t row, char* txt) -{ - char line[Settings.display_cols[0] +2]; - - int len = strlen(txt); - int offset = 0; - if (len >= Settings.display_cols[0]) { - len = Settings.display_cols[0]; - } else { - offset = (Settings.display_cols[0] - len) / 2; - } - memset(line, 0x20, Settings.display_cols[0]); - line[Settings.display_cols[0]] = 0; - for (uint32_t i = 0; i < len; i++) { - line[offset +i] = txt[i]; - } - lcd->setCursor(0, row); - lcd->print(line); -} - -bool LcdPrintLog(void) -{ - bool result = false; - - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - - char* txt = DisplayLogBuffer('\337'); - if (txt != nullptr) { - uint8_t last_row = Settings.display_rows -1; - - for (uint32_t i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - lcd->setCursor(0, i); - lcd->print(disp_screen_buffer[i +1]); - } - strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); - DisplayFillScreen(last_row); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); - - lcd->setCursor(0, last_row); - lcd->print(disp_screen_buffer[last_row]); - - result = true; - } - } - return result; -} - -void LcdTime(void) -{ - char line[Settings.display_cols[0] +1]; - - snprintf_P(line, sizeof(line), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - LcdCenter(0, line); - snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); - LcdCenter(1, line); -} - -void LcdRefresh(void) -{ - if (Settings.display_mode) { - switch (Settings.display_mode) { - case 1: - LcdTime(); - break; - case 2: - case 4: - LcdPrintLog(); - break; - case 3: - case 5: { - if (!LcdPrintLog()) { LcdTime(); } - break; - } - } - } -} - -#endif - - - - - -bool Xdsp01(uint8_t function) -{ - if (!I2cEnabled(XI2C_03)) { return false; } - - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - LcdInitDriver(); - } - else if (XDSP_01 == Settings.display_model) { - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_INIT: - LcdInit(dsp_init); - break; - case FUNC_DISPLAY_POWER: - LcdDisplayOnOff(disp_power); - break; - case FUNC_DISPLAY_CLEAR: - lcd->clear(); - break; -# 238 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_01_lcd.ino" - case FUNC_DISPLAY_DRAW_STRING: - LcdDrawStringAt(); - break; - case FUNC_DISPLAY_ONOFF: - LcdDisplayOnOff(dsp_on); - break; - - -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - LcdRefresh(); - break; -#endif - } - } - return result; -} - -#endif -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_02_ssd1306.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_02_ssd1306.ino" -#ifdef USE_I2C -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_SSD1306 - -#define XDSP_02 2 -#define XI2C_04 4 - -#define OLED_RESET 4 - -#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); - -#define OLED_ADDRESS1 0x3C -#define OLED_ADDRESS2 0x3D - -#define OLED_BUFFER_COLS 40 -#define OLED_BUFFER_ROWS 16 - -#define OLED_FONT_WIDTH 6 -#define OLED_FONT_HEIGTH 8 - -#include -#include -#include - -Adafruit_SSD1306 *oled1306; - -extern uint8_t *buffer; - - - -void SSD1306InitDriver(void) -{ - if (!Settings.display_model) { - if (I2cSetDevice(OLED_ADDRESS1)) { - Settings.display_address[0] = OLED_ADDRESS1; - Settings.display_model = XDSP_02; - } - else if (I2cSetDevice(OLED_ADDRESS2)) { - Settings.display_address[0] = OLED_ADDRESS2; - Settings.display_model = XDSP_02; - } - } - - if (XDSP_02 == Settings.display_model) { - I2cSetActiveFound(Settings.display_address[0], "SSD1306"); - - if ((Settings.display_width != 64) && (Settings.display_width != 96) && (Settings.display_width != 128)) { - Settings.display_width = 128; - } - if ((Settings.display_height != 16) && (Settings.display_height != 32) && (Settings.display_height != 48) && (Settings.display_height != 64)) { - Settings.display_height = 64; - } - - uint8_t reset_pin = -1; - if (pin[GPIO_OLED_RESET] < 99) { - reset_pin = pin[GPIO_OLED_RESET]; - } - - - if (buffer) { free(buffer); } - buffer = (unsigned char*)calloc((Settings.display_width * Settings.display_height) / 8,1); - if (!buffer) { return; } - - - - oled1306 = new Adafruit_SSD1306(Settings.display_width, Settings.display_height, &Wire, reset_pin); - oled1306->begin(SSD1306_SWITCHCAPVCC, Settings.display_address[0], reset_pin >= 0); - renderer = oled1306; - renderer->DisplayInit(DISPLAY_INIT_MODE, Settings.display_size, Settings.display_rotate, Settings.display_font); - renderer->setTextColor(1,0); - -#ifdef SHOW_SPLASH - renderer->setTextFont(0); - renderer->setTextSize(2); - renderer->setCursor(20,20); - renderer->println(F("SSD1306")); - renderer->Updateframe(); - renderer->DisplayOnff(1); -#endif - - } -} - - -#ifdef USE_DISPLAY_MODES1TO5 - -void Ssd1306PrintLog(void) -{ - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - - char* txt = DisplayLogBuffer('\370'); - if (txt != NULL) { - uint8_t last_row = Settings.display_rows -1; - - renderer->clearDisplay(); - renderer->setTextSize(Settings.display_size); - renderer->setCursor(0,0); - for (byte i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - renderer->println(disp_screen_buffer[i]); - } - strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); - DisplayFillScreen(last_row); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); - - renderer->println(disp_screen_buffer[last_row]); - renderer->Updateframe(); - } - } -} - -void Ssd1306Time(void) -{ - char line[12]; - - renderer->clearDisplay(); - renderer->setTextSize(Settings.display_size); - renderer->setTextFont(Settings.display_font); - renderer->setCursor(0, 0); - snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - renderer->println(line); - snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); - renderer->println(line); - renderer->Updateframe(); -} - -void Ssd1306Refresh(void) -{ - if (!renderer) return; - - if (Settings.display_mode) { - switch (Settings.display_mode) { - case 1: - Ssd1306Time(); - break; - case 2: - case 3: - case 4: - case 5: - Ssd1306PrintLog(); - break; - } - } -} - -#endif - - - - - -bool Xdsp02(byte function) -{ - if (!I2cEnabled(XI2C_04)) { return false; } - - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - SSD1306InitDriver(); - } - else if (XDSP_02 == Settings.display_model) { - switch (function) { -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - Ssd1306Refresh(); - break; -#endif - case FUNC_DISPLAY_MODEL: - result = true; - break; - } - } - return result; -} - -#endif -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_03_matrix.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_03_matrix.ino" -#ifdef USE_I2C -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_MATRIX - -#define XDSP_03 3 -#define XI2C_05 5 - -#define MTX_MAX_SCREEN_BUFFER 80 - -#include -#include -#include - -Adafruit_8x8matrix *matrix[8]; -uint8_t mtx_matrices = 0; -uint8_t mtx_state = 0; -uint8_t mtx_counter = 0; -int16_t mtx_x = 0; -int16_t mtx_y = 0; - - -char *mtx_buffer = nullptr; - -uint8_t mtx_mode = 0; -uint8_t mtx_loop = 0; -uint8_t mtx_done = 0; - - - -void MatrixWrite(void) -{ - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->writeDisplay(); - } -} - -void MatrixClear(void) -{ - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->clear(); - } - MatrixWrite(); -} - -void MatrixFixed(char* txt) -{ - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->clear(); - matrix[i]->setCursor(-i *8, 0); - matrix[i]->print(txt); - matrix[i]->setBrightness(Settings.display_dimmer); - } - MatrixWrite(); -} - -void MatrixCenter(char* txt) -{ - int offset; - - int len = strlen(txt); - offset = (len < 8) ? offset = ((mtx_matrices *8) - (len *6)) / 2 : 0; - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->clear(); - matrix[i]->setCursor(-(i *8)+offset, 0); - matrix[i]->print(txt); - matrix[i]->setBrightness(Settings.display_dimmer); - } - MatrixWrite(); -} - -void MatrixScrollLeft(char* txt, int loop) -{ - switch (mtx_state) { - case 1: - mtx_state = 2; - - mtx_x = 8 * mtx_matrices; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), txt); - - disp_refresh = Settings.display_refresh; - case 2: - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->clear(); - matrix[i]->setCursor(mtx_x - i *8, 0); - matrix[i]->print(txt); - matrix[i]->setBrightness(Settings.display_dimmer); - } - MatrixWrite(); - - mtx_x--; - int16_t len = strlen(txt); - if (mtx_x < -(len *6)) { mtx_state = loop; } - } - break; - } -} - -void MatrixScrollUp(char* txt, int loop) -{ - int wordcounter = 0; - char tmpbuf[200]; - char *words[100]; - - - - char separators[] = " /"; - - switch (mtx_state) { - case 1: - mtx_state = 2; - - mtx_y = 8; - mtx_counter = 0; - disp_refresh = Settings.display_refresh; - case 2: - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - strlcpy(tmpbuf, txt, sizeof(tmpbuf)); - char *p = strtok(tmpbuf, separators); - while (p != nullptr && wordcounter < 40) { - words[wordcounter++] = p; - p = strtok(nullptr, separators); - } - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->clear(); - for (uint32_t j = 0; j < wordcounter; j++) { - matrix[i]->setCursor(-i *8, mtx_y + (j *8)); - matrix[i]->println(words[j]); - } - matrix[i]->setBrightness(Settings.display_dimmer); - } - MatrixWrite(); - if (((mtx_y %8) == 0) && mtx_counter) { - mtx_counter--; - } else { - mtx_y--; - mtx_counter = STATES * 1; - } - if (mtx_y < -(wordcounter *8)) { mtx_state = loop; } - } - break; - } -} - - - -void MatrixInitMode(void) -{ - for (uint32_t i = 0; i < mtx_matrices; i++) { - matrix[i]->setRotation(Settings.display_rotate); - matrix[i]->setBrightness(Settings.display_dimmer); - matrix[i]->blinkRate(0); - matrix[i]->setTextWrap(false); - - - matrix[i]->cp437(true); - } - MatrixClear(); -} - -void MatrixInit(uint8_t mode) -{ - switch(mode) { - case DISPLAY_INIT_MODE: - MatrixInitMode(); - break; - case DISPLAY_INIT_PARTIAL: - case DISPLAY_INIT_FULL: - break; - } -} - -void MatrixInitDriver(void) -{ - mtx_buffer = (char*)(malloc(MTX_MAX_SCREEN_BUFFER)); - if (mtx_buffer != nullptr) { - if (!Settings.display_model) { - if (I2cSetDevice(Settings.display_address[1])) { - Settings.display_model = XDSP_03; - } - } - - if (XDSP_03 == Settings.display_model) { - mtx_state = 1; - for (mtx_matrices = 0; mtx_matrices < 8; mtx_matrices++) { - if (Settings.display_address[mtx_matrices]) { - I2cSetActiveFound(Settings.display_address[mtx_matrices], "8x8Matrix"); - matrix[mtx_matrices] = new Adafruit_8x8matrix(); - matrix[mtx_matrices]->begin(Settings.display_address[mtx_matrices]); - } else { - break; - } - } - - Settings.display_width = mtx_matrices * 8; - Settings.display_height = 8; - - MatrixInitMode(); - } - } -} - -void MatrixOnOff(void) -{ - if (!disp_power) { MatrixClear(); } -} - -void MatrixDrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) -{ - strlcpy(mtx_buffer, str, MTX_MAX_SCREEN_BUFFER); - mtx_mode = x &1; - mtx_loop = y &1; - if (!mtx_state) { mtx_state = 1; } -} - - - -#ifdef USE_DISPLAY_MODES1TO5 - -void MatrixPrintLog(uint8_t direction) -{ - char* txt = (!mtx_done) ? DisplayLogBuffer('\370') : mtx_buffer; - if (txt != nullptr) { - if (!mtx_state) { mtx_state = 1; } - - if (!mtx_done) { - - uint8_t space = 0; - uint8_t max_cols = (disp_log_buffer_cols < MTX_MAX_SCREEN_BUFFER) ? disp_log_buffer_cols : MTX_MAX_SCREEN_BUFFER; - mtx_buffer[0] = '\0'; - uint8_t i = 0; - while ((txt[i] != '\0') && (i < max_cols)) { - if (txt[i] == ' ') { - space++; - } else { - space = 0; - } - if (space < 2) { - strncat(mtx_buffer, (const char*)txt +i, (strlen(mtx_buffer) < MTX_MAX_SCREEN_BUFFER -1) ? 1 : 0); - } - i++; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), mtx_buffer); - - mtx_done = 1; - } - - if (direction) { - MatrixScrollUp(mtx_buffer, 0); - } else { - MatrixScrollLeft(mtx_buffer, 0); - } - if (!mtx_state) { mtx_done = 0; } - } else { - char disp_time[9]; - - snprintf_P(disp_time, sizeof(disp_time), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - MatrixFixed(disp_time); - } -} - -#endif - -void MatrixRefresh(void) -{ - if (disp_power) { - switch (Settings.display_mode) { - case 0: { - switch (mtx_mode) { - case 0: - MatrixScrollLeft(mtx_buffer, mtx_loop); - break; - case 1: - MatrixScrollUp(mtx_buffer, mtx_loop); - break; - } - break; - } -#ifdef USE_DISPLAY_MODES1TO5 - case 2: { - char disp_date[9]; - snprintf_P(disp_date, sizeof(disp_date), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%02d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year -2000); - MatrixFixed(disp_date); - break; - } - case 3: { - char disp_day[10]; - snprintf_P(disp_day, sizeof(disp_day), PSTR("%d %s"), RtcTime.day_of_month, RtcTime.name_of_month); - MatrixCenter(disp_day); - break; - } - case 4: - MatrixPrintLog(0); - break; - case 1: - case 5: - MatrixPrintLog(1); - break; -#endif - } - } -} - - - - - -bool Xdsp03(uint8_t function) -{ - if (!I2cEnabled(XI2C_05)) { return false; } - - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - MatrixInitDriver(); - } - else if (XDSP_03 == Settings.display_model) { - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_INIT: - MatrixInit(dsp_init); - break; - case FUNC_DISPLAY_EVERY_50_MSECOND: - MatrixRefresh(); - break; - case FUNC_DISPLAY_POWER: - MatrixOnOff(); - break; - case FUNC_DISPLAY_DRAW_STRING: - MatrixDrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); - break; - } - } - return result; -} - -#endif -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_04_ili9341.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_04_ili9341.ino" -#ifdef USE_SPI -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_ILI9341 - -#define XDSP_04 4 - -#define TFT_TOP 16 -#define TFT_BOTTOM 16 -#define TFT_FONT_WIDTH 6 -#define TFT_FONT_HEIGTH 8 - -#include -#include -#include - -Adafruit_ILI9341 *tft; - -uint16_t tft_scroll; - - - -void Ili9341InitMode(void) -{ - tft->setRotation(Settings.display_rotate); - tft->invertDisplay(0); - tft->fillScreen(ILI9341_BLACK); - tft->setTextWrap(false); - tft->cp437(true); - if (!Settings.display_mode) { - tft->setCursor(0, 0); - tft->setTextColor(ILI9341_WHITE, ILI9341_BLACK); - tft->setTextSize(1); - } else { - tft->setScrollMargins(TFT_TOP, TFT_BOTTOM); - tft->setCursor(0, 0); - tft->setTextColor(ILI9341_YELLOW, ILI9341_BLACK); - tft->setTextSize(2); - - - tft_scroll = TFT_TOP; - } -} - -void Ili9341Init(uint8_t mode) -{ - switch(mode) { - case DISPLAY_INIT_MODE: - Ili9341InitMode(); -#ifdef USE_DISPLAY_MODES1TO5 - if (Settings.display_rotate) { - DisplayClearScreenBuffer(); - } -#endif - break; - case DISPLAY_INIT_PARTIAL: - case DISPLAY_INIT_FULL: - break; - } -} - -void Ili9341InitDriver(void) -{ - if (!Settings.display_model) { - Settings.display_model = XDSP_04; - } - - if (XDSP_04 == Settings.display_model) { - if (Settings.display_width != ILI9341_TFTWIDTH) { - Settings.display_width = ILI9341_TFTWIDTH; - } - if (Settings.display_height != ILI9341_TFTHEIGHT) { - Settings.display_height = ILI9341_TFTHEIGHT; - } - tft = new Adafruit_ILI9341(pin[GPIO_SPI_CS], pin[GPIO_SPI_DC]); - tft->begin(); - -#ifdef USE_DISPLAY_MODES1TO5 - if (Settings.display_rotate) { - DisplayAllocScreenBuffer(); - } -#endif - - Ili9341InitMode(); - } -} - -void Ili9341Clear(void) -{ - tft->fillScreen(ILI9341_BLACK); - tft->setCursor(0, 0); -} - -void Ili9341DrawStringAt(uint16_t x, uint16_t y, char *str, uint16_t color, uint8_t flag) -{ - uint16_t active_color = ILI9341_WHITE; - - tft->setTextSize(Settings.display_size); - if (!flag) { - tft->setCursor(x, y); - } else { - tft->setCursor((x-1) * TFT_FONT_WIDTH * Settings.display_size, (y-1) * TFT_FONT_HEIGTH * Settings.display_size); - } - if (color) { active_color = color; } - tft->setTextColor(active_color, ILI9341_BLACK); - tft->println(str); -} - -void Ili9341DisplayOnOff(uint8_t on) -{ - - - if (pin[GPIO_BACKLIGHT] < 99) { - pinMode(pin[GPIO_BACKLIGHT], OUTPUT); - digitalWrite(pin[GPIO_BACKLIGHT], on); - } -} - -void Ili9341OnOff(void) -{ - Ili9341DisplayOnOff(disp_power); -} - - - -#ifdef USE_DISPLAY_MODES1TO5 - -void Ili9341PrintLog(void) -{ - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - if (Settings.display_rotate) { - if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - } - - char* txt = DisplayLogBuffer('\370'); - if (txt != nullptr) { - uint8_t size = Settings.display_size; - uint16_t theight = size * TFT_FONT_HEIGTH; - - tft->setTextSize(size); - tft->setTextColor(ILI9341_CYAN, ILI9341_BLACK); - if (!Settings.display_rotate) { - tft->setCursor(0, tft_scroll); - tft->fillRect(0, tft_scroll, tft->width(), theight, ILI9341_BLACK); - tft->print(txt); - tft_scroll += theight; - if (tft_scroll >= (tft->height() - TFT_BOTTOM)) { - tft_scroll = TFT_TOP; - } - tft->scrollTo(tft_scroll); - } else { - uint8_t last_row = Settings.display_rows -1; - - tft_scroll = theight; - tft->setCursor(0, tft_scroll); - for (uint32_t i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - - tft->print(disp_screen_buffer[i]); - tft_scroll += theight; - tft->setCursor(0, tft_scroll); - delay(1); - } - strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); - DisplayFillScreen(last_row); - tft->print(disp_screen_buffer[last_row]); - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); - } - } -} - -void Ili9341Refresh(void) -{ - if (Settings.display_mode) { - char tftdt[Settings.display_cols[0] +1]; - char date4[11]; - char space[Settings.display_cols[0] - 17]; - char time[9]; - - tft->setTextSize(2); - tft->setTextColor(ILI9341_YELLOW, ILI9341_RED); - tft->setCursor(0, 0); - - snprintf_P(date4, sizeof(date4), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); - memset(space, 0x20, sizeof(space)); - space[sizeof(space) -1] = '\0'; - snprintf_P(time, sizeof(time), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - snprintf_P(tftdt, sizeof(tftdt), PSTR("%s%s%s"), date4, space, time); - - tft->print(tftdt); - - switch (Settings.display_mode) { - case 1: - case 2: - case 3: - case 4: - case 5: - Ili9341PrintLog(); - break; - } - } -} - -#endif - - - - - -bool Xdsp04(uint8_t function) -{ - bool result = false; - - if (spi_flg) { - if (FUNC_DISPLAY_INIT_DRIVER == function) { - Ili9341InitDriver(); - } - else if (XDSP_04 == Settings.display_model) { - - if (!dsp_color) { dsp_color = ILI9341_WHITE; } - - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_INIT: - Ili9341Init(dsp_init); - break; - case FUNC_DISPLAY_POWER: - Ili9341OnOff(); - break; - case FUNC_DISPLAY_CLEAR: - Ili9341Clear(); - break; - case FUNC_DISPLAY_DRAW_HLINE: - tft->writeFastHLine(dsp_x, dsp_y, dsp_len, dsp_color); - break; - case FUNC_DISPLAY_DRAW_VLINE: - tft->writeFastVLine(dsp_x, dsp_y, dsp_len, dsp_color); - break; - case FUNC_DISPLAY_DRAW_LINE: - tft->writeLine(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); - break; - case FUNC_DISPLAY_DRAW_CIRCLE: - tft->drawCircle(dsp_x, dsp_y, dsp_rad, dsp_color); - break; - case FUNC_DISPLAY_FILL_CIRCLE: - tft->fillCircle(dsp_x, dsp_y, dsp_rad, dsp_color); - break; - case FUNC_DISPLAY_DRAW_RECTANGLE: - tft->drawRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); - break; - case FUNC_DISPLAY_FILL_RECTANGLE: - tft->fillRect(dsp_x, dsp_y, dsp_x2, dsp_y2, dsp_color); - break; - - - - case FUNC_DISPLAY_TEXT_SIZE: - tft->setTextSize(Settings.display_size); - break; - case FUNC_DISPLAY_FONT_SIZE: - - break; - case FUNC_DISPLAY_DRAW_STRING: - Ili9341DrawStringAt(dsp_x, dsp_y, dsp_str, dsp_color, dsp_flag); - break; - case FUNC_DISPLAY_ONOFF: - Ili9341DisplayOnOff(dsp_on); - break; - case FUNC_DISPLAY_ROTATION: - tft->setRotation(Settings.display_rotate); - break; -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - Ili9341Refresh(); - break; -#endif - } - } - } - return result; -} - -#endif -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_05_epaper_29.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_05_epaper_29.ino" -#ifdef USE_SPI -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_EPAPER_29 - -#define XDSP_05 5 - -#define EPD_TOP 12 -#define EPD_FONT_HEIGTH 12 - -#define COLORED 1 -#define UNCOLORED 0 - - - -#define USE_TINY_FONT - -#include -#include - - -extern uint8_t *buffer; -uint16_t epd_scroll; - -Epd *epd; - - - -void EpdInitDriver29() -{ - if (!Settings.display_model) { - Settings.display_model = XDSP_05; - } - - if (XDSP_05 == Settings.display_model) { - if (Settings.display_width != EPD_WIDTH) { - Settings.display_width = EPD_WIDTH; - } - if (Settings.display_height != EPD_HEIGHT) { - Settings.display_height = EPD_HEIGHT; - } - - - if (buffer) free(buffer); - buffer=(unsigned char*)calloc((EPD_WIDTH * EPD_HEIGHT) / 8,1); - if (!buffer) return; - - - epd = new Epd(EPD_WIDTH,EPD_HEIGHT); - - - if ((pin[GPIO_SPI_CS] < 99) && (pin[GPIO_SPI_CLK] < 99) && (pin[GPIO_SPI_MOSI] < 99)) { - epd->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: HardSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SPI_CS], pin[GPIO_SPI_CLK], pin[GPIO_SPI_MOSI]); - } - else if ((pin[GPIO_SSPI_CS] < 99) && (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_MOSI] < 99)) { - epd->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EPD: SoftSPI CS %d, CLK %d, MOSI %d"),pin[GPIO_SSPI_CS], pin[GPIO_SSPI_SCLK], pin[GPIO_SSPI_MOSI]); - } else { - free(buffer); - return; - } - - renderer = epd; - epd->Init(DISPLAY_INIT_FULL); - epd->Init(DISPLAY_INIT_PARTIAL); - renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); - - renderer->setTextColor(1,0); - -#ifdef SHOW_SPLASH - - renderer->setTextFont(1); - renderer->DrawStringAt(50, 50, "Waveshare E-Paper Display!", COLORED,0); - renderer->Updateframe(); - delay(1000); - renderer->fillScreen(0); -#endif - - } -} - - - - - - - -#ifdef USE_DISPLAY_MODES1TO5 -#define EPD_FONT_HEIGTH 12 -void EpdPrintLog29(void) -{ - - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - - if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - - - char* txt = DisplayLogBuffer('\040'); - if (txt != nullptr) { - uint8_t size = Settings.display_size; - uint16_t theight = size * EPD_FONT_HEIGTH; - - renderer->setTextFont(size); - uint8_t last_row = Settings.display_rows -1; - - - epd_scroll = 0; - for (uint32_t i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[i], COLORED, 0); - epd_scroll += theight; - } - strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); - DisplayFillScreen(last_row); - renderer->DrawStringAt(0, epd_scroll, disp_screen_buffer[last_row], COLORED, 0); - - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "[%s]"), txt); - } - } -} - -void EpdRefresh29(void) -{ - if (Settings.display_mode) { - - if (!renderer) return; -# 165 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_05_epaper_29.ino" - switch (Settings.display_mode) { - case 1: - case 2: - case 3: - case 4: - case 5: - EpdPrintLog29(); - renderer->Updateframe(); - break; - } - - - } -} - -#endif - - - - - -bool Xdsp05(uint8_t function) -{ - bool result = false; - if (FUNC_DISPLAY_INIT_DRIVER == function) { - EpdInitDriver29(); - } - else if (XDSP_05 == Settings.display_model) { - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - EpdRefresh29(); - break; -#endif - } - } - return result; -} - -#endif -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_06_epaper_42.ino" -# 21 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_06_epaper_42.ino" -#ifdef USE_SPI -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_EPAPER_42 - -#define XDSP_06 6 - -#define COLORED42 1 -#define UNCOLORED42 0 - - - -#define USE_TINY_FONT - -#include -#include - -extern uint8_t *buffer; - -Epd42 *epd42; - - - - -void EpdInitDriver42() -{ - if (!Settings.display_model) { - Settings.display_model = XDSP_06; - } - - if (XDSP_06 == Settings.display_model) { - - if (Settings.display_width != EPD_WIDTH42) { - Settings.display_width = EPD_WIDTH42; - } - if (Settings.display_height != EPD_HEIGHT42) { - Settings.display_height = EPD_HEIGHT42; - } - - - if (buffer) free(buffer); - buffer=(unsigned char*)calloc((EPD_WIDTH42 * EPD_HEIGHT42) / 8,1); - if (!buffer) return; - - - epd42 = new Epd42(EPD_WIDTH42,EPD_HEIGHT42); - - #ifdef USE_SPI - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)) { - epd42->Begin(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); - } else { - free(buffer); - return; - } - #else - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { - epd42->Begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); - } else { - free(buffer); - return; - } - #endif - - renderer = epd42; - - epd42->Init(); - - renderer->fillScreen(0); - - - epd42->Init(DISPLAY_INIT_FULL); - - renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); - - epd42->ClearFrame(); - renderer->Updateframe(); - delay(3000); - renderer->setTextColor(1,0); - -#ifdef SHOW_SPLASH - - renderer->setTextFont(2); - renderer->DrawStringAt(50, 140, "Waveshare E-Paper!", COLORED42,0); - renderer->Updateframe(); - delay(350); - renderer->fillScreen(0); -#endif - - } -} - - - - - - - -#ifdef USE_DISPLAY_MODES1TO5 - -void EpdRefresh42() -{ - if (Settings.display_mode) { - - } -} - -#endif - - - - - - -bool Xdsp06(uint8_t function) -{ - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - EpdInitDriver42(); - } - else if (XDSP_06 == Settings.display_model) { - - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - EpdRefresh42(); - break; -#endif - } - } - return result; -} - - -#endif -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_07_sh1106.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_07_sh1106.ino" -#ifdef USE_I2C -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_SH1106 - -#define OLED_RESET 4 - -#define SPRINT(A) char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str); - -extern uint8_t *buffer; - -#define XDSP_07 7 -#define XI2C_06 6 - -#define OLED_ADDRESS1 0x3C -#define OLED_ADDRESS2 0x3D - -#define OLED_BUFFER_COLS 40 -#define OLED_BUFFER_ROWS 16 - -#define OLED_FONT_WIDTH 6 -#define OLED_FONT_HEIGTH 8 - -#include -#include -#include - -Adafruit_SH1106 *oled1106; - - - - -void SH1106InitDriver() -{ - if (!Settings.display_model) { - if (I2cSetDevice(OLED_ADDRESS1)) { - Settings.display_address[0] = OLED_ADDRESS1; - Settings.display_model = XDSP_07; - } - else if (I2cSetDevice(OLED_ADDRESS2)) { - Settings.display_address[0] = OLED_ADDRESS2; - Settings.display_model = XDSP_07; - } - } - - if (XDSP_07 == Settings.display_model) { - I2cSetActiveFound(Settings.display_address[0], "SH1106"); - - if (Settings.display_width != SH1106_LCDWIDTH) { - Settings.display_width = SH1106_LCDWIDTH; - } - if (Settings.display_height != SH1106_LCDHEIGHT) { - Settings.display_height = SH1106_LCDHEIGHT; - } - - - if (buffer) free(buffer); - buffer=(unsigned char*)calloc((SH1106_LCDWIDTH * SH1106_LCDHEIGHT) / 8,1); - if (!buffer) return; - - - oled1106 = new Adafruit_SH1106(SH1106_LCDWIDTH,SH1106_LCDHEIGHT); - renderer=oled1106; - renderer->Begin(SH1106_SWITCHCAPVCC, Settings.display_address[0],0); - renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); - renderer->setTextColor(1,0); - -#ifdef SHOW_SPLASH - renderer->setTextFont(0); - renderer->setTextSize(2); - renderer->setCursor(20,20); - renderer->println(F("SH1106")); - renderer->Updateframe(); - renderer->DisplayOnff(1); -#endif - } -} - - - -#ifdef USE_DISPLAY_MODES1TO5 - -void SH1106PrintLog(void) -{ - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - - char* txt = DisplayLogBuffer('\370'); - if (txt != NULL) { - uint8_t last_row = Settings.display_rows -1; - - renderer->clearDisplay(); - renderer->setTextSize(Settings.display_size); - renderer->setCursor(0,0); - for (byte i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - renderer->println(disp_screen_buffer[i]); - } - strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); - DisplayFillScreen(last_row); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); - - renderer->println(disp_screen_buffer[last_row]); - renderer->Updateframe(); - } - } -} - -void SH1106Time(void) -{ - char line[12]; - - renderer->clearDisplay(); - renderer->setTextSize(Settings.display_size); - renderer->setTextFont(Settings.display_font); - renderer->setCursor(0, 0); - snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - renderer->println(line); - snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); - renderer->println(line); - renderer->Updateframe(); -} - -void SH1106Refresh(void) -{ - if (!renderer) return; - if (Settings.display_mode) { - switch (Settings.display_mode) { - case 1: - SH1106Time(); - break; - case 2: - case 3: - case 4: - case 5: - SH1106PrintLog(); - break; - } - } -} - -#endif - - - - - -bool Xdsp07(uint8_t function) -{ - if (!I2cEnabled(XI2C_06)) { return false; } - - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - SH1106InitDriver(); - } - else if (XDSP_07 == Settings.display_model) { - - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - SH1106Refresh(); - break; -#endif - } - } - return result; -} - -#endif -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_08_ILI9488.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_08_ILI9488.ino" -#ifdef USE_SPI -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_ILI9488 - -#define XDSP_08 8 -#define XI2C_38 38 - -#define COLORED 1 -#define UNCOLORED 0 - - -#define FT6236_address 0x38 - - - -#define USE_TINY_FONT - - -#include -#include - -TouchLocation ili9488_pLoc; -uint8_t ili9488_ctouch_counter = 0; - - -#define BACKPLANE_PIN 2 - -extern uint8_t *buffer; -extern uint8_t color_type; -ILI9488 *ili9488; - -#ifdef USE_TOUCH_BUTTONS -extern VButton *buttons[]; -#endif - -extern const uint16_t picture[]; -uint8_t FT6236_found; - - - -void ILI9488_InitDriver() -{ - if (!Settings.display_model) { - Settings.display_model = XDSP_08; - } - - if (XDSP_08 == Settings.display_model) { - - if (Settings.display_width != ILI9488_TFTWIDTH) { - Settings.display_width = ILI9488_TFTWIDTH; - } - if (Settings.display_height != ILI9488_TFTHEIGHT) { - Settings.display_height = ILI9488_TFTHEIGHT; - } - - - buffer=NULL; - - - fg_color = ILI9488_WHITE; - bg_color = ILI9488_BLACK; - - uint8_t bppin=BACKPLANE_PIN; - if (pin[GPIO_BACKLIGHT]<99) { - bppin=pin[GPIO_BACKLIGHT]; - } - - - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ - ili9488 = new ILI9488(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK],bppin); - } else { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)) { - ili9488 = new ILI9488(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK],bppin); - } else { - return; - } - } - - SPI.begin(); - ili9488->begin(); - renderer = ili9488; - renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); - -#ifdef SHOW_SPLASH - - renderer->setTextFont(2); - renderer->setTextColor(ILI9488_WHITE,ILI9488_BLACK); - renderer->DrawStringAt(50, 50, "ILI9488 TFT Display!", ILI9488_WHITE,0); - delay(1000); - - -#endif - - color_type = COLOR_COLOR; - - - if (I2cEnabled(XI2C_38) && I2cSetDevice(FT6236_address)) { - FT6236begin(FT6236_address); - FT6236_found=1; - I2cSetActiveFound(FT6236_address, "FT6236"); - } else { - FT6236_found=0; - } - - } -} - -#ifdef USE_TOUCH_BUTTONS -void ILI9488_MQTT(uint8_t count,const char *cp) { - ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7); - MqttPublishTeleSensor(); -} - -void ILI9488_RDW_BUTT(uint32_t count,uint32_t pwr) { - buttons[count]->xdrawButton(pwr); - if (pwr) buttons[count]->vpower|=0x80; - else buttons[count]->vpower&=0x7f; -} - -void FT6236Check() { -uint16_t temp; -uint8_t rbutt=0,vbutt=0; -ili9488_ctouch_counter++; -if (2 == ili9488_ctouch_counter) { - - ili9488_ctouch_counter=0; - if (FT6236readTouchLocation(&ili9488_pLoc,1)) { - - if (renderer) { - uint8_t rot=renderer->getRotation(); - switch (rot) { - case 0: - temp=ili9488_pLoc.y; - ili9488_pLoc.y=renderer->height()-ili9488_pLoc.x; - ili9488_pLoc.x=temp; - break; - case 1: - break; - case 2: - break; - case 3: - temp=ili9488_pLoc.y; - ili9488_pLoc.y=ili9488_pLoc.x; - ili9488_pLoc.x=renderer->width()-temp; - break; - } - - for (uint8_t count=0; countvpower&0x7f; - if (buttons[count]->contains(ili9488_pLoc.x,ili9488_pLoc.y)) { - - buttons[count]->press(true); - if (buttons[count]->justPressed()) { - if (!bflags) { - uint8_t pwr=bitRead(power,rbutt); - if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) { - ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON); - ILI9488_RDW_BUTT(count,!pwr); - } - } else { - - const char *cp; - if (bflags==1) { - - buttons[count]->vpower^=0x80; - cp="TBT"; - } else { - - buttons[count]->vpower|=0x80; - cp="PBT"; - } - buttons[count]->xdrawButton(buttons[count]->vpower&0x80); - ILI9488_MQTT(count,cp); - } - } - } - if (!bflags) { - rbutt++; - } else { - vbutt++; - } - } - } - } - } else { - - for (uint8_t count=0; countvpower&0x7f; - buttons[count]->press(false); - if (buttons[count]->justReleased()) { - uint8_t bflags=buttons[count]->vpower&0x7f; - if (bflags>0) { - if (bflags>1) { - - buttons[count]->vpower&=0x7f; - ILI9488_MQTT(count,"PBT"); - } - buttons[count]->xdrawButton(buttons[count]->vpower&0x80); - } - } - if (!bflags) { - - uint8_t pwr=bitRead(power,rbutt); - uint8_t vpwr=(buttons[count]->vpower&0x80)>>7; - if (pwr!=vpwr) { - ILI9488_RDW_BUTT(count,pwr); - } - rbutt++; - } - } - } - ili9488_pLoc.x=0; - ili9488_pLoc.y=0; - } -} -} -#endif - - - - -bool Xdsp08(uint8_t function) -{ - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - ILI9488_InitDriver(); - } - else if (XDSP_08 == Settings.display_model) { - - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_EVERY_50_MSECOND: -#ifdef USE_TOUCH_BUTTONS - if (FT6236_found) FT6236Check(); -#endif - break; - } - } - - return result; -} - -#endif -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_09_SSD1351.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_09_SSD1351.ino" -#ifdef USE_SPI -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_SSD1351 - -#define XDSP_09 9 - -#define COLORED 1 -#define UNCOLORED 0 - - - - -#define USE_TINY_FONT - -#include - -extern uint8_t *buffer; -extern uint8_t color_type; -SSD1351 *ssd1351; - - - -void SSD1351_InitDriver() { - if (!Settings.display_model) { - Settings.display_model = XDSP_09; - } - - if (XDSP_09 == Settings.display_model) { - - if (Settings.display_width != SSD1351_WIDTH) { - Settings.display_width = SSD1351_WIDTH; - } - if (Settings.display_height != SSD1351_HEIGHT) { - Settings.display_height = SSD1351_HEIGHT; - } - - buffer=0; - - - fg_color = SSD1351_WHITE; - bg_color = SSD1351_BLACK; - - - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]<99) && (pin[GPIO_SSPI_SCLK]<99)){ - ssd1351 = new SSD1351(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_SCLK]); - } else { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]<99) && (pin[GPIO_SPI_CLK]<99)){ - ssd1351 = new SSD1351(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_CLK]); - } else { - return; - } - } - - delay(100); - SPI.begin(); - ssd1351->begin(); - renderer = ssd1351; - renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); - renderer->dim(Settings.display_dimmer); - -#ifdef SHOW_SPLASH - - renderer->setTextFont(2); - renderer->setTextColor(SSD1351_WHITE,SSD1351_BLACK); - renderer->DrawStringAt(10, 60, "SSD1351", SSD1351_RED,0); - delay(1000); - -#endif - color_type = COLOR_COLOR; - } -} - -#ifdef USE_DISPLAY_MODES1TO5 - -void SSD1351PrintLog(void) -{ - disp_refresh--; - if (!disp_refresh) { - disp_refresh = Settings.display_refresh; - if (!disp_screen_buffer_cols) { DisplayAllocScreenBuffer(); } - - char* txt = DisplayLogBuffer('\370'); - if (txt != NULL) { - uint8_t last_row = Settings.display_rows -1; - - renderer->clearDisplay(); - renderer->setTextSize(Settings.display_size); - renderer->setCursor(0,0); - for (byte i = 0; i < last_row; i++) { - strlcpy(disp_screen_buffer[i], disp_screen_buffer[i +1], disp_screen_buffer_cols); - renderer->println(disp_screen_buffer[i]); - } - strlcpy(disp_screen_buffer[last_row], txt, disp_screen_buffer_cols); - DisplayFillScreen(last_row); - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "[%s]"), disp_screen_buffer[last_row]); - - renderer->println(disp_screen_buffer[last_row]); - renderer->Updateframe(); - } - } -} - -void SSD1351Time(void) -{ - char line[12]; - - renderer->clearDisplay(); - renderer->setTextSize(2); - renderer->setCursor(0, 0); - snprintf_P(line, sizeof(line), PSTR(" %02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d"), RtcTime.hour, RtcTime.minute, RtcTime.second); - renderer->println(line); - snprintf_P(line, sizeof(line), PSTR("%02d" D_MONTH_DAY_SEPARATOR "%02d" D_YEAR_MONTH_SEPARATOR "%04d"), RtcTime.day_of_month, RtcTime.month, RtcTime.year); - renderer->println(line); - renderer->Updateframe(); -} - -void SSD1351Refresh(void) -{ - if (Settings.display_mode) { - switch (Settings.display_mode) { - case 1: - SSD1351Time(); - break; - case 2: - case 3: - case 4: - case 5: - SSD1351PrintLog(); - break; - } - } -} - -#endif - - - - -bool Xdsp09(uint8_t function) -{ - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - SSD1351_InitDriver(); - } - else if (XDSP_09 == Settings.display_model) { - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; -#ifdef USE_DISPLAY_MODES1TO5 - case FUNC_DISPLAY_EVERY_SECOND: - SSD1351Refresh(); - break; -#endif - } - } - return result; -} -#endif -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_10_RA8876.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_10_RA8876.ino" -#ifdef USE_SPI -#ifdef USE_DISPLAY -#ifdef USE_DISPLAY_RA8876 - -#define XDSP_10 10 -#define XI2C_39 39 - -#define COLORED 1 -#define UNCOLORED 0 - - -#define FT5316_address 0x38 - - - -#define USE_TINY_FONT - -#include -#include - -TouchLocation ra8876_pLoc; -uint8_t ra8876_ctouch_counter = 0; - -#ifdef USE_TOUCH_BUTTONS -extern VButton *buttons[]; -#endif - -extern uint8_t *buffer; -extern uint8_t color_type; -RA8876 *ra8876; - -uint8_t FT5316_found; - - -void RA8876_InitDriver() -{ - if (!Settings.display_model) { - Settings.display_model = XDSP_10; - } - - if (XDSP_10 == Settings.display_model) { - - if (Settings.display_width != RA8876_TFTWIDTH) { - Settings.display_width = RA8876_TFTWIDTH; - } - if (Settings.display_height != RA8876_TFTHEIGHT) { - Settings.display_height = RA8876_TFTHEIGHT; - } - buffer=0; - - - fg_color = RA8876_WHITE; - bg_color = RA8876_BLACK; - - - if ((pin[GPIO_SSPI_CS]<99) && (pin[GPIO_SSPI_MOSI]==13) && (pin[GPIO_SSPI_MISO]==12) && (pin[GPIO_SSPI_SCLK]==14)) { - ra8876 = new RA8876(pin[GPIO_SSPI_CS],pin[GPIO_SSPI_MOSI],pin[GPIO_SSPI_MISO],pin[GPIO_SSPI_SCLK],pin[GPIO_BACKLIGHT]); - } else { - if ((pin[GPIO_SPI_CS]<99) && (pin[GPIO_SPI_MOSI]==13) && (pin[GPIO_SPI_MISO]==12) && (pin[GPIO_SPI_CLK]==14)) { - ra8876 = new RA8876(pin[GPIO_SPI_CS],pin[GPIO_SPI_MOSI],pin[GPIO_SPI_MISO],pin[GPIO_SPI_CLK],pin[GPIO_BACKLIGHT]); - } else { - return; - } - } - - ra8876->begin(); - renderer = ra8876; - renderer->DisplayInit(DISPLAY_INIT_MODE,Settings.display_size,Settings.display_rotate,Settings.display_font); - renderer->dim(Settings.display_dimmer); - - -#ifdef SHOW_SPLASH - - renderer->setTextFont(2); - renderer->setTextColor(RA8876_WHITE,RA8876_BLACK); - renderer->DrawStringAt(600, 300, "RA8876", RA8876_RED,0); - delay(1000); - -#endif - color_type = COLOR_COLOR; - - if (I2cEnabled(XI2C_39) && I2cSetDevice(FT5316_address)) { - FT6236begin(FT5316_address); - FT5316_found=1; - I2cSetActiveFound(FT5316_address, "FT5316"); - } else { - FT5316_found=0; - } - - } -} - -#ifdef USE_TOUCH_BUTTONS -void RA8876_MQTT(uint8_t count,const char *cp) { - ResponseTime_P(PSTR(",\"RA8876\":{\"%s%d\":\"%d\"}}"), cp,count+1,(buttons[count]->vpower&0x80)>>7); - MqttPublishTeleSensor(); -} - -void RA8876_RDW_BUTT(uint32_t count,uint32_t pwr) { - buttons[count]->xdrawButton(pwr); - if (pwr) buttons[count]->vpower|=0x80; - else buttons[count]->vpower&=0x7f; -} - - -void FT5316Check() { -uint16_t temp; -uint8_t rbutt=0,vbutt=0; -ra8876_ctouch_counter++; -if (2 == ra8876_ctouch_counter) { - - ra8876_ctouch_counter=0; - - if (FT6236readTouchLocation(&ra8876_pLoc,1)) { - ra8876_pLoc.x=ra8876_pLoc.x*RA8876_TFTWIDTH/800; - ra8876_pLoc.y=ra8876_pLoc.y*RA8876_TFTHEIGHT/480; - - - if (renderer) { - - - ra8876_pLoc.x=RA8876_TFTWIDTH-ra8876_pLoc.x; - ra8876_pLoc.y=RA8876_TFTHEIGHT-ra8876_pLoc.y; -# 170 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_10_RA8876.ino" - for (uint8_t count=0; countvpower&0x7f; - if (buttons[count]->contains(ra8876_pLoc.x,ra8876_pLoc.y)) { - - buttons[count]->press(true); - if (buttons[count]->justPressed()) { - if (!bflags) { - - uint8_t pwr=bitRead(power,rbutt); - if (!SendKey(KEY_BUTTON, rbutt+1, POWER_TOGGLE)) { - ExecuteCommandPower(rbutt+1, POWER_TOGGLE, SRC_BUTTON); - RA8876_RDW_BUTT(count,!pwr); - } - } else { - - const char *cp; - if (bflags==1) { - - buttons[count]->vpower^=0x80; - cp="TBT"; - } else { - - buttons[count]->vpower|=0x80; - cp="PBT"; - } - buttons[count]->xdrawButton(buttons[count]->vpower&0x80); - RA8876_MQTT(count,cp); - } - } - } - if (!bflags) { - rbutt++; - } else { - vbutt++; - } - } - } - } - } else { - - for (uint8_t count=0; countvpower&0x7f; - buttons[count]->press(false); - if (buttons[count]->justReleased()) { - if (bflags>0) { - if (bflags>1) { - - buttons[count]->vpower&=0x7f; - RA8876_MQTT(count,"PBT"); - } - buttons[count]->xdrawButton(buttons[count]->vpower&0x80); - } - } - if (!bflags) { - - uint8_t pwr=bitRead(power,rbutt); - uint8_t vpwr=(buttons[count]->vpower&0x80)>>7; - if (pwr!=vpwr) { - RA8876_RDW_BUTT(count,pwr); - } - rbutt++; - } - } - } - ra8876_pLoc.x=0; - ra8876_pLoc.y=0; - } -} -} -#endif -# 426 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_10_RA8876.ino" -bool Xdsp10(uint8_t function) -{ - bool result = false; - - if (FUNC_DISPLAY_INIT_DRIVER == function) { - RA8876_InitDriver(); - } - else if (XDSP_10 == Settings.display_model) { - switch (function) { - case FUNC_DISPLAY_MODEL: - result = true; - break; - case FUNC_DISPLAY_EVERY_50_MSECOND: -#ifdef USE_TOUCH_BUTTONS - if (FT5316_found) FT5316Check(); -#endif - break; - } - } - return result; -} -#endif -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_interface.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_interface.ino" -#ifdef USE_DISPLAY - -#ifdef XFUNC_PTR_IN_ROM -bool (* const xdsp_func_ptr[])(uint8_t) PROGMEM = { -#else -bool (* const xdsp_func_ptr[])(uint8_t) = { -#endif - -#ifdef XDSP_01 - &Xdsp01, -#endif - -#ifdef XDSP_02 - &Xdsp02, -#endif - -#ifdef XDSP_03 - &Xdsp03, -#endif - -#ifdef XDSP_04 - &Xdsp04, -#endif - -#ifdef XDSP_05 - &Xdsp05, -#endif - -#ifdef XDSP_06 - &Xdsp06, -#endif - -#ifdef XDSP_07 - &Xdsp07, -#endif - -#ifdef XDSP_08 - &Xdsp08, -#endif - -#ifdef XDSP_09 - &Xdsp09, -#endif - -#ifdef XDSP_10 - &Xdsp10, -#endif - -#ifdef XDSP_11 - &Xdsp11, -#endif - -#ifdef XDSP_12 - &Xdsp12, -#endif - -#ifdef XDSP_13 - &Xdsp13, -#endif - -#ifdef XDSP_14 - &Xdsp14, -#endif - -#ifdef XDSP_15 - &Xdsp15, -#endif - -#ifdef XDSP_16 - &Xdsp16 -#endif -}; - -const uint8_t xdsp_present = sizeof(xdsp_func_ptr) / sizeof(xdsp_func_ptr[0]); -# 117 "C:/shared/sonoff/Git/Tasmota/tasmota/xdsp_interface.ino" -uint8_t XdspPresent(void) -{ - return xdsp_present; -} - -bool XdspCall(uint8_t Function) -{ - bool result = false; - - DEBUG_TRACE_LOG(PSTR("DSP: %d"), Function); - - for (uint32_t x = 0; x < xdsp_present; x++) { - result = xdsp_func_ptr[x](Function); - - if (result && (FUNC_DISPLAY_MODEL == Function)) { - break; - } - } - - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_01_ws2812.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_01_ws2812.ino" -#ifdef USE_LIGHT -#ifdef USE_WS2812 -# 38 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_01_ws2812.ino" -#define XLGT_01 1 - -const uint8_t WS2812_SCHEMES = 8; - -const char kWs2812Commands[] PROGMEM = "|" - D_CMND_LED "|" D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_WIDTH ; - -void (* const Ws2812Command[])(void) PROGMEM = { - &CmndLed, &CmndPixels, &CmndRotation, &CmndWidth }; - -#include - -#if (USE_WS2812_CTYPE == NEO_GRB) - typedef NeoGrbFeature selectedNeoFeatureType; -#elif (USE_WS2812_CTYPE == NEO_BRG) - typedef NeoBrgFeature selectedNeoFeatureType; -#elif (USE_WS2812_CTYPE == NEO_RBG) - typedef NeoRbgFeature selectedNeoFeatureType; -#elif (USE_WS2812_CTYPE == NEO_RGBW) - typedef NeoRgbwFeature selectedNeoFeatureType; -#elif (USE_WS2812_CTYPE == NEO_GRBW) - typedef NeoGrbwFeature selectedNeoFeatureType; -#else - typedef NeoRgbFeature selectedNeoFeatureType; -#endif - -#ifdef USE_WS2812_DMA - - -#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) - typedef NeoEsp8266DmaWs2812xMethod selectedNeoSpeedType; -#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) - typedef NeoEsp8266DmaSk6812Method selectedNeoSpeedType; -#elif (USE_WS2812_HARDWARE == NEO_HW_APA106) - typedef NeoEsp8266DmaApa106Method selectedNeoSpeedType; -#else - typedef NeoEsp8266Dma800KbpsMethod selectedNeoSpeedType; -#endif - -#else - - -#if (USE_WS2812_HARDWARE == NEO_HW_WS2812X) - typedef NeoEsp8266BitBangWs2812xMethod selectedNeoSpeedType; -#elif (USE_WS2812_HARDWARE == NEO_HW_SK6812) - typedef NeoEsp8266BitBangSk6812Method selectedNeoSpeedType; -#else - typedef NeoEsp8266BitBang800KbpsMethod selectedNeoSpeedType; -#endif - -#endif - -NeoPixelBus *strip = nullptr; - -struct WsColor { - uint8_t red, green, blue; -}; - -struct ColorScheme { - WsColor* colors; - uint8_t count; -}; - -WsColor kIncandescent[2] = { 255,140,20, 0,0,0 }; -WsColor kRgb[3] = { 255,0,0, 0,255,0, 0,0,255 }; -WsColor kChristmas[2] = { 255,0,0, 0,255,0 }; -WsColor kHanukkah[2] = { 0,0,255, 255,255,255 }; -WsColor kwanzaa[3] = { 255,0,0, 0,0,0, 0,255,0 }; -WsColor kRainbow[7] = { 255,0,0, 255,128,0, 255,255,0, 0,255,0, 0,0,255, 128,0,255, 255,0,255 }; -WsColor kFire[3] = { 255,0,0, 255,102,0, 255,192,0 }; -ColorScheme kSchemes[WS2812_SCHEMES -1] = { - kIncandescent, 2, - kRgb, 3, - kChristmas, 2, - kHanukkah, 2, - kwanzaa, 3, - kRainbow, 7, - kFire, 3 }; - -uint8_t kWidth[5] = { - 1, - 2, - 4, - 8, - 255 }; -uint8_t kWsRepeat[5] = { - 8, - 6, - 4, - 2, - 1 }; - -struct WS2812 { - uint8_t show_next = 1; - uint8_t scheme_offset = 0; - bool suspend_update = false; -} Ws2812; - - - -void Ws2812StripShow(void) -{ -#if (USE_WS2812_CTYPE > NEO_3LED) - RgbwColor c; -#else - RgbColor c; -#endif - - if (Settings.light_correction) { - for (uint32_t i = 0; i < Settings.light_pixels; i++) { - c = strip->GetPixelColor(i); - c.R = ledGamma(c.R); - c.G = ledGamma(c.G); - c.B = ledGamma(c.B); -#if (USE_WS2812_CTYPE > NEO_3LED) - c.W = ledGamma(c.W); -#endif - strip->SetPixelColor(i, c); - } - } - strip->Show(); -} - -int mod(int a, int b) -{ - int ret = a % b; - if (ret < 0) ret += b; - return ret; -} - -void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offset) -{ -#if (USE_WS2812_CTYPE > NEO_3LED) - RgbwColor color; -#else - RgbColor color; -#endif - - uint32_t mod_position = mod(position, (int)Settings.light_pixels); - - color = strip->GetPixelColor(mod_position); - float dimmer = 100 / (float)Settings.light_dimmer; - color.R = tmin(color.R + ((hand_color.red / dimmer) * offset), 255); - color.G = tmin(color.G + ((hand_color.green / dimmer) * offset), 255); - color.B = tmin(color.B + ((hand_color.blue / dimmer) * offset), 255); - strip->SetPixelColor(mod_position, color); -} - -void Ws2812UpdateHand(int position, uint32_t index) -{ - uint32_t width = Settings.light_width; - if (index < WS_MARKER) { width = Settings.ws_width[index]; } - if (!width) { return; } - - position = (position + Settings.light_rotation) % Settings.light_pixels; - - if (Settings.flag.ws_clock_reverse) { - position = Settings.light_pixels -position; - } - WsColor hand_color = { Settings.ws_color[index][WS_RED], Settings.ws_color[index][WS_GREEN], Settings.ws_color[index][WS_BLUE] }; - - Ws2812UpdatePixelColor(position, hand_color, 1); - - uint32_t range = ((width -1) / 2) +1; - for (uint32_t h = 1; h < range; h++) { - float offset = (float)(range - h) / (float)range; - Ws2812UpdatePixelColor(position -h, hand_color, offset); - Ws2812UpdatePixelColor(position +h, hand_color, offset); - } -} - -void Ws2812Clock(void) -{ - strip->ClearTo(0); - int clksize = 60000 / (int)Settings.light_pixels; - - Ws2812UpdateHand((RtcTime.second * 1000) / clksize, WS_SECOND); - Ws2812UpdateHand((RtcTime.minute * 1000) / clksize, WS_MINUTE); - Ws2812UpdateHand((((RtcTime.hour % 12) * 5000) + ((RtcTime.minute * 1000) / 12 )) / clksize, WS_HOUR); - if (Settings.ws_color[WS_MARKER][WS_RED] + Settings.ws_color[WS_MARKER][WS_GREEN] + Settings.ws_color[WS_MARKER][WS_BLUE]) { - for (uint32_t i = 0; i < 12; i++) { - Ws2812UpdateHand((i * 5000) / clksize, WS_MARKER); - } - } - - Ws2812StripShow(); -} - -void Ws2812GradientColor(uint32_t schemenr, struct WsColor* mColor, uint32_t range, uint32_t gradRange, uint32_t i) -{ - - - - - ColorScheme scheme = kSchemes[schemenr]; - uint32_t curRange = i / range; - uint32_t rangeIndex = i % range; - uint32_t colorIndex = rangeIndex / gradRange; - uint32_t start = colorIndex; - uint32_t end = colorIndex +1; - if (curRange % 2 != 0) { - start = (scheme.count -1) - start; - end = (scheme.count -1) - end; - } - float dimmer = 100 / (float)Settings.light_dimmer; - float fmyRed = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / dimmer; - float fmyGrn = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / dimmer; - float fmyBlu = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / dimmer; - mColor->red = (uint8_t)fmyRed; - mColor->green = (uint8_t)fmyGrn; - mColor->blue = (uint8_t)fmyBlu; -} - -void Ws2812Gradient(uint32_t schemenr) -{ - - - - - -#if (USE_WS2812_CTYPE > NEO_3LED) - RgbwColor c; - c.W = 0; -#else - RgbColor c; -#endif - - ColorScheme scheme = kSchemes[schemenr]; - if (scheme.count < 2) { return; } - - uint32_t repeat = kWsRepeat[Settings.light_width]; - uint32_t range = (uint32_t)ceil((float)Settings.light_pixels / (float)repeat); - uint32_t gradRange = (uint32_t)ceil((float)range / (float)(scheme.count - 1)); - uint32_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); - uint32_t offset = speed > 0 ? Light.strip_timer_counter / speed : 0; - - WsColor oldColor, currentColor; - Ws2812GradientColor(schemenr, &oldColor, range, gradRange, offset); - currentColor = oldColor; - for (uint32_t i = 0; i < Settings.light_pixels; i++) { - if (kWsRepeat[Settings.light_width] > 1) { - Ws2812GradientColor(schemenr, ¤tColor, range, gradRange, i +offset); - } - if (Settings.light_speed > 0) { - - c.R = map(Light.strip_timer_counter % speed, 0, speed, oldColor.red, currentColor.red); - c.G = map(Light.strip_timer_counter % speed, 0, speed, oldColor.green, currentColor.green); - c.B = map(Light.strip_timer_counter % speed, 0, speed, oldColor.blue, currentColor.blue); - } - else { - - c.R = currentColor.red; - c.G = currentColor.green; - c.B = currentColor.blue; - } - strip->SetPixelColor(i, c); - oldColor = currentColor; - } - Ws2812StripShow(); -} - -void Ws2812Bars(uint32_t schemenr) -{ - - - - - -#if (USE_WS2812_CTYPE > NEO_3LED) - RgbwColor c; - c.W = 0; -#else - RgbColor c; -#endif - - ColorScheme scheme = kSchemes[schemenr]; - - uint32_t maxSize = Settings.light_pixels / scheme.count; - if (kWidth[Settings.light_width] > maxSize) { maxSize = 0; } - - uint32_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10); - uint32_t offset = (speed > 0) ? Light.strip_timer_counter / speed : 0; - - WsColor mcolor[scheme.count]; - memcpy(mcolor, scheme.colors, sizeof(mcolor)); - float dimmer = 100 / (float)Settings.light_dimmer; - for (uint32_t i = 0; i < scheme.count; i++) { - float fmyRed = (float)mcolor[i].red / dimmer; - float fmyGrn = (float)mcolor[i].green / dimmer; - float fmyBlu = (float)mcolor[i].blue / dimmer; - mcolor[i].red = (uint8_t)fmyRed; - mcolor[i].green = (uint8_t)fmyGrn; - mcolor[i].blue = (uint8_t)fmyBlu; - } - uint32_t colorIndex = offset % scheme.count; - for (uint32_t i = 0; i < Settings.light_pixels; i++) { - if (maxSize) { colorIndex = ((i + offset) % (scheme.count * kWidth[Settings.light_width])) / kWidth[Settings.light_width]; } - c.R = mcolor[colorIndex].red; - c.G = mcolor[colorIndex].green; - c.B = mcolor[colorIndex].blue; - strip->SetPixelColor(i, c); - } - Ws2812StripShow(); -} - -void Ws2812Clear(void) -{ - strip->ClearTo(0); - strip->Show(); - Ws2812.show_next = 1; -} - -void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) -{ -#if (USE_WS2812_CTYPE > NEO_3LED) - RgbwColor lcolor; - lcolor.W = white; -#else - RgbColor lcolor; -#endif - - lcolor.R = red; - lcolor.G = green; - lcolor.B = blue; - if (led) { - strip->SetPixelColor(led -1, lcolor); - } else { - - for (uint32_t i = 0; i < Settings.light_pixels; i++) { - strip->SetPixelColor(i, lcolor); - } - } - - if (!Ws2812.suspend_update) { - strip->Show(); - Ws2812.show_next = 1; - } -} - -char* Ws2812GetColor(uint32_t led, char* scolor) -{ - uint8_t sl_ledcolor[4]; - - #if (USE_WS2812_CTYPE > NEO_3LED) - RgbwColor lcolor = strip->GetPixelColor(led -1); - sl_ledcolor[3] = lcolor.W; - #else - RgbColor lcolor = strip->GetPixelColor(led -1); - #endif - sl_ledcolor[0] = lcolor.R; - sl_ledcolor[1] = lcolor.G; - sl_ledcolor[2] = lcolor.B; - scolor[0] = '\0'; - for (uint32_t i = 0; i < Light.subtype; i++) { - if (Settings.flag.decimal_text) { - snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", sl_ledcolor[i]); - } else { - snprintf_P(scolor, 25, PSTR("%s%02X"), scolor, sl_ledcolor[i]); - } - } - return scolor; -} - - - - - -void Ws2812ForceSuspend (void) -{ - Ws2812.suspend_update = true; -} - -void Ws2812ForceUpdate (void) -{ - Ws2812.suspend_update = false; - strip->Show(); - Ws2812.show_next = 1; -} - - - -bool Ws2812SetChannels(void) -{ - uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; - - Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); - - return true; -} - -void Ws2812ShowScheme(void) -{ - uint32_t scheme = Settings.light_scheme - Ws2812.scheme_offset; - - switch (scheme) { - case 0: - if ((1 == state_250mS) || (Ws2812.show_next)) { - Ws2812Clock(); - Ws2812.show_next = 0; - } - break; - default: - if (1 == Settings.light_fade) { - Ws2812Gradient(scheme -1); - } else { - Ws2812Bars(scheme -1); - } - Ws2812.show_next = 1; - break; - } -} - -void Ws2812ModuleSelected(void) -{ - if (pin[GPIO_WS2812] < 99) { - - - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); - strip->Begin(); - - Ws2812Clear(); - - Ws2812.scheme_offset = Light.max_scheme +1; - Light.max_scheme += WS2812_SCHEMES; - -#if (USE_WS2812_CTYPE > NEO_3LED) - light_type = LT_RGBW; -#else - light_type = LT_RGB; -#endif - light_flg = XLGT_01; - } -} - - - -void CmndLed(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Settings.light_pixels)) { - if (XdrvMailbox.data_len > 0) { - char *p; - uint16_t idx = XdrvMailbox.index; - Ws2812ForceSuspend(); - for (char *color = strtok_r(XdrvMailbox.data, " ", &p); color; color = strtok_r(nullptr, " ", &p)) { - if (LightColorEntry(color, strlen(color))) { - Ws2812SetColor(idx, Light.entry_color[0], Light.entry_color[1], Light.entry_color[2], Light.entry_color[3]); - idx++; - if (idx > Settings.light_pixels) { break; } - } else { - break; - } - } - Ws2812ForceUpdate(); - } - char scolor[LIGHT_COLOR_SIZE]; - ResponseCmndIdxChar(Ws2812GetColor(XdrvMailbox.index, scolor)); - } -} - -void CmndPixels(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= WS2812_MAX_LEDS)) { - Settings.light_pixels = XdrvMailbox.payload; - Settings.light_rotation = 0; - Ws2812Clear(); - Light.update = true; - } - ResponseCmndNumber(Settings.light_pixels); -} - -void CmndRotation(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < Settings.light_pixels)) { - Settings.light_rotation = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.light_rotation); -} - -void CmndWidth(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { - if (1 == XdrvMailbox.index) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 4)) { - Settings.light_width = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.light_width); - } else { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32)) { - Settings.ws_width[XdrvMailbox.index -2] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.ws_width[XdrvMailbox.index -2]); - } - } -} - - - - - -bool Xlgt01(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_SET_CHANNELS: - result = Ws2812SetChannels(); - break; - case FUNC_SET_SCHEME: - Ws2812ShowScheme(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kWs2812Commands, Ws2812Command); - break; - case FUNC_MODULE_INIT: - Ws2812ModuleSelected(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_02_my92x1.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_02_my92x1.ino" -#ifdef USE_LIGHT -#ifdef USE_MY92X1 - - - - -#define XLGT_02 2 - -struct MY92X1 { - uint8_t pdi_pin = 0; - uint8_t pdcki_pin = 0; - uint8_t model = 0; -} My92x1; - -extern "C" { - void os_delay_us(unsigned int); -} - -void LightDiPulse(uint8_t times) -{ - for (uint32_t i = 0; i < times; i++) { - digitalWrite(My92x1.pdi_pin, HIGH); - digitalWrite(My92x1.pdi_pin, LOW); - } -} - -void LightDckiPulse(uint8_t times) -{ - for (uint32_t i = 0; i < times; i++) { - digitalWrite(My92x1.pdcki_pin, HIGH); - digitalWrite(My92x1.pdcki_pin, LOW); - } -} - -void LightMy92x1Write(uint8_t data) -{ - for (uint32_t i = 0; i < 4; i++) { - digitalWrite(My92x1.pdcki_pin, LOW); - digitalWrite(My92x1.pdi_pin, (data & 0x80)); - digitalWrite(My92x1.pdcki_pin, HIGH); - data = data << 1; - digitalWrite(My92x1.pdi_pin, (data & 0x80)); - digitalWrite(My92x1.pdcki_pin, LOW); - digitalWrite(My92x1.pdi_pin, LOW); - data = data << 1; - } -} - -void LightMy92x1Init(void) -{ - uint8_t chips[3] = { 1, 2, 2 }; - - LightDckiPulse(chips[My92x1.model] * 32); - os_delay_us(12); - - - LightDiPulse(12); - os_delay_us(12); - for (uint32_t n = 0; n < chips[My92x1.model]; n++) { - LightMy92x1Write(0x18); - } - os_delay_us(12); - - - LightDiPulse(16); - os_delay_us(12); -} - -void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty_w, uint8_t duty_c) -{ - uint8_t channels[3] = { 4, 6, 6 }; - - uint8_t duty[3][6] = {{ duty_r, duty_g, duty_b, duty_w, 0, 0 }, - { duty_w, duty_c, 0, duty_g, duty_r, duty_b }, - { duty_r, duty_g, duty_b, duty_w, duty_w, duty_w }}; - - os_delay_us(12); - for (uint32_t channel = 0; channel < channels[My92x1.model]; channel++) { - LightMy92x1Write(duty[My92x1.model][channel]); - } - os_delay_us(12); - LightDiPulse(8); - os_delay_us(12); -} - - - -bool My92x1SetChannels(void) -{ - uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; - - LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); - - return true; -} - -void My92x1ModuleSelected(void) -{ - if ((pin[GPIO_DCKI] < 99) && (pin[GPIO_DI] < 99)) { - My92x1.pdi_pin = pin[GPIO_DI]; - My92x1.pdcki_pin = pin[GPIO_DCKI]; - - pinMode(My92x1.pdi_pin, OUTPUT); - pinMode(My92x1.pdcki_pin, OUTPUT); - digitalWrite(My92x1.pdi_pin, LOW); - digitalWrite(My92x1.pdcki_pin, LOW); - - My92x1.model = 2; - light_type = LT_RGBW; - if (AILIGHT == my_module_type) { - My92x1.model = 0; - - } - else if (SONOFF_B1 == my_module_type) { - My92x1.model = 1; - light_type = LT_RGBWC; - } - - LightMy92x1Init(); - - light_flg = XLGT_02; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: MY29x1 Found")); - } -} - - - - - -bool Xlgt02(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_SET_CHANNELS: - result = My92x1SetChannels(); - break; - case FUNC_MODULE_INIT: - My92x1ModuleSelected(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_03_sm16716.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_03_sm16716.ino" -#ifdef USE_LIGHT -#ifdef USE_SM16716 - - - - - - - -#define XLGT_03 3 - -#define D_LOG_SM16716 "SM16716: " - -struct SM16716 { - uint8_t pin_clk = 0; - uint8_t pin_dat = 0; - uint8_t pin_sel = 0; - bool enabled = false; -} Sm16716; - -void SM16716_SendBit(uint8_t v) -{ - - - - - - digitalWrite(Sm16716.pin_dat, (v != 0) ? HIGH : LOW); - - digitalWrite(Sm16716.pin_clk, HIGH); - - digitalWrite(Sm16716.pin_clk, LOW); -} - -void SM16716_SendByte(uint8_t v) -{ - uint8_t mask; - - for (mask = 0x80; mask; mask >>= 1) { - SM16716_SendBit(v & mask); - } -} - -void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b) -{ - if (Sm16716.pin_sel < 99) { - bool should_enable = (duty_r | duty_g | duty_b); - if (!Sm16716.enabled && should_enable) { - DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color on")); - Sm16716.enabled = true; - digitalWrite(Sm16716.pin_sel, HIGH); - - - delayMicroseconds(1000); - SM16716_Init(); - } - else if (Sm16716.enabled && !should_enable) { - DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color off")); - Sm16716.enabled = false; - digitalWrite(Sm16716.pin_sel, LOW); - } - } - DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), duty_r, duty_g, duty_b); - - - SM16716_SendBit(1); - SM16716_SendByte(duty_r); - SM16716_SendByte(duty_g); - SM16716_SendByte(duty_b); - - - - - - SM16716_SendBit(0); - SM16716_SendByte(0); - SM16716_SendByte(0); - SM16716_SendByte(0); -} -# 111 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_03_sm16716.ino" -void SM16716_Init(void) -{ - for (uint32_t t_init = 0; t_init < 50; ++t_init) { - SM16716_SendBit(0); - } -} - - - -bool Sm16716SetChannels(void) -{ -# 132 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_03_sm16716.ino" - uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; - - SM16716_Update(cur_col[0], cur_col[1], cur_col[2]); - - return true; -} - -void Sm16716ModuleSelected(void) -{ - if ((pin[GPIO_SM16716_CLK] < 99) && (pin[GPIO_SM16716_DAT] < 99)) { - Sm16716.pin_clk = pin[GPIO_SM16716_CLK]; - Sm16716.pin_dat = pin[GPIO_SM16716_DAT]; - Sm16716.pin_sel = pin[GPIO_SM16716_SEL]; -# 157 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_03_sm16716.ino" - pinMode(Sm16716.pin_clk, OUTPUT); - digitalWrite(Sm16716.pin_clk, LOW); - - pinMode(Sm16716.pin_dat, OUTPUT); - digitalWrite(Sm16716.pin_dat, LOW); - - if (Sm16716.pin_sel < 99) { - pinMode(Sm16716.pin_sel, OUTPUT); - digitalWrite(Sm16716.pin_sel, LOW); - - } else { - - SM16716_Init(); - } - - LightPwmOffset(LST_RGB); - light_type += LST_RGB; - light_flg = XLGT_03; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM16716 Found")); - } -} - - - - - -bool Xlgt03(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_SET_CHANNELS: - result = Sm16716SetChannels(); - break; - case FUNC_MODULE_INIT: - Sm16716ModuleSelected(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_04_sm2135.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_04_sm2135.ino" -#ifdef USE_LIGHT -#ifdef USE_SM2135 - - - - - - -#define XLGT_04 4 - -#define SM2135_ADDR_MC 0xC0 -#define SM2135_ADDR_CH 0xC1 -#define SM2135_ADDR_R 0xC2 -#define SM2135_ADDR_G 0xC3 -#define SM2135_ADDR_B 0xC4 -#define SM2135_ADDR_C 0xC5 -#define SM2135_ADDR_W 0xC6 - -#define SM2135_RGB 0x00 -#define SM2135_CW 0x80 - -#define SM2135_10MA 0x00 -#define SM2135_15MA 0x01 -#define SM2135_20MA 0x02 -#define SM2135_25MA 0x03 -#define SM2135_30MA 0x04 -#define SM2135_35MA 0x05 -#define SM2135_40MA 0x06 -#define SM2135_45MA 0x07 -#define SM2135_50MA 0x08 -#define SM2135_55MA 0x09 -#define SM2135_60MA 0x0A - - -const uint8_t SM2135_CURRENT = (SM2135_20MA << 4) | SM2135_15MA; - -struct SM2135 { - uint8_t clk = 0; - uint8_t data = 0; -} Sm2135; - -uint8_t Sm2135Write(uint8_t data) -{ - for (uint32_t i = 0; i < 8; i++) { - digitalWrite(Sm2135.clk, LOW); - digitalWrite(Sm2135.data, (data & 0x80)); - digitalWrite(Sm2135.clk, HIGH); - data = data << 1; - } - digitalWrite(Sm2135.clk, LOW); - digitalWrite(Sm2135.data, HIGH); - pinMode(Sm2135.data, INPUT); - digitalWrite(Sm2135.clk, HIGH); - uint8_t ack = digitalRead(Sm2135.data); - pinMode(Sm2135.data, OUTPUT); - return ack; -} - -void Sm2135Send(uint8_t *buffer, uint8_t size) -{ - digitalWrite(Sm2135.data, LOW); - for (uint32_t i = 0; i < size; i++) { - Sm2135Write(buffer[i]); - } - digitalWrite(Sm2135.clk, LOW); - digitalWrite(Sm2135.clk, HIGH); - digitalWrite(Sm2135.data, HIGH); -} - - - -bool Sm2135SetChannels(void) -{ - uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; - uint8_t data[6]; - - if ((0 == cur_col[0]) && (0 == cur_col[1]) && (0 == cur_col[2])) { -# 106 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_04_sm2135.ino" - data[0] = SM2135_ADDR_MC; - data[1] = SM2135_CURRENT; - data[2] = SM2135_CW; - Sm2135Send(data, 3); - delay(1); - data[0] = SM2135_ADDR_C; - data[1] = cur_col[4]; - data[2] = cur_col[3]; - Sm2135Send(data, 3); - } else { - - - - - - - - data[0] = SM2135_ADDR_MC; - data[1] = SM2135_CURRENT; - data[2] = SM2135_RGB; - data[3] = cur_col[1]; - data[4] = cur_col[0]; - data[5] = cur_col[2]; - Sm2135Send(data, 6); - } - - return true; -} - -void Sm2135ModuleSelected(void) -{ - if ((pin[GPIO_SM2135_CLK] < 99) && (pin[GPIO_SM2135_DAT] < 99)) { - Sm2135.clk = pin[GPIO_SM2135_CLK]; - Sm2135.data = pin[GPIO_SM2135_DAT]; - - pinMode(Sm2135.data, OUTPUT); - digitalWrite(Sm2135.data, HIGH); - pinMode(Sm2135.clk, OUTPUT); - digitalWrite(Sm2135.clk, HIGH); - - light_type = LT_RGBWC; - light_flg = XLGT_04; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM2135 Found")); - } -} - - - - - -bool Xlgt04(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_SET_CHANNELS: - result = Sm2135SetChannels(); - break; - case FUNC_MODULE_INIT: - Sm2135ModuleSelected(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_05_sonoff_l1.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_05_sonoff_l1.ino" -#ifdef USE_LIGHT -#ifdef USE_SONOFF_L1 - - - - -#define XLGT_05 5 - -#define SONOFF_L1_BUFFER_SIZE 140 - -#define SONOFF_L1_MODE_COLORFUL 1 -#define SONOFF_L1_MODE_COLORFUL_GRADIENT 2 -#define SONOFF_L1_MODE_COLORFUL_BREATH 3 -#define SONOFF_L1_MODE_DIY_GRADIENT 4 -#define SONOFF_L1_MODE_DIY_PULSE 5 -#define SONOFF_L1_MODE_DIY_BREATH 6 -#define SONOFF_L1_MODE_DIY_STROBE 7 -#define SONOFF_L1_MODE_RGB_GRADIENT 8 -#define SONOFF_L1_MODE_RGB_PULSE 9 -#define SONOFF_L1_MODE_RGB_BREATH 10 -#define SONOFF_L1_MODE_RGB_STROBE 11 -#define SONOFF_L1_MODE_SYNC_TO_MUSIC 12 - -struct SNFL1 { - uint32_t unlock = 0; - bool receive_ready = true; -} Snfl1; - - - -void SnfL1Send(const char *buffer) -{ - - - Serial.print(buffer); - Serial.write(0x1B); - Serial.flush(); -} - -void SnfL1SerialSendOk(void) -{ - char buffer[16]; - snprintf_P(buffer, sizeof(buffer), PSTR("AT+SEND=ok")); - - SnfL1Send(buffer); -} - -bool SnfL1SerialInput(void) -{ - if (serial_in_byte != 0x1B) { - if (serial_in_byte_counter >= 140) { - serial_in_byte_counter = 0; - } - if (serial_in_byte_counter || (!serial_in_byte_counter && ('A' == serial_in_byte))) { - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; - } - } else { - serial_in_buffer[serial_in_byte_counter++] = 0x00; - - - - - - - if (!strncmp(serial_in_buffer +3, "RESULT", 6)) { - Snfl1.receive_ready = true; - } - else if (!strncmp(serial_in_buffer +3, "UPDATE", 6)) { - char cmnd_dimmer[20]; - char cmnd_color[20]; - char *end_str; - char *string = serial_in_buffer +10; - char *token = strtok_r(string, ",", &end_str); - - bool color_updated[3] = { false, false, false }; - uint8_t current_color[3]; - memcpy(current_color, Settings.light_color, 3); - - bool switch_state = false; - bool is_power_change = false; - bool is_color_change = false; - bool is_brightness_change = false; - - while (token != nullptr) { - char* end_token; - char* token2 = strtok_r(token, ":", &end_token); - char* token3 = strtok_r(nullptr, ":", &end_token); - - if (!strncmp(token2, "\"sequence\"", 10)) { - - - - token = nullptr; - } - - else if (!strncmp(token2, "\"switch\"", 8)) { - switch_state = !strncmp(token3, "\"on\"", 4) ? true : false; - - - - is_power_change = (switch_state != Light.power); - } - - else if (!strncmp(token2, "\"color", 6)) { - char color_channel_name = token2[6]; - int color_index; - switch(color_channel_name) - { - case 'R': color_index = 0; - break; - case 'G': color_index = 1; - break; - case 'B': color_index = 2; - break; - } - int color_value = atoi(token3); - current_color[color_index] = color_value; - color_updated[color_index] = true; - - bool all_color_channels_updated = color_updated[0] && color_updated[1] && color_updated[2]; - if (all_color_channels_updated) { - - - - - - is_color_change = (Light.power && (memcmp(current_color, Settings.light_color, 3) != 0)); - } - snprintf_P(cmnd_color, sizeof(cmnd_color), PSTR(D_CMND_COLOR "2 %02x%02x%02x"), current_color[0], current_color[1], current_color[2]); - } - - else if (!strncmp(token2, "\"bright\"", 8)) { - uint8_t dimmer = atoi(token3); - - - - is_brightness_change = (Light.power && (dimmer > 0) && (dimmer != Settings.light_dimmer)); - snprintf_P(cmnd_dimmer, sizeof(cmnd_dimmer), PSTR(D_CMND_DIMMER " %d"), dimmer); - } - - token = strtok_r(nullptr, ",", &end_str); - } - - if (is_power_change) { - if (Settings.light_scheme > 0) { - if (!switch_state) { - char cmnd_scheme[20]; - snprintf_P(cmnd_scheme, sizeof(cmnd_scheme), PSTR(D_CMND_SCHEME " 0")); - ExecuteCommand(cmnd_scheme, SRC_SWITCH); - } - } else { - ExecuteCommandPower(1, switch_state, SRC_SWITCH); - } - } - else if (is_brightness_change) { - ExecuteCommand(cmnd_dimmer, SRC_SWITCH); - } - else if (Light.power && is_color_change) { - if (0 == Settings.light_scheme) { - if (Settings.light_fade) { - char cmnd_fade[20]; - snprintf_P(cmnd_fade, sizeof(cmnd_fade), PSTR(D_CMND_FADE " 0")); - ExecuteCommand(cmnd_fade, SRC_SWITCH); - } - ExecuteCommand(cmnd_color, SRC_SWITCH); - } - } - } - - SnfL1SerialSendOk(); - - return true; - } - serial_in_byte = 0; - return false; -} - - - -bool SnfL1SetChannels(void) -{ - if (Snfl1.receive_ready || TimeReached(Snfl1.unlock)) { - - uint8_t *scale_col = (uint8_t*)XdrvMailbox.topic; - - char buffer[140]; - snprintf_P(buffer, sizeof(buffer), PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d"), - LocalTime(), millis()%1000, - Light.power ? "on" : "off", - scale_col[0], scale_col[1], scale_col[2], - light_state.getDimmer(), - SONOFF_L1_MODE_COLORFUL); - - SnfL1Send(buffer); - - Snfl1.unlock = millis() + 500; - Snfl1.receive_ready = false; - } - return true; -} - -void SnfL1ModuleSelected(void) -{ - if (SONOFF_L1 == my_module_type) { - if ((pin[GPIO_RXD] < 99) && (pin[GPIO_TXD] < 99)) { - SetSerial(19200, TS_SERIAL_8N1); - - light_type = LT_RGB; - light_flg = XLGT_05; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("LGT: Sonoff L1 Found")); - } - } -} - - - - - -bool Xlgt05(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_SERIAL: - result = SnfL1SerialInput(); - break; - case FUNC_SET_CHANNELS: - result = SnfL1SetChannels(); - break; - case FUNC_MODULE_INIT: - SnfL1ModuleSelected(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_interface.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xlgt_interface.ino" -#ifdef USE_LIGHT - -#ifdef XFUNC_PTR_IN_ROM -bool (* const xlgt_func_ptr[])(uint8_t) PROGMEM = { -#else -bool (* const xlgt_func_ptr[])(uint8_t) = { -#endif - -#ifdef XLGT_01 - &Xlgt01, -#endif - -#ifdef XLGT_02 - &Xlgt02, -#endif - -#ifdef XLGT_03 - &Xlgt03, -#endif - -#ifdef XLGT_04 - &Xlgt04, -#endif - -#ifdef XLGT_05 - &Xlgt05, -#endif - -#ifdef XLGT_06 - &Xlgt06, -#endif - -#ifdef XLGT_07 - &Xlgt07, -#endif - -#ifdef XLGT_08 - &Xlgt08, -#endif - -#ifdef XLGT_09 - &Xlgt09, -#endif - -#ifdef XLGT_10 - &Xlgt10, -#endif - -#ifdef XLGT_11 - &Xlgt11, -#endif - -#ifdef XLGT_12 - &Xlgt12, -#endif - -#ifdef XLGT_13 - &Xlgt13, -#endif - -#ifdef XLGT_14 - &Xlgt14, -#endif - -#ifdef XLGT_15 - &Xlgt15, -#endif - -#ifdef XLGT_16 - &Xlgt16 -#endif -}; - -const uint8_t xlgt_present = sizeof(xlgt_func_ptr) / sizeof(xlgt_func_ptr[0]); - -uint8_t xlgt_active = 0; - -bool XlgtCall(uint8_t function) -{ - DEBUG_TRACE_LOG(PSTR("LGT: %d"), function); - - if (FUNC_MODULE_INIT == function) { - for (uint32_t x = 0; x < xlgt_present; x++) { - xlgt_func_ptr[x](function); - if (light_flg) { - xlgt_active = x; - return true; - } - } - } - else if (light_flg) { - return xlgt_func_ptr[xlgt_active](function); - } - return false; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_01_hlw8012.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_01_hlw8012.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_HLW8012 - - - - - - -#define XNRG_01 1 - - -#define HLW_PREF 10000 -#define HLW_UREF 2200 -#define HLW_IREF 4545 - - -#define HJL_PREF 1362 -#define HJL_UREF 822 -#define HJL_IREF 3300 - -#define HLW_POWER_PROBE_TIME 10 -#define HLW_SAMPLE_COUNT 10 - - - -struct HLW { -#ifdef HLW_DEBUG - unsigned long debug[HLW_SAMPLE_COUNT]; -#endif - unsigned long cf_pulse_length = 0; - unsigned long cf_pulse_last_time = 0; - unsigned long cf_power_pulse_length = 0; - - unsigned long cf1_pulse_length = 0; - unsigned long cf1_pulse_last_time = 0; - unsigned long cf1_summed_pulse_length = 0; - unsigned long cf1_pulse_counter = 0; - unsigned long cf1_voltage_pulse_length = 0; - unsigned long cf1_current_pulse_length = 0; - - unsigned long energy_period_counter = 0; - - unsigned long power_ratio = 0; - unsigned long voltage_ratio = 0; - unsigned long current_ratio = 0; - - uint8_t model_type = 0; - uint8_t cf1_timer = 0; - uint8_t power_retry = 0; - bool select_ui_flag = false; - bool ui_flag = true; - bool load_off = true; -} Hlw; - - -#ifndef USE_WS2812_DMA -void HlwCfInterrupt(void) ICACHE_RAM_ATTR; -void HlwCf1Interrupt(void) ICACHE_RAM_ATTR; -#endif - -void HlwCfInterrupt(void) -{ - unsigned long us = micros(); - - if (Hlw.load_off) { - Hlw.cf_pulse_last_time = us; - Hlw.load_off = false; - } else { - Hlw.cf_pulse_length = us - Hlw.cf_pulse_last_time; - Hlw.cf_pulse_last_time = us; - Hlw.energy_period_counter++; - } - Energy.data_valid[0] = 0; -} - -void HlwCf1Interrupt(void) -{ - unsigned long us = micros(); - - Hlw.cf1_pulse_length = us - Hlw.cf1_pulse_last_time; - Hlw.cf1_pulse_last_time = us; - if ((Hlw.cf1_timer > 2) && (Hlw.cf1_timer < 8)) { - Hlw.cf1_summed_pulse_length += Hlw.cf1_pulse_length; -#ifdef HLW_DEBUG - Hlw.debug[Hlw.cf1_pulse_counter] = Hlw.cf1_pulse_length; -#endif - Hlw.cf1_pulse_counter++; - if (HLW_SAMPLE_COUNT == Hlw.cf1_pulse_counter) { - Hlw.cf1_timer = 8; - } - } - Energy.data_valid[0] = 0; -} - - - -void HlwEvery200ms(void) -{ - unsigned long cf1_pulse_length = 0; - unsigned long hlw_w = 0; - unsigned long hlw_u = 0; - unsigned long hlw_i = 0; - - if (micros() - Hlw.cf_pulse_last_time > (HLW_POWER_PROBE_TIME * 1000000)) { - Hlw.cf_pulse_length = 0; - Hlw.load_off = true; - } - Hlw.cf_power_pulse_length = Hlw.cf_pulse_length; - - if (Hlw.cf_power_pulse_length && Energy.power_on && !Hlw.load_off) { - hlw_w = (Hlw.power_ratio * Settings.energy_power_calibration) / Hlw.cf_power_pulse_length ; - Energy.active_power[0] = (float)hlw_w / 10; - Hlw.power_retry = 1; - } else { - if (Hlw.power_retry) { - Hlw.power_retry--; - } else { - Energy.active_power[0] = 0; - } - } - - if (pin[GPIO_NRG_CF1] < 99) { - Hlw.cf1_timer++; - if (Hlw.cf1_timer >= 8) { - Hlw.cf1_timer = 0; - Hlw.select_ui_flag = (Hlw.select_ui_flag) ? false : true; - DigitalWrite(GPIO_NRG_SEL, Hlw.select_ui_flag); - - if (Hlw.cf1_pulse_counter) { - cf1_pulse_length = Hlw.cf1_summed_pulse_length / Hlw.cf1_pulse_counter; - } - -#ifdef HLW_DEBUG - - char stemp[100]; - stemp[0] = '\0'; - for (uint32_t i = 0; i < Hlw.cf1_pulse_counter; i++) { - snprintf_P(stemp, sizeof(stemp), PSTR("%s %d"), stemp, Hlw.debug[i]); - } - for (uint32_t i = 0; i < Hlw.cf1_pulse_counter; i++) { - for (uint32_t j = i + 1; j < Hlw.cf1_pulse_counter; j++) { - if (Hlw.debug[i] > Hlw.debug[j]) { - std::swap(Hlw.debug[i], Hlw.debug[j]); - } - } - } - unsigned long median = Hlw.debug[(Hlw.cf1_pulse_counter +1) / 2]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("NRG: power %d, ui %d, cnt %d, smpl%s, sum %d, mean %d, median %d"), - Hlw.cf_power_pulse_length , Hlw.select_ui_flag, Hlw.cf1_pulse_counter, stemp, Hlw.cf1_summed_pulse_length, cf1_pulse_length, median); -#endif - - if (Hlw.select_ui_flag == Hlw.ui_flag) { - Hlw.cf1_voltage_pulse_length = cf1_pulse_length; - - if (Hlw.cf1_voltage_pulse_length && Energy.power_on) { - hlw_u = (Hlw.voltage_ratio * Settings.energy_voltage_calibration) / Hlw.cf1_voltage_pulse_length ; - Energy.voltage[0] = (float)hlw_u / 10; - } else { - Energy.voltage[0] = 0; - } - - } else { - Hlw.cf1_current_pulse_length = cf1_pulse_length; - - if (Hlw.cf1_current_pulse_length && Energy.active_power[0]) { - hlw_i = (Hlw.current_ratio * Settings.energy_current_calibration) / Hlw.cf1_current_pulse_length; - Energy.current[0] = (float)hlw_i / 1000; - } else { - Energy.current[0] = 0; - } - - } - Hlw.cf1_summed_pulse_length = 0; - Hlw.cf1_pulse_counter = 0; - } - } -} - -void HlwEverySecond(void) -{ - if (Energy.data_valid[0] > ENERGY_WATCHDOG) { - Hlw.cf1_voltage_pulse_length = 0; - Hlw.cf1_current_pulse_length = 0; - Hlw.cf_power_pulse_length = 0; - } else { - unsigned long hlw_len; - - if (Hlw.energy_period_counter) { - hlw_len = 10000 / Hlw.energy_period_counter; - Hlw.energy_period_counter = 0; - if (hlw_len) { - Energy.kWhtoday_delta += ((Hlw.power_ratio * Settings.energy_power_calibration) / hlw_len) / 36; - EnergyUpdateToday(); - } - } - } -} - -void HlwSnsInit(void) -{ - if (!Settings.energy_power_calibration || (4975 == Settings.energy_power_calibration)) { - Settings.energy_power_calibration = HLW_PREF_PULSE; - Settings.energy_voltage_calibration = HLW_UREF_PULSE; - Settings.energy_current_calibration = HLW_IREF_PULSE; - } - - if (Hlw.model_type) { - Hlw.power_ratio = HJL_PREF; - Hlw.voltage_ratio = HJL_UREF; - Hlw.current_ratio = HJL_IREF; - } else { - Hlw.power_ratio = HLW_PREF; - Hlw.voltage_ratio = HLW_UREF; - Hlw.current_ratio = HLW_IREF; - } - - if (pin[GPIO_NRG_SEL] < 99) { - pinMode(pin[GPIO_NRG_SEL], OUTPUT); - digitalWrite(pin[GPIO_NRG_SEL], Hlw.select_ui_flag); - } - if (pin[GPIO_NRG_CF1] < 99) { - pinMode(pin[GPIO_NRG_CF1], INPUT_PULLUP); - attachInterrupt(pin[GPIO_NRG_CF1], HlwCf1Interrupt, FALLING); - } - pinMode(pin[GPIO_HLW_CF], INPUT_PULLUP); - attachInterrupt(pin[GPIO_HLW_CF], HlwCfInterrupt, FALLING); -} - -void HlwDrvInit(void) -{ - Hlw.model_type = 0; - if (pin[GPIO_HJL_CF] < 99) { - pin[GPIO_HLW_CF] = pin[GPIO_HJL_CF]; - pin[GPIO_HJL_CF] = 99; - Hlw.model_type = 1; - } - - if (pin[GPIO_HLW_CF] < 99) { - - Hlw.ui_flag = true; - if (pin[GPIO_NRG_SEL_INV] < 99) { - pin[GPIO_NRG_SEL] = pin[GPIO_NRG_SEL_INV]; - pin[GPIO_NRG_SEL_INV] = 99; - Hlw.ui_flag = false; - } - - if (pin[GPIO_NRG_CF1] < 99) { - if (99 == pin[GPIO_NRG_SEL]) { - Energy.current_available = false; - } - } else { - Energy.current_available = false; - Energy.voltage_available = false; - } - - energy_flg = XNRG_01; - } -} - -bool HlwCommand(void) -{ - bool serviced = true; - - if ((CMND_POWERCAL == Energy.command_code) || (CMND_VOLTAGECAL == Energy.command_code) || (CMND_CURRENTCAL == Energy.command_code)) { - - } - else if (CMND_POWERSET == Energy.command_code) { - if (XdrvMailbox.data_len && Hlw.cf_power_pulse_length ) { - Settings.energy_power_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * Hlw.cf_power_pulse_length ) / Hlw.power_ratio; - } - } - else if (CMND_VOLTAGESET == Energy.command_code) { - if (XdrvMailbox.data_len && Hlw.cf1_voltage_pulse_length ) { - Settings.energy_voltage_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data) * 10) * Hlw.cf1_voltage_pulse_length ) / Hlw.voltage_ratio; - } - } - else if (CMND_CURRENTSET == Energy.command_code) { - if (XdrvMailbox.data_len && Hlw.cf1_current_pulse_length) { - Settings.energy_current_calibration = ((unsigned long)(CharToFloat(XdrvMailbox.data)) * Hlw.cf1_current_pulse_length) / Hlw.current_ratio; - } - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg01(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_200_MSECOND: - HlwEvery200ms(); - break; - case FUNC_ENERGY_EVERY_SECOND: - HlwEverySecond(); - break; - case FUNC_COMMAND: - result = HlwCommand(); - break; - case FUNC_INIT: - HlwSnsInit(); - break; - case FUNC_PRE_INIT: - HlwDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_02_cse7766.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_02_cse7766.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_CSE7766 - - - - - - - -#define XNRG_02 2 - -#define CSE_MAX_INVALID_POWER 128 - -#define CSE_NOT_CALIBRATED 0xAA - -#define CSE_PULSES_NOT_INITIALIZED -1 - -#define CSE_PREF 1000 -#define CSE_UREF 100 - -#define CSE_BUFFER_SIZE 25 - -#include - -TasmotaSerial *CseSerial = nullptr; - -struct CSE { - long voltage_cycle = 0; - long current_cycle = 0; - long power_cycle = 0; - long power_cycle_first = 0; - long cf_pulses = 0; - long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; - - int byte_counter = 0; - uint8_t *rx_buffer = nullptr; - uint8_t power_invalid = 0; - bool received = false; -} Cse; - -void CseReceived(void) -{ - - - - - - - uint8_t header = Cse.rx_buffer[0]; - if ((header & 0xFC) == 0xFC) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Abnormal hardware")); - return; - } - - - if (HLW_UREF_PULSE == Settings.energy_voltage_calibration) { - long voltage_coefficient = 191200; - if (CSE_NOT_CALIBRATED != header) { - voltage_coefficient = Cse.rx_buffer[2] << 16 | Cse.rx_buffer[3] << 8 | Cse.rx_buffer[4]; - } - Settings.energy_voltage_calibration = voltage_coefficient / CSE_UREF; - } - if (HLW_IREF_PULSE == Settings.energy_current_calibration) { - long current_coefficient = 16140; - if (CSE_NOT_CALIBRATED != header) { - current_coefficient = Cse.rx_buffer[8] << 16 | Cse.rx_buffer[9] << 8 | Cse.rx_buffer[10]; - } - Settings.energy_current_calibration = current_coefficient; - } - if (HLW_PREF_PULSE == Settings.energy_power_calibration) { - long power_coefficient = 5364000; - if (CSE_NOT_CALIBRATED != header) { - power_coefficient = Cse.rx_buffer[14] << 16 | Cse.rx_buffer[15] << 8 | Cse.rx_buffer[16]; - } - Settings.energy_power_calibration = power_coefficient / CSE_PREF; - } - - uint8_t adjustement = Cse.rx_buffer[20]; - Cse.voltage_cycle = Cse.rx_buffer[5] << 16 | Cse.rx_buffer[6] << 8 | Cse.rx_buffer[7]; - Cse.current_cycle = Cse.rx_buffer[11] << 16 | Cse.rx_buffer[12] << 8 | Cse.rx_buffer[13]; - Cse.power_cycle = Cse.rx_buffer[17] << 16 | Cse.rx_buffer[18] << 8 | Cse.rx_buffer[19]; - Cse.cf_pulses = Cse.rx_buffer[21] << 8 | Cse.rx_buffer[22]; - - if (Energy.power_on) { - if (adjustement & 0x40) { - Energy.voltage[0] = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)Cse.voltage_cycle; - } - if (adjustement & 0x10) { - Cse.power_invalid = 0; - if ((header & 0xF2) == 0xF2) { - Energy.active_power[0] = 0; - } else { - if (0 == Cse.power_cycle_first) { Cse.power_cycle_first = Cse.power_cycle; } - if (Cse.power_cycle_first != Cse.power_cycle) { - Cse.power_cycle_first = -1; - Energy.active_power[0] = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)Cse.power_cycle; - } else { - Energy.active_power[0] = 0; - } - } - } else { - if (Cse.power_invalid < Settings.param[P_CSE7766_INVALID_POWER]) { - Cse.power_invalid++; - } else { - Cse.power_cycle_first = 0; - Energy.active_power[0] = 0; - } - } - if (adjustement & 0x20) { - if (0 == Energy.active_power[0]) { - Energy.current[0] = 0; - } else { - Energy.current[0] = (float)Settings.energy_current_calibration / (float)Cse.current_cycle; - } - } - } else { - Cse.power_cycle_first = 0; - Energy.voltage[0] = 0; - Energy.active_power[0] = 0; - Energy.current[0] = 0; - } -} - -bool CseSerialInput(void) -{ - while (CseSerial->available()) { - yield(); - uint8_t serial_in_byte = CseSerial->read(); - - if (Cse.received) { - Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte; - if (24 == Cse.byte_counter) { - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, Cse.rx_buffer, 24); - - uint8_t checksum = 0; - for (uint32_t i = 2; i < 23; i++) { checksum += Cse.rx_buffer[i]; } - if (checksum == Cse.rx_buffer[23]) { - Energy.data_valid[0] = 0; - CseReceived(); - Cse.received = false; - return true; - } else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: " D_CHECKSUM_FAILURE)); - do { - memmove(Cse.rx_buffer, Cse.rx_buffer +1, 24); - Cse.byte_counter--; - } while ((Cse.byte_counter > 2) && (0x5A != Cse.rx_buffer[1])); - if (0x5A != Cse.rx_buffer[1]) { - Cse.received = false; - Cse.byte_counter = 0; - } - } - } - } else { - if ((0x5A == serial_in_byte) && (1 == Cse.byte_counter)) { - Cse.received = true; - } else { - Cse.byte_counter = 0; - } - Cse.rx_buffer[Cse.byte_counter++] = serial_in_byte; - } - } -} - - - -void CseEverySecond(void) -{ - if (Energy.data_valid[0] > ENERGY_WATCHDOG) { - Cse.voltage_cycle = 0; - Cse.current_cycle = 0; - Cse.power_cycle = 0; - } else { - long cf_frequency = 0; - - if (CSE_PULSES_NOT_INITIALIZED == Cse.cf_pulses_last_time) { - Cse.cf_pulses_last_time = Cse.cf_pulses; - } else { - if (Cse.cf_pulses < Cse.cf_pulses_last_time) { - cf_frequency = (65536 - Cse.cf_pulses_last_time) + Cse.cf_pulses; - } else { - cf_frequency = Cse.cf_pulses - Cse.cf_pulses_last_time; - } - if (cf_frequency && Energy.active_power[0]) { - unsigned long delta = (cf_frequency * Settings.energy_power_calibration) / 36; - - - - if (delta <= (4000*100/36) * 10 ) { - Cse.cf_pulses_last_time = Cse.cf_pulses; - Energy.kWhtoday_delta += delta; - } - else { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("CSE: Load overflow")); - Cse.cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED; - } - EnergyUpdateToday(); - } - } - } -} - -void CseSnsInit(void) -{ - - - CseSerial = new TasmotaSerial(pin[GPIO_CSE7766_RX], -1, 1); - if (CseSerial->begin(4800, 2)) { - if (CseSerial->hardwareSerial()) { - SetSerial(4800, TS_SERIAL_8E1); - ClaimSerial(); - } - if (0 == Settings.param[P_CSE7766_INVALID_POWER]) { - Settings.param[P_CSE7766_INVALID_POWER] = CSE_MAX_INVALID_POWER; - } - Cse.power_invalid = Settings.param[P_CSE7766_INVALID_POWER]; - } else { - energy_flg = ENERGY_NONE; - } -} - -void CseDrvInit(void) -{ - Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE)); - if (Cse.rx_buffer != nullptr) { - - if (pin[GPIO_CSE7766_RX] < 99) { - energy_flg = XNRG_02; - } - } -} - -bool CseCommand(void) -{ - bool serviced = true; - - if (CMND_POWERSET == Energy.command_code) { - if (XdrvMailbox.data_len && Cse.power_cycle) { - Settings.energy_power_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.power_cycle) / CSE_PREF; - } - } - else if (CMND_VOLTAGESET == Energy.command_code) { - if (XdrvMailbox.data_len && Cse.voltage_cycle) { - Settings.energy_voltage_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.voltage_cycle) / CSE_UREF; - } - } - else if (CMND_CURRENTSET == Energy.command_code) { - if (XdrvMailbox.data_len && Cse.current_cycle) { - Settings.energy_current_calibration = (unsigned long)(CharToFloat(XdrvMailbox.data) * Cse.current_cycle) / 1000; - } - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg02(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_LOOP: - if (CseSerial) { CseSerialInput(); } - break; - case FUNC_ENERGY_EVERY_SECOND: - CseEverySecond(); - break; - case FUNC_COMMAND: - result = CseCommand(); - break; - case FUNC_INIT: - CseSnsInit(); - break; - case FUNC_PRE_INIT: - CseDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_03_pzem004t.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_03_pzem004t.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_PZEM004T -# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_03_pzem004t.ino" -#define XNRG_03 3 - -const uint32_t PZEM_STABILIZE = 30; - -#include - -TasmotaSerial *PzemSerial = nullptr; - -#define PZEM_VOLTAGE (uint8_t)0xB0 -#define RESP_VOLTAGE (uint8_t)0xA0 - -#define PZEM_CURRENT (uint8_t)0xB1 -#define RESP_CURRENT (uint8_t)0xA1 - -#define PZEM_POWER (uint8_t)0xB2 -#define RESP_POWER (uint8_t)0xA2 - -#define PZEM_ENERGY (uint8_t)0xB3 -#define RESP_ENERGY (uint8_t)0xA3 - -#define PZEM_SET_ADDRESS (uint8_t)0xB4 -#define RESP_SET_ADDRESS (uint8_t)0xA4 - -#define PZEM_POWER_ALARM (uint8_t)0xB5 -#define RESP_POWER_ALARM (uint8_t)0xA5 - -#define PZEM_DEFAULT_READ_TIMEOUT 500 - - - -struct PZEM { - float energy = 0; - float last_energy = 0; - uint8_t send_retry = 0; - uint8_t read_state = 0; - uint8_t phase = 0; - uint8_t address = 0; -} Pzem; - -struct PZEMCommand { - uint8_t command; - uint8_t addr[4]; - uint8_t data; - uint8_t crc; -}; - -uint8_t PzemCrc(uint8_t *data) -{ - uint16_t crc = 0; - for (uint32_t i = 0; i < sizeof(PZEMCommand) -1; i++) { - crc += *data++; - } - return (uint8_t)(crc & 0xFF); -} - -void PzemSend(uint8_t cmd) -{ - PZEMCommand pzem; - - pzem.command = cmd; - pzem.addr[0] = 192; - pzem.addr[1] = 168; - pzem.addr[2] = 1; - pzem.addr[3] = ((PZEM_SET_ADDRESS == cmd) && Pzem.address) ? Pzem.address : 1 + Pzem.phase; - pzem.data = 0; - - uint8_t *bytes = (uint8_t*)&pzem; - pzem.crc = PzemCrc(bytes); - - PzemSerial->flush(); - PzemSerial->write(bytes, sizeof(pzem)); - - Pzem.address = 0; -} - -bool PzemReceiveReady(void) -{ - return PzemSerial->available() >= (int)sizeof(PZEMCommand); -} - -bool PzemRecieve(uint8_t resp, float *data) -{ -# 124 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_03_pzem004t.ino" - uint8_t buffer[sizeof(PZEMCommand)] = { 0 }; - - unsigned long start = millis(); - uint8_t len = 0; - while ((len < sizeof(PZEMCommand)) && (millis() - start < PZEM_DEFAULT_READ_TIMEOUT)) { - if (PzemSerial->available() > 0) { - uint8_t c = (uint8_t)PzemSerial->read(); - if (!len && ((c & 0xF8) != 0xA0)) { - continue; - } - buffer[len++] = c; - } - } - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, len); - - if (len != sizeof(PZEMCommand)) { - - return false; - } - if (buffer[6] != PzemCrc(buffer)) { - - return false; - } - if (buffer[0] != resp) { - - return false; - } - - switch (resp) { - case RESP_VOLTAGE: - *data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 10.0); - break; - case RESP_CURRENT: - *data = (float)(buffer[1] << 8) + buffer[2] + (buffer[3] / 100.0); - break; - case RESP_POWER: - *data = (float)(buffer[1] << 8) + buffer[2]; - break; - case RESP_ENERGY: - *data = (float)((uint32_t)buffer[1] << 16) + ((uint16_t)buffer[2] << 8) + buffer[3]; - break; - } - return true; -} - - - -const uint8_t pzem_commands[] { PZEM_SET_ADDRESS, PZEM_VOLTAGE, PZEM_CURRENT, PZEM_POWER, PZEM_ENERGY }; -const uint8_t pzem_responses[] { RESP_SET_ADDRESS, RESP_VOLTAGE, RESP_CURRENT, RESP_POWER, RESP_ENERGY }; - -void PzemEvery250ms(void) -{ - bool data_ready = PzemReceiveReady(); - - if (data_ready) { - float value = 0; - if (PzemRecieve(pzem_responses[Pzem.read_state], &value)) { - Energy.data_valid[Pzem.phase] = 0; - switch (Pzem.read_state) { - case 1: - Energy.voltage[Pzem.phase] = value; - break; - case 2: - Energy.current[Pzem.phase] = value; - break; - case 3: - Energy.active_power[Pzem.phase] = value; - break; - case 4: - Pzem.energy += value; - if (Pzem.phase == Energy.phase_count -1) { - if (Pzem.energy > Pzem.last_energy) { - if (uptime > PZEM_STABILIZE) { - EnergyUpdateTotal(Pzem.energy, false); - } - Pzem.last_energy = Pzem.energy; - } - Pzem.energy = 0; - } - break; - } - Pzem.read_state++; - if (5 == Pzem.read_state) { - Pzem.read_state = 1; - } - - - } - } - - if (0 == Pzem.send_retry || data_ready) { - if (1 == Pzem.read_state) { - if (0 == Pzem.phase) { - Pzem.phase = Energy.phase_count -1; - } else { - Pzem.phase--; - } - - - } - - if (Pzem.address) { - Pzem.read_state = 0; - } - - Pzem.send_retry = 5; - PzemSend(pzem_commands[Pzem.read_state]); - } - else { - Pzem.send_retry--; - if ((Energy.phase_count > 1) && (0 == Pzem.send_retry) && (uptime < PZEM_STABILIZE)) { - Energy.phase_count--; - } - } -} - -void PzemSnsInit(void) -{ - - PzemSerial = new TasmotaSerial(pin[GPIO_PZEM004_RX], pin[GPIO_PZEM0XX_TX], 1); - if (PzemSerial->begin(9600)) { - if (PzemSerial->hardwareSerial()) { - ClaimSerial(); - } - Energy.phase_count = 3; - Pzem.phase = 0; - Pzem.read_state = 1; - } else { - energy_flg = ENERGY_NONE; - } -} - -void PzemDrvInit(void) -{ - if ((pin[GPIO_PZEM004_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { - energy_flg = XNRG_03; - } -} - -bool PzemCommand(void) -{ - bool serviced = true; - - if (CMND_MODULEADDRESS == Energy.command_code) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 4)) { - Pzem.address = XdrvMailbox.payload; - } - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg03(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - if (PzemSerial && (uptime > 4)) { PzemEvery250ms(); } - break; - case FUNC_COMMAND: - result = PzemCommand(); - break; - case FUNC_INIT: - PzemSnsInit(); - break; - case FUNC_PRE_INIT: - PzemDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_04_mcp39f501.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_04_mcp39f501.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_MCP39F501 - - - - - - - -#define XNRG_04 4 - -#define MCP_BAUDRATE 4800 -#define MCP_TIMEOUT 4 -#define MCP_CALIBRATION_TIMEOUT 2 - -#define MCP_CALIBRATE_POWER 0x001 -#define MCP_CALIBRATE_VOLTAGE 0x002 -#define MCP_CALIBRATE_CURRENT 0x004 -#define MCP_CALIBRATE_FREQUENCY 0x008 -#define MCP_SINGLE_WIRE_FLAG 0x100 - -#define MCP_START_FRAME 0xA5 -#define MCP_ACK_FRAME 0x06 -#define MCP_ERROR_NAK 0x15 -#define MCP_ERROR_CRC 0x51 - -#define MCP_SINGLE_WIRE 0xAB - -#define MCP_SET_ADDRESS 0x41 - -#define MCP_READ 0x4E -#define MCP_READ_16 0x52 -#define MCP_READ_32 0x44 - -#define MCP_WRITE 0x4D -#define MCP_WRITE_16 0x57 -#define MCP_WRITE_32 0x45 - -#define MCP_SAVE_REGISTERS 0x53 - -#define MCP_CALIBRATION_BASE 0x0028 -#define MCP_CALIBRATION_LEN 52 - -#define MCP_FREQUENCY_REF_BASE 0x0094 -#define MCP_FREQUENCY_GAIN_BASE 0x00AE -#define MCP_FREQUENCY_LEN 4 - -#define MCP_BUFFER_SIZE 60 - -#include -TasmotaSerial *McpSerial = nullptr; - -typedef struct mcp_cal_registers_type { - uint16_t gain_current_rms; - uint16_t gain_voltage_rms; - uint16_t gain_active_power; - uint16_t gain_reactive_power; - sint32_t offset_current_rms; - sint32_t offset_active_power; - sint32_t offset_reactive_power; - sint16_t dc_offset_current; - sint16_t phase_compensation; - uint16_t apparent_power_divisor; - - uint32_t system_configuration; - uint16_t dio_configuration; - uint32_t range; - - uint32_t calibration_current; - uint16_t calibration_voltage; - uint32_t calibration_active_power; - uint32_t calibration_reactive_power; - uint16_t accumulation_interval; -} mcp_cal_registers_type; - -char *mcp_buffer = nullptr; -unsigned long mcp_window = 0; -unsigned long mcp_kWhcounter = 0; -uint32_t mcp_system_configuration = 0x03000000; -uint32_t mcp_active_power; - - -uint32_t mcp_current_rms; -uint16_t mcp_voltage_rms; -uint16_t mcp_line_frequency; - -uint8_t mcp_address = 0; -uint8_t mcp_calibration_active = 0; -uint8_t mcp_init = 0; -uint8_t mcp_timeout = 0; -uint8_t mcp_calibrate = 0; -uint8_t mcp_byte_counter = 0; - - - - - - -uint8_t McpChecksum(uint8_t *data) -{ - uint8_t checksum = 0; - uint8_t offset = 0; - uint8_t len = data[1] -1; - - for (uint32_t i = offset; i < len; i++) { checksum += data[i]; } - return checksum; -} - -unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size) -{ - unsigned long result = 0; - unsigned long pow = 1; - - for (uint32_t i = 0; i < size; i++) { - result = result + (uint8_t)data[offset + i] * pow; - pow = pow * 256; - } - return result; -} - -void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size) -{ - for (uint32_t i = 0; i < size; i++) { - data[offset + i] = ((value >> (i * 8)) & 0xFF); - } -} - -void McpSend(uint8_t *data) -{ - if (mcp_timeout) { return; } - mcp_timeout = MCP_TIMEOUT; - - data[0] = MCP_START_FRAME; - data[data[1] -1] = McpChecksum(data); - - - - for (uint32_t i = 0; i < data[1]; i++) { - McpSerial->write(data[i]); - } -} - - - -void McpGetAddress(void) -{ - uint8_t data[] = { MCP_START_FRAME, 7, MCP_SET_ADDRESS, 0x00, 0x26, MCP_READ_16, 0x00 }; - - McpSend(data); -} - -void McpAddressReceive(void) -{ - - mcp_address = mcp_buffer[3]; -} - - - -void McpGetCalibration(void) -{ - if (mcp_calibration_active) { return; } - mcp_calibration_active = MCP_CALIBRATION_TIMEOUT; - - uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, (MCP_CALIBRATION_BASE >> 8) & 0xFF, MCP_CALIBRATION_BASE & 0xFF, MCP_READ, MCP_CALIBRATION_LEN, 0x00 }; - - McpSend(data); -} - -void McpParseCalibration(void) -{ - bool action = false; - mcp_cal_registers_type cal_registers; - - - cal_registers.gain_current_rms = McpExtractInt(mcp_buffer, 2, 2); - cal_registers.gain_voltage_rms = McpExtractInt(mcp_buffer, 4, 2); - cal_registers.gain_active_power = McpExtractInt(mcp_buffer, 6, 2); - cal_registers.gain_reactive_power = McpExtractInt(mcp_buffer, 8, 2); - cal_registers.offset_current_rms = McpExtractInt(mcp_buffer, 10, 4); - cal_registers.offset_active_power = McpExtractInt(mcp_buffer, 14, 4); - cal_registers.offset_reactive_power = McpExtractInt(mcp_buffer, 18, 4); - cal_registers.dc_offset_current = McpExtractInt(mcp_buffer, 22, 2); - cal_registers.phase_compensation = McpExtractInt(mcp_buffer, 24, 2); - cal_registers.apparent_power_divisor = McpExtractInt(mcp_buffer, 26, 2); - - cal_registers.system_configuration = McpExtractInt(mcp_buffer, 28, 4); - cal_registers.dio_configuration = McpExtractInt(mcp_buffer, 32, 2); - cal_registers.range = McpExtractInt(mcp_buffer, 34, 4); - - cal_registers.calibration_current = McpExtractInt(mcp_buffer, 38, 4); - cal_registers.calibration_voltage = McpExtractInt(mcp_buffer, 42, 2); - cal_registers.calibration_active_power = McpExtractInt(mcp_buffer, 44, 4); - cal_registers.calibration_reactive_power = McpExtractInt(mcp_buffer, 48, 4); - cal_registers.accumulation_interval = McpExtractInt(mcp_buffer, 52, 2); - - if (mcp_calibrate & MCP_CALIBRATE_POWER) { - cal_registers.calibration_active_power = Settings.energy_power_calibration; - if (McpCalibrationCalc(&cal_registers, 16)) { action = true; } - } - if (mcp_calibrate & MCP_CALIBRATE_VOLTAGE) { - cal_registers.calibration_voltage = Settings.energy_voltage_calibration; - if (McpCalibrationCalc(&cal_registers, 0)) { action = true; } - } - if (mcp_calibrate & MCP_CALIBRATE_CURRENT) { - cal_registers.calibration_current = Settings.energy_current_calibration; - if (McpCalibrationCalc(&cal_registers, 8)) { action = true; } - } - mcp_timeout = 0; - if (action) { McpSetCalibration(&cal_registers); } - - mcp_calibrate = 0; - - Settings.energy_power_calibration = cal_registers.calibration_active_power; - Settings.energy_voltage_calibration = cal_registers.calibration_voltage; - Settings.energy_current_calibration = cal_registers.calibration_current; - - mcp_system_configuration = cal_registers.system_configuration; - - if (mcp_system_configuration & MCP_SINGLE_WIRE_FLAG) { - mcp_system_configuration &= ~MCP_SINGLE_WIRE_FLAG; - McpSetSystemConfiguration(2); - } -} - -bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift) -{ - uint32_t measured; - uint32_t expected; - uint16_t *gain; - uint32_t new_gain; - - if (range_shift == 0) { - measured = mcp_voltage_rms; - expected = cal_registers->calibration_voltage; - gain = &(cal_registers->gain_voltage_rms); - } else if (range_shift == 8) { - measured = mcp_current_rms; - expected = cal_registers->calibration_current; - gain = &(cal_registers->gain_current_rms); - } else if (range_shift == 16) { - measured = mcp_active_power; - expected = cal_registers->calibration_active_power; - gain = &(cal_registers->gain_active_power); - } else { - return false; - } - - if (measured == 0) { - return false; - } - - uint32_t range = (cal_registers->range >> range_shift) & 0xFF; - -calc: - new_gain = (*gain) * expected / measured; - - if (new_gain < 25000) { - range++; - if (measured > 6) { - measured = measured / 2; - goto calc; - } - } - - if (new_gain > 55000) { - range--; - measured = measured * 2; - goto calc; - } - - *gain = new_gain; - uint32_t old_range = (cal_registers->range >> range_shift) & 0xFF; - cal_registers->range = cal_registers->range ^ (old_range << range_shift); - cal_registers->range = cal_registers->range | (range << range_shift); - - return true; -} - - - - - - -void McpSetCalibration(struct mcp_cal_registers_type *cal_registers) -{ - uint8_t data[7 + MCP_CALIBRATION_LEN + 2 + 1]; - - data[1] = sizeof(data); - data[2] = MCP_SET_ADDRESS; - data[3] = (MCP_CALIBRATION_BASE >> 8) & 0xFF; - data[4] = (MCP_CALIBRATION_BASE >> 0) & 0xFF; - - data[5] = MCP_WRITE; - data[6] = MCP_CALIBRATION_LEN; - - McpSetInt(cal_registers->gain_current_rms, data, 0+7, 2); - McpSetInt(cal_registers->gain_voltage_rms, data, 2+7, 2); - McpSetInt(cal_registers->gain_active_power, data, 4+7, 2); - McpSetInt(cal_registers->gain_reactive_power, data, 6+7, 2); - McpSetInt(cal_registers->offset_current_rms, data, 8+7, 4); - McpSetInt(cal_registers->offset_active_power, data, 12+7, 4); - McpSetInt(cal_registers->offset_reactive_power, data, 16+7, 4); - McpSetInt(cal_registers->dc_offset_current, data, 20+7, 2); - McpSetInt(cal_registers->phase_compensation, data, 22+7, 2); - McpSetInt(cal_registers->apparent_power_divisor, data, 24+7, 2); - - McpSetInt(cal_registers->system_configuration, data, 26+7, 4); - McpSetInt(cal_registers->dio_configuration, data, 30+7, 2); - McpSetInt(cal_registers->range, data, 32+7, 4); - - McpSetInt(cal_registers->calibration_current, data, 36+7, 4); - McpSetInt(cal_registers->calibration_voltage, data, 40+7, 2); - McpSetInt(cal_registers->calibration_active_power, data, 42+7, 4); - McpSetInt(cal_registers->calibration_reactive_power, data, 46+7, 4); - McpSetInt(cal_registers->accumulation_interval, data, 50+7, 2); - - data[MCP_CALIBRATION_LEN+7] = MCP_SAVE_REGISTERS; - data[MCP_CALIBRATION_LEN+8] = mcp_address; - - McpSend(data); -} - - - -void McpSetSystemConfiguration(uint16 interval) -{ - - uint8_t data[17]; - - data[ 1] = sizeof(data); - data[ 2] = MCP_SET_ADDRESS; - data[ 3] = 0x00; - data[ 4] = 0x42; - data[ 5] = MCP_WRITE_32; - data[ 6] = (mcp_system_configuration >> 24) & 0xFF; - data[ 7] = (mcp_system_configuration >> 16) & 0xFF; - data[ 8] = (mcp_system_configuration >> 8) & 0xFF; - data[ 9] = (mcp_system_configuration >> 0) & 0xFF; - data[10] = MCP_SET_ADDRESS; - data[11] = 0x00; - data[12] = 0x5A; - data[13] = MCP_WRITE_16; - data[14] = (interval >> 8) & 0xFF; - data[15] = (interval >> 0) & 0xFF; - - McpSend(data); -} - - - -void McpGetFrequency(void) -{ - if (mcp_calibration_active) { return; } - mcp_calibration_active = MCP_CALIBRATION_TIMEOUT; - - uint8_t data[] = { MCP_START_FRAME, 11, MCP_SET_ADDRESS, (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF, MCP_FREQUENCY_REF_BASE & 0xFF, MCP_READ_16, - MCP_SET_ADDRESS, (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF, MCP_FREQUENCY_GAIN_BASE & 0xFF, MCP_READ_16, 0x00 }; - - McpSend(data); -} - -void McpParseFrequency(void) -{ - - uint16_t line_frequency_ref = mcp_buffer[2] * 256 + mcp_buffer[3]; - uint16_t gain_line_frequency = mcp_buffer[4] * 256 + mcp_buffer[5]; - - if (mcp_calibrate & MCP_CALIBRATE_FREQUENCY) { - line_frequency_ref = Settings.energy_frequency_calibration; - - if ((0xFFFF == mcp_line_frequency) || (0 == gain_line_frequency)) { - mcp_line_frequency = 50000; - gain_line_frequency = 0x8000; - } - gain_line_frequency = gain_line_frequency * line_frequency_ref / mcp_line_frequency; - - mcp_timeout = 0; - McpSetFrequency(line_frequency_ref, gain_line_frequency); - } - - Settings.energy_frequency_calibration = line_frequency_ref; - - mcp_calibrate = 0; -} - -void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency) -{ - - uint8_t data[17]; - - data[ 1] = sizeof(data); - data[ 2] = MCP_SET_ADDRESS; - data[ 3] = (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF; - data[ 4] = (MCP_FREQUENCY_REF_BASE >> 0) & 0xFF; - - data[ 5] = MCP_WRITE_16; - data[ 6] = (line_frequency_ref >> 8) & 0xFF; - data[ 7] = (line_frequency_ref >> 0) & 0xFF; - - data[ 8] = MCP_SET_ADDRESS; - data[ 9] = (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF; - data[10] = (MCP_FREQUENCY_GAIN_BASE >> 0) & 0xFF; - - data[11] = MCP_WRITE_16; - data[12] = (gain_line_frequency >> 8) & 0xFF; - data[13] = (gain_line_frequency >> 0) & 0xFF; - - data[14] = MCP_SAVE_REGISTERS; - data[15] = mcp_address; - - McpSend(data); -} - - - -void McpGetData(void) -{ - uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, 0x00, 0x04, MCP_READ, 22, 0x00 }; - - McpSend(data); -} - -void McpParseData(void) -{ - - - - - - mcp_current_rms = McpExtractInt(mcp_buffer, 2, 4); - mcp_voltage_rms = McpExtractInt(mcp_buffer, 6, 2); - mcp_active_power = McpExtractInt(mcp_buffer, 8, 4); - - - mcp_line_frequency = McpExtractInt(mcp_buffer, 22, 2); - - if (Energy.power_on) { - Energy.data_valid[0] = 0; - Energy.frequency[0] = (float)mcp_line_frequency / 1000; - Energy.voltage[0] = (float)mcp_voltage_rms / 10; - Energy.active_power[0] = (float)mcp_active_power / 100; - if (0 == Energy.active_power[0]) { - Energy.current[0] = 0; - } else { - Energy.current[0] = (float)mcp_current_rms / 10000; - } - } else { - Energy.data_valid[0] = ENERGY_WATCHDOG; - } -} - - - -void McpSerialInput(void) -{ - while ((McpSerial->available()) && (mcp_byte_counter < MCP_BUFFER_SIZE)) { - yield(); - mcp_buffer[mcp_byte_counter++] = McpSerial->read(); - mcp_window = millis(); - } - - - if ((mcp_byte_counter) && (millis() - mcp_window > (24000 / MCP_BAUDRATE) +1)) { - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, (uint8_t*)mcp_buffer, mcp_byte_counter); - - if (MCP_BUFFER_SIZE == mcp_byte_counter) { - - } - else if (1 == mcp_byte_counter) { - if (MCP_ERROR_CRC == mcp_buffer[0]) { - - mcp_timeout = 0; - } - else if (MCP_ERROR_NAK == mcp_buffer[0]) { - - mcp_timeout = 0; - } - } - else if (MCP_ACK_FRAME == mcp_buffer[0]) { - if (mcp_byte_counter == mcp_buffer[1]) { - - if (McpChecksum((uint8_t *)mcp_buffer) != mcp_buffer[mcp_byte_counter -1]) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE)); - } else { - if (5 == mcp_buffer[1]) { McpAddressReceive(); } - if (25 == mcp_buffer[1]) { McpParseData(); } - if (MCP_CALIBRATION_LEN + 3 == mcp_buffer[1]) { McpParseCalibration(); } - if (MCP_FREQUENCY_LEN + 3 == mcp_buffer[1]) { McpParseFrequency(); } - } - - } - mcp_timeout = 0; - } - else if (MCP_SINGLE_WIRE == mcp_buffer[0]) { - mcp_timeout = 0; - } - - mcp_byte_counter = 0; - McpSerial->flush(); - } -} - - - -void McpEverySecond(void) -{ - if (Energy.data_valid[0] > ENERGY_WATCHDOG) { - mcp_voltage_rms = 0; - mcp_current_rms = 0; - mcp_active_power = 0; - mcp_line_frequency = 0; - } - - if (mcp_active_power) { - Energy.kWhtoday_delta += ((mcp_active_power * 10) / 36); - EnergyUpdateToday(); - } - - if (mcp_timeout) { - mcp_timeout--; - } - else if (mcp_calibration_active) { - mcp_calibration_active--; - } - else if (mcp_init) { - if (2 == mcp_init) { - McpGetCalibration(); - } - else if (1 == mcp_init) { - McpGetFrequency(); - } - mcp_init--; - } - else if (!mcp_address) { - McpGetAddress(); - } - else { - McpGetData(); - } -} - -void McpSnsInit(void) -{ - - McpSerial = new TasmotaSerial(pin[GPIO_MCP39F5_RX], pin[GPIO_MCP39F5_TX], 1); - if (McpSerial->begin(MCP_BAUDRATE)) { - if (McpSerial->hardwareSerial()) { - ClaimSerial(); - mcp_buffer = serial_in_buffer; - } else { - mcp_buffer = (char*)(malloc(MCP_BUFFER_SIZE)); - } - DigitalWrite(GPIO_MCP39F5_RST, 1); - } else { - energy_flg = ENERGY_NONE; - } -} - -void McpDrvInit(void) -{ - if ((pin[GPIO_MCP39F5_RX] < 99) && (pin[GPIO_MCP39F5_TX] < 99)) { - if (pin[GPIO_MCP39F5_RST] < 99) { - pinMode(pin[GPIO_MCP39F5_RST], OUTPUT); - digitalWrite(pin[GPIO_MCP39F5_RST], 0); - } - mcp_calibrate = 0; - mcp_timeout = 2; - mcp_init = 2; - energy_flg = XNRG_04; - } -} - -bool McpCommand(void) -{ - bool serviced = true; - unsigned long value = 0; - - if (CMND_POWERSET == Energy.command_code) { - if (XdrvMailbox.data_len && mcp_active_power) { - value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 100); - if ((value > 100) && (value < 200000)) { - Settings.energy_power_calibration = value; - mcp_calibrate |= MCP_CALIBRATE_POWER; - McpGetCalibration(); - } - } - } - else if (CMND_VOLTAGESET == Energy.command_code) { - if (XdrvMailbox.data_len && mcp_voltage_rms) { - value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); - if ((value > 1000) && (value < 2600)) { - Settings.energy_voltage_calibration = value; - mcp_calibrate |= MCP_CALIBRATE_VOLTAGE; - McpGetCalibration(); - } - } - } - else if (CMND_CURRENTSET == Energy.command_code) { - if (XdrvMailbox.data_len && mcp_current_rms) { - value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 10); - if ((value > 100) && (value < 80000)) { - Settings.energy_current_calibration = value; - mcp_calibrate |= MCP_CALIBRATE_CURRENT; - McpGetCalibration(); - } - } - } - else if (CMND_FREQUENCYSET == Energy.command_code) { - if (XdrvMailbox.data_len && mcp_line_frequency) { - value = (unsigned long)(CharToFloat(XdrvMailbox.data) * 1000); - if ((value > 45000) && (value < 65000)) { - Settings.energy_frequency_calibration = value; - mcp_calibrate |= MCP_CALIBRATE_FREQUENCY; - McpGetFrequency(); - } - } - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg04(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_LOOP: - if (McpSerial) { McpSerialInput(); } - break; - case FUNC_ENERGY_EVERY_SECOND: - if (McpSerial) { McpEverySecond(); } - break; - case FUNC_COMMAND: - result = McpCommand(); - break; - case FUNC_INIT: - McpSnsInit(); - break; - case FUNC_PRE_INIT: - McpDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_05_pzem_ac.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_05_pzem_ac.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_PZEM_AC -# 33 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_05_pzem_ac.ino" -#define XNRG_05 5 - -const uint8_t PZEM_AC_DEVICE_ADDRESS = 0x01; -const uint32_t PZEM_AC_STABILIZE = 30; - -#include -TasmotaModbus *PzemAcModbus; - -struct PZEMAC { - float energy = 0; - float last_energy = 0; - uint8_t send_retry = 0; - uint8_t phase = 0; - uint8_t address = 0; - uint8_t address_step = ADDR_IDLE; -} PzemAc; - -void PzemAcEverySecond(void) -{ - bool data_ready = PzemAcModbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[30]; - - uint8_t registers = 10; - if (ADDR_RECEIVE == PzemAc.address_step) { - registers = 2; - PzemAc.address_step--; - } - uint8_t error = PzemAcModbus->ReceiveBuffer(buffer, registers); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemAcModbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAC: PzemAc %d error %d"), PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, error); - } else { - Energy.data_valid[PzemAc.phase] = 0; - if (10 == registers) { - - - - - - Energy.voltage[PzemAc.phase] = (float)((buffer[3] << 8) + buffer[4]) / 10.0; - Energy.current[PzemAc.phase] = (float)((buffer[7] << 24) + (buffer[8] << 16) + (buffer[5] << 8) + buffer[6]) / 1000.0; - Energy.active_power[PzemAc.phase] = (float)((buffer[11] << 24) + (buffer[12] << 16) + (buffer[9] << 8) + buffer[10]) / 10.0; - Energy.frequency[PzemAc.phase] = (float)((buffer[17] << 8) + buffer[18]) / 10.0; - Energy.power_factor[PzemAc.phase] = (float)((buffer[19] << 8) + buffer[20]) / 100.0; - - PzemAc.energy += (float)((buffer[15] << 24) + (buffer[16] << 16) + (buffer[13] << 8) + buffer[14]); - if (PzemAc.phase == Energy.phase_count -1) { - if (PzemAc.energy > PzemAc.last_energy) { - if (uptime > PZEM_AC_STABILIZE) { - EnergyUpdateTotal(PzemAc.energy, false); - } - PzemAc.last_energy = PzemAc.energy; - } - PzemAc.energy = 0; - } - - } - } - } - - if (0 == PzemAc.send_retry || data_ready) { - if (0 == PzemAc.phase) { - PzemAc.phase = Energy.phase_count -1; - } else { - PzemAc.phase--; - } - PzemAc.send_retry = ENERGY_WATCHDOG; - if (ADDR_SEND == PzemAc.address_step) { - PzemAcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemAc.address); - PzemAc.address_step--; - } else { - PzemAcModbus->Send(PZEM_AC_DEVICE_ADDRESS + PzemAc.phase, 0x04, 0, 10); - } - } - else { - PzemAc.send_retry--; - if ((Energy.phase_count > 1) && (0 == PzemAc.send_retry) && (uptime < PZEM_AC_STABILIZE)) { - Energy.phase_count--; - } - } -} - -void PzemAcSnsInit(void) -{ - PzemAcModbus = new TasmotaModbus(pin[GPIO_PZEM016_RX], pin[GPIO_PZEM0XX_TX]); - uint8_t result = PzemAcModbus->Begin(9600); - if (result) { - if (2 == result) { ClaimSerial(); } - Energy.phase_count = 3; - PzemAc.phase = 0; - } else { - energy_flg = ENERGY_NONE; - } -} - -void PzemAcDrvInit(void) -{ - if ((pin[GPIO_PZEM016_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { - energy_flg = XNRG_05; - } -} - -bool PzemAcCommand(void) -{ - bool serviced = true; - - if (CMND_MODULEADDRESS == Energy.command_code) { - PzemAc.address = XdrvMailbox.payload; - PzemAc.address_step = ADDR_SEND; - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg05(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_ENERGY_EVERY_SECOND: - if (uptime > 4) { PzemAcEverySecond(); } - break; - case FUNC_COMMAND: - result = PzemAcCommand(); - break; - case FUNC_INIT: - PzemAcSnsInit(); - break; - case FUNC_PRE_INIT: - PzemAcDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_06_pzem_dc.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_06_pzem_dc.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_PZEM_DC -# 32 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_06_pzem_dc.ino" -#define XNRG_06 6 - -const uint8_t PZEM_DC_DEVICE_ADDRESS = 0x01; -const uint32_t PZEM_DC_STABILIZE = 30; - -#include -TasmotaModbus *PzemDcModbus; - -struct PZEMDC { - float energy = 0; - float last_energy = 0; - uint8_t send_retry = 0; - uint8_t channel = 0; - uint8_t address = 0; - uint8_t address_step = ADDR_IDLE; -} PzemDc; - -void PzemDcEverySecond(void) -{ - bool data_ready = PzemDcModbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[26]; - - uint8_t registers = 8; - if (ADDR_RECEIVE == PzemDc.address_step) { - registers = 2; - PzemDc.address_step--; - } - uint8_t error = PzemDcModbus->ReceiveBuffer(buffer, registers); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, PzemDcModbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PDC: PzemDc %d error %d"), PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, error); - } else { - Energy.data_valid[PzemDc.channel] = 0; - if (8 == registers) { - - - - - - Energy.voltage[PzemDc.channel] = (float)((buffer[3] << 8) + buffer[4]) / 100.0; - Energy.current[PzemDc.channel] = (float)((buffer[5] << 8) + buffer[6]) / 100.0; - Energy.active_power[PzemDc.channel] = (float)((buffer[9] << 24) + (buffer[10] << 16) + (buffer[7] << 8) + buffer[8]) / 10.0; - - PzemDc.energy += (float)((buffer[13] << 24) + (buffer[14] << 16) + (buffer[11] << 8) + buffer[12]); - if (PzemDc.channel == Energy.phase_count -1) { - if (PzemDc.energy > PzemDc.last_energy) { - if (uptime > PZEM_DC_STABILIZE) { - EnergyUpdateTotal(PzemDc.energy, false); - } - PzemDc.last_energy = PzemDc.energy; - } - PzemDc.energy = 0; - } - } - } - } - - if (0 == PzemDc.send_retry || data_ready) { - if (0 == PzemDc.channel) { - PzemDc.channel = Energy.phase_count -1; - } else { - PzemDc.channel--; - } - PzemDc.send_retry = ENERGY_WATCHDOG; - if (ADDR_SEND == PzemDc.address_step) { - PzemDcModbus->Send(0xF8, 0x06, 0x0002, (uint16_t)PzemDc.address); - PzemDc.address_step--; - } else { - PzemDcModbus->Send(PZEM_DC_DEVICE_ADDRESS + PzemDc.channel, 0x04, 0, 8); - } - } - else { - PzemDc.send_retry--; - if ((Energy.phase_count > 1) && (0 == PzemDc.send_retry) && (uptime < PZEM_DC_STABILIZE)) { - Energy.phase_count--; - } - } -} - -void PzemDcSnsInit(void) -{ - PzemDcModbus = new TasmotaModbus(pin[GPIO_PZEM017_RX], pin[GPIO_PZEM0XX_TX]); - uint8_t result = PzemDcModbus->Begin(9600, 2); - if (result) { - if (2 == result) { ClaimSerial(); } - Energy.type_dc = true; - Energy.phase_count = 3; - PzemDc.channel = 0; - } else { - energy_flg = ENERGY_NONE; - } -} - -void PzemDcDrvInit(void) -{ - if ((pin[GPIO_PZEM017_RX] < 99) && (pin[GPIO_PZEM0XX_TX] < 99)) { - energy_flg = XNRG_06; - } -} - -bool PzemDcCommand(void) -{ - bool serviced = true; - - if (CMND_MODULEADDRESS == Energy.command_code) { - PzemDc.address = XdrvMailbox.payload; - PzemDc.address_step = ADDR_SEND; - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg06(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_ENERGY_EVERY_SECOND: - if (uptime > 4) { PzemDcEverySecond(); } - break; - case FUNC_COMMAND: - result = PzemDcCommand(); - break; - case FUNC_INIT: - PzemDcSnsInit(); - break; - case FUNC_PRE_INIT: - PzemDcDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_07_ade7953.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_07_ade7953.ino" -#ifdef USE_I2C -#ifdef USE_ENERGY_SENSOR -#ifdef USE_ADE7953 -# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_07_ade7953.ino" -#define XNRG_07 7 -#define XI2C_07 7 - -#define ADE7953_PREF 1540 -#define ADE7953_UREF 26000 -#define ADE7953_IREF 10000 - -#define ADE7953_ADDR 0x38 - -const uint16_t Ade7953Registers[] { - 0x31B, - 0x313, - 0x311, - 0x315, - 0x31A, - 0x312, - 0x310, - 0x314, - 0x31C, - 0x10E -}; - -struct Ade7953 { - uint32_t voltage_rms = 0; - uint32_t period = 0; - uint32_t current_rms[2] = { 0, 0 }; - uint32_t active_power[2] = { 0, 0 }; - uint8_t init_step = 0; -} Ade7953; - -int Ade7953RegSize(uint16_t reg) -{ - int size = 0; - switch ((reg >> 8) & 0x0F) { - case 0x03: - size++; - case 0x02: - size++; - case 0x01: - size++; - case 0x00: - case 0x07: - case 0x08: - size++; - } - return size; -} - -void Ade7953Write(uint16_t reg, uint32_t val) -{ - int size = Ade7953RegSize(reg); - if (size) { - Wire.beginTransmission(ADE7953_ADDR); - Wire.write((reg >> 8) & 0xFF); - Wire.write(reg & 0xFF); - while (size--) { - Wire.write((val >> (8 * size)) & 0xFF); - } - Wire.endTransmission(); - delayMicroseconds(5); - } -} - -int32_t Ade7953Read(uint16_t reg) -{ - uint32_t response = 0; - - int size = Ade7953RegSize(reg); - if (size) { - Wire.beginTransmission(ADE7953_ADDR); - Wire.write((reg >> 8) & 0xFF); - Wire.write(reg & 0xFF); - Wire.endTransmission(0); - Wire.requestFrom(ADE7953_ADDR, size); - if (size <= Wire.available()) { - for (uint32_t i = 0; i < size; i++) { - response = response << 8 | Wire.read(); - } - } - } - return response; -} - -void Ade7953Init(void) -{ - Ade7953Write(0x102, 0x0004); - Ade7953Write(0x0FE, 0x00AD); - Ade7953Write(0x120, 0x0030); -} - -void Ade7953GetData(void) -{ - int32_t reg[2][4]; - for (uint32_t i = 0; i < sizeof(Ade7953Registers)/sizeof(uint16_t); i++) { - int32_t value = Ade7953Read(Ade7953Registers[i]); - if (8 == i) { - Ade7953.voltage_rms = value; - } else if (9 == i) { - Ade7953.period = value; - } else { - reg[i >> 2][i &3] = value; - } - } - AddLog_P2(LOG_LEVEL_DEBUG_MORE, PSTR("ADE: %d, %d, [%d, %d, %d, %d], [%d, %d, %d, %d]"), - Ade7953.voltage_rms, Ade7953.period, - reg[0][0], reg[0][1], reg[0][2], reg[0][3], - reg[1][0], reg[1][1], reg[1][2], reg[1][3]); - - uint32_t apparent_power[2] = { 0, 0 }; - uint32_t reactive_power[2] = { 0, 0 }; - - for (uint32_t channel = 0; channel < 2; channel++) { - Ade7953.current_rms[channel] = reg[channel][0]; - if (Ade7953.current_rms[channel] < 2000) { - Ade7953.current_rms[channel] = 0; - Ade7953.active_power[channel] = 0; - } else { - Ade7953.active_power[channel] = abs(reg[channel][1]); - apparent_power[channel] = abs(reg[channel][2]); - reactive_power[channel] = abs(reg[channel][3]); - } - } - - uint32_t current_rms_sum = Ade7953.current_rms[0] + Ade7953.current_rms[1]; - uint32_t active_power_sum = Ade7953.active_power[0] + Ade7953.active_power[1]; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ADE: U %d, C %d, I %d + %d = %d, P %d + %d = %d"), - Ade7953.voltage_rms, Ade7953.period, - Ade7953.current_rms[0], Ade7953.current_rms[1], current_rms_sum, - Ade7953.active_power[0], Ade7953.active_power[1], active_power_sum); - - if (Energy.power_on) { - Energy.voltage[0] = (float)Ade7953.voltage_rms / Settings.energy_voltage_calibration; - Energy.frequency[0] = 223750.0f / ( (float)Ade7953.period + 1); - - for (uint32_t channel = 0; channel < 2; channel++) { - Energy.data_valid[channel] = 0; - Energy.active_power[channel] = (float)Ade7953.active_power[channel] / (Settings.energy_power_calibration / 10); - Energy.reactive_power[channel] = (float)reactive_power[channel] / (Settings.energy_power_calibration / 10); - Energy.apparent_power[channel] = (float)apparent_power[channel] / (Settings.energy_power_calibration / 10); - if (0 == Energy.active_power[channel]) { - Energy.current[channel] = 0; - } else { - Energy.current[channel] = (float)Ade7953.current_rms[channel] / (Settings.energy_current_calibration * 10); - } - } - } else { - Energy.data_valid[0] = ENERGY_WATCHDOG; - Energy.data_valid[1] = ENERGY_WATCHDOG; - } - - if (active_power_sum) { - Energy.kWhtoday_delta += ((active_power_sum * (100000 / (Settings.energy_power_calibration / 10))) / 3600); - EnergyUpdateToday(); - } -} - -void Ade7953EnergyEverySecond(void) -{ - if (Ade7953.init_step) { - if (1 == Ade7953.init_step) { - Ade7953Init(); - } - Ade7953.init_step--; - } else { - Ade7953GetData(); - } -} - -void Ade7953DrvInit(void) -{ - if (pin[GPIO_ADE7953_IRQ] < 99) { - delay(100); - if (I2cSetDevice(ADE7953_ADDR)) { - if (HLW_PREF_PULSE == Settings.energy_power_calibration) { - Settings.energy_power_calibration = ADE7953_PREF; - Settings.energy_voltage_calibration = ADE7953_UREF; - Settings.energy_current_calibration = ADE7953_IREF; - } - I2cSetActiveFound(ADE7953_ADDR, "ADE7953"); - Ade7953.init_step = 2; - - Energy.phase_count = 2; - Energy.voltage_common = true; - - energy_flg = XNRG_07; - } - } -} - -bool Ade7953Command(void) -{ - bool serviced = true; - - uint32_t channel = (2 == XdrvMailbox.index) ? 1 : 0; - uint32_t value = (uint32_t)(CharToFloat(XdrvMailbox.data) * 100); - - if (CMND_POWERCAL == Energy.command_code) { - if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_PREF; } - - } - else if (CMND_VOLTAGECAL == Energy.command_code) { - if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_UREF; } - - } - else if (CMND_CURRENTCAL == Energy.command_code) { - if (1 == XdrvMailbox.payload) { XdrvMailbox.payload = ADE7953_IREF; } - - } - else if (CMND_POWERSET == Energy.command_code) { - if (XdrvMailbox.data_len && Ade7953.active_power[channel]) { - if ((value > 100) && (value < 200000)) { - Settings.energy_power_calibration = (Ade7953.active_power[channel] * 1000) / value; - } - } - } - else if (CMND_VOLTAGESET == Energy.command_code) { - if (XdrvMailbox.data_len && Ade7953.voltage_rms) { - if ((value > 10000) && (value < 26000)) { - Settings.energy_voltage_calibration = (Ade7953.voltage_rms * 100) / value; - } - } - } - else if (CMND_CURRENTSET == Energy.command_code) { - if (XdrvMailbox.data_len && Ade7953.current_rms[channel]) { - if ((value > 2000) && (value < 1000000)) { - Settings.energy_current_calibration = ((Ade7953.current_rms[channel] * 100) / value) * 100; - } - } - } - else serviced = false; - - return serviced; -} - - - - - -bool Xnrg07(uint8_t function) -{ - if (!I2cEnabled(XI2C_07)) { return false; } - - bool result = false; - - switch (function) { - case FUNC_ENERGY_EVERY_SECOND: - Ade7953EnergyEverySecond(); - break; - case FUNC_COMMAND: - result = Ade7953Command(); - break; - case FUNC_PRE_INIT: - Ade7953DrvInit(); - break; - } - return result; -} - -#endif -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_08_sdm120.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_08_sdm120.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_SDM120 - - - - - - -#define XNRG_08 8 - - -#ifndef SDM120_SPEED - #define SDM120_SPEED 2400 -#endif - -#ifndef SDM120_ADDR - #define SDM120_ADDR 1 -#endif - -#include -TasmotaModbus *Sdm120Modbus; - -const uint8_t sdm120_table = 8; -const uint8_t sdm220_table = 13; - -const uint16_t sdm120_start_addresses[] { - 0x0000, - 0x0006, - 0x000C, - 0x0012, - 0x0018, - 0x001E, - 0x0046, - 0x0156, - - 0X0048, - 0X004A, - 0X004C, - 0X004E, - 0X0024 -}; - -struct SDM120 { - float total_active = 0; - float import_active = NAN; - float import_reactive = 0; - float export_reactive = 0; - float phase_angle = 0; - uint8_t read_state = 0; - uint8_t send_retry = 0; - uint8_t start_address_count = sdm220_table; -} Sdm120; - - - -void SDM120Every250ms(void) -{ - bool data_ready = Sdm120Modbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[14]; - - uint32_t error = Sdm120Modbus->ReceiveBuffer(buffer, 2); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm120Modbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM120 error %d"), error); - } else { - Energy.data_valid[0] = 0; - - - - - float value; - ((uint8_t*)&value)[3] = buffer[3]; - ((uint8_t*)&value)[2] = buffer[4]; - ((uint8_t*)&value)[1] = buffer[5]; - ((uint8_t*)&value)[0] = buffer[6]; - - switch(Sdm120.read_state) { - case 0: - Energy.voltage[0] = value; - break; - - case 1: - Energy.current[0] = value; - break; - - case 2: - Energy.active_power[0] = value; - break; - - case 3: - Energy.apparent_power[0] = value; - break; - - case 4: - Energy.reactive_power[0] = value; - break; - - case 5: - Energy.power_factor[0] = value; - break; - - case 6: - Energy.frequency[0] = value; - break; - - case 7: - Sdm120.total_active = value; - break; - - case 8: - Sdm120.import_active = value; - break; - - case 9: - Energy.export_active = value; - break; - - case 10: - Sdm120.import_reactive = value; - break; - - case 11: - Sdm120.export_reactive = value; - break; - - case 12: - Sdm120.phase_angle = value; - break; - } - - Sdm120.read_state++; - if (Sdm120.read_state == Sdm120.start_address_count) { - Sdm120.read_state = 0; - - if (Sdm120.start_address_count > sdm120_table) { - if (!isnan(Sdm120.import_active)) { - Sdm120.total_active = Sdm120.import_active; - } else { - Sdm120.start_address_count = sdm120_table; - } - } - EnergyUpdateTotal(Sdm120.total_active, true); - } - } - } - - if (0 == Sdm120.send_retry || data_ready) { - Sdm120.send_retry = 5; - Sdm120Modbus->Send(SDM120_ADDR, 0x04, sdm120_start_addresses[Sdm120.read_state], 2); - } else { - Sdm120.send_retry--; - } -} - -void Sdm120SnsInit(void) -{ - Sdm120Modbus = new TasmotaModbus(pin[GPIO_SDM120_RX], pin[GPIO_SDM120_TX]); - uint8_t result = Sdm120Modbus->Begin(SDM120_SPEED); - if (result) { - if (2 == result) { ClaimSerial(); } - } else { - energy_flg = ENERGY_NONE; - } -} - -void Sdm120DrvInit(void) -{ - if ((pin[GPIO_SDM120_RX] < 99) && (pin[GPIO_SDM120_TX] < 99)) { - energy_flg = XNRG_08; - } -} - -void Sdm220Reset(void) -{ - if (isnan(Sdm120.import_active)) { return; } - - Sdm120.import_active = 0; - Sdm120.import_reactive = 0; - Sdm120.export_reactive = 0; - Sdm120.phase_angle = 0; -} - -#ifdef USE_WEBSERVER -const char HTTP_ENERGY_SDM220[] PROGMEM = - "{s}" D_IMPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" - "{s}" D_EXPORT_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" - "{s}" D_PHASE_ANGLE "{m}%s " D_UNIT_ANGLE "{e}"; -#endif - -void Sdm220Show(bool json) -{ - if (isnan(Sdm120.import_active)) { return; } - - char import_active_chr[FLOATSZ]; - dtostrfd(Sdm120.import_active, Settings.flag2.energy_resolution, import_active_chr); - char import_reactive_chr[FLOATSZ]; - dtostrfd(Sdm120.import_reactive, Settings.flag2.energy_resolution, import_reactive_chr); - char export_reactive_chr[FLOATSZ]; - dtostrfd(Sdm120.export_reactive, Settings.flag2.energy_resolution, export_reactive_chr); - char phase_angle_chr[FLOATSZ]; - dtostrfd(Sdm120.phase_angle, 2, phase_angle_chr); - - if (json) { - ResponseAppend_P(PSTR(",\"" D_JSON_IMPORT_ACTIVE "\":%s,\"" D_JSON_IMPORT_REACTIVE "\":%s,\"" D_JSON_EXPORT_REACTIVE "\":%s,\"" D_JSON_PHASE_ANGLE "\":%s"), - import_active_chr, import_reactive_chr, export_reactive_chr, phase_angle_chr); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_ENERGY_SDM220, import_reactive_chr, export_reactive_chr, phase_angle_chr); -#endif - } -} - - - - - -bool Xnrg08(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - if (uptime > 4) { SDM120Every250ms(); } - break; - case FUNC_JSON_APPEND: - Sdm220Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Sdm220Show(0); - break; -#endif - case FUNC_ENERGY_RESET: - Sdm220Reset(); - break; - case FUNC_INIT: - Sdm120SnsInit(); - break; - case FUNC_PRE_INIT: - Sdm120DrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_09_dds2382.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_09_dds2382.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_DDS2382 - - - - - - -#define XNRG_09 9 - -#ifndef DDS2382_SPEED -#define DDS2382_SPEED 9600 -#endif -#ifndef DDS2382_ADDR -#define DDS2382_ADDR 1 -#endif - -#include -TasmotaModbus *Dds2382Modbus; - -uint8_t Dds2382_send_retry = 0; - -void Dds2382EverySecond(void) -{ - bool data_ready = Dds2382Modbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[46]; - - uint32_t error = Dds2382Modbus->ReceiveBuffer(buffer, 18); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Dds2382Modbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "DDS2382 response error %d"), error); - } else { - Energy.data_valid[0] = 0; -# 67 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_09_dds2382.ino" - Energy.voltage[0] = (float)((buffer[27] << 8) + buffer[28]) / 10.0; - Energy.current[0] = (float)((buffer[29] << 8) + buffer[30]) / 100.0; - Energy.active_power[0] = (float)((buffer[31] << 8) + buffer[32]); - Energy.reactive_power[0] = (float)((buffer[33] << 8) + buffer[34]); - Energy.power_factor[0] = (float)((buffer[35] << 8) + buffer[36]) / 1000.0; - Energy.frequency[0] = (float)((buffer[37] << 8) + buffer[38]) / 100.0; - uint8_t offset = 11; - if (Settings.flag3.dds2382_model) { - offset = 19; - } - Energy.export_active = (float)((buffer[offset] << 24) + (buffer[offset +1] << 16) + (buffer[offset +2] << 8) + buffer[offset +3]) / 100.0; - float import_active = (float)((buffer[offset +4] << 24) + (buffer[offset +5] << 16) + (buffer[offset +6] << 8) + buffer[offset +7]) / 100.0; - - EnergyUpdateTotal(import_active, true); - } - } - - if (0 == Dds2382_send_retry || data_ready) { - Dds2382_send_retry = 5; - Dds2382Modbus->Send(DDS2382_ADDR, 0x03, 0, 18); - } else { - Dds2382_send_retry--; - } -} - -void Dds2382SnsInit(void) -{ - Dds2382Modbus = new TasmotaModbus(pin[GPIO_DDS2382_RX], pin[GPIO_DDS2382_TX]); - uint8_t result = Dds2382Modbus->Begin(DDS2382_SPEED); - if (result) { - if (2 == result) { ClaimSerial(); } - } else { - energy_flg = ENERGY_NONE; - } -} - -void Dds2382DrvInit(void) -{ - if ((pin[GPIO_DDS2382_RX] < 99) && (pin[GPIO_DDS2382_TX] < 99)) { - energy_flg = XNRG_09; - } -} - - - - - -bool Xnrg09(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_ENERGY_EVERY_SECOND: - if (uptime > 4) { Dds2382EverySecond(); } - break; - case FUNC_INIT: - Dds2382SnsInit(); - break; - case FUNC_PRE_INIT: - Dds2382DrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_10_sdm630.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_10_sdm630.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_SDM630 - - - - - - -#define XNRG_10 10 - - -#ifndef SDM630_SPEED - #define SDM630_SPEED 9600 -#endif - -#ifndef SDM630_ADDR - #define SDM630_ADDR 1 -#endif - -#include -TasmotaModbus *Sdm630Modbus; - -const uint16_t sdm630_start_addresses[] { - 0x0000, - 0x0002, - 0x0004, - 0x0006, - 0x0008, - 0x000A, - 0x000C, - 0x000E, - 0x0010, - 0x0018, - 0x001A, - 0x001C, - 0x001E, - 0x0020, - 0x0022, - 0x0156 -}; - -struct SDM630 { - uint8_t read_state = 0; - uint8_t send_retry = 0; -} Sdm630; - - - -void SDM630Every250ms(void) -{ - bool data_ready = Sdm630Modbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[14]; - - uint32_t error = Sdm630Modbus->ReceiveBuffer(buffer, 2); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Sdm630Modbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: SDM630 error %d"), error); - } else { - Energy.data_valid[0] = 0; - Energy.data_valid[1] = 0; - Energy.data_valid[2] = 0; - - - - - float value; - ((uint8_t*)&value)[3] = buffer[3]; - ((uint8_t*)&value)[2] = buffer[4]; - ((uint8_t*)&value)[1] = buffer[5]; - ((uint8_t*)&value)[0] = buffer[6]; - - switch(Sdm630.read_state) { - case 0: - Energy.voltage[0] = value; - break; - - case 1: - Energy.voltage[1] = value; - break; - - case 2: - Energy.voltage[2] = value; - break; - - case 3: - Energy.current[0] = value; - break; - - case 4: - Energy.current[1] = value; - break; - - case 5: - Energy.current[2] = value; - break; - - case 6: - Energy.active_power[0] = value; - break; - - case 7: - Energy.active_power[1] = value; - break; - - case 8: - Energy.active_power[2] = value; - break; - - case 9: - Energy.reactive_power[0] = value; - break; - - case 10: - Energy.reactive_power[1] = value; - break; - - case 11: - Energy.reactive_power[2] = value; - break; - - case 12: - Energy.power_factor[0] = value; - break; - - case 13: - Energy.power_factor[1] = value; - break; - - case 14: - Energy.power_factor[2] = value; - break; - - case 15: - EnergyUpdateTotal(value, true); - break; - } - - Sdm630.read_state++; - if (sizeof(sdm630_start_addresses)/2 == Sdm630.read_state) { - Sdm630.read_state = 0; - } - } - } - - if (0 == Sdm630.send_retry || data_ready) { - Sdm630.send_retry = 5; - Sdm630Modbus->Send(SDM630_ADDR, 0x04, sdm630_start_addresses[Sdm630.read_state], 2); - } else { - Sdm630.send_retry--; - } -} - -void Sdm630SnsInit(void) -{ - Sdm630Modbus = new TasmotaModbus(pin[GPIO_SDM630_RX], pin[GPIO_SDM630_TX]); - uint8_t result = Sdm630Modbus->Begin(SDM630_SPEED); - if (result) { - if (2 == result) { ClaimSerial(); } - Energy.phase_count = 3; - } else { - energy_flg = ENERGY_NONE; - } -} - -void Sdm630DrvInit(void) -{ - if ((pin[GPIO_SDM630_RX] < 99) && (pin[GPIO_SDM630_TX] < 99)) { - energy_flg = XNRG_10; - } -} - - - - - -bool Xnrg10(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - if (uptime > 4) { SDM630Every250ms(); } - break; - case FUNC_INIT: - Sdm630SnsInit(); - break; - case FUNC_PRE_INIT: - Sdm630DrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_11_ddsu666.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_11_ddsu666.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_DDSU666 - - - - -#define XNRG_11 11 - - -#ifndef DDSU666_SPEED - #define DDSU666_SPEED 9600 -#endif - -#ifndef DDSU666_ADDR - #define DDSU666_ADDR 1 -#endif - -#include -TasmotaModbus *Ddsu666Modbus; - -const uint16_t Ddsu666_start_addresses[] { - 0x2000, - 0x2002, - 0x2004, - 0x2006, - 0x200A, - 0x200E, - 0X4000, - 0X400A, -}; - -struct DDSU666 { - float import_active = NAN; - uint8_t read_state = 0; - uint8_t send_retry = 0; -} Ddsu666; - - - -void DDSU666Every250ms(void) -{ - bool data_ready = Ddsu666Modbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[14]; - - uint32_t error = Ddsu666Modbus->ReceiveBuffer(buffer, 2); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, Ddsu666Modbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SDM: Ddsu666 error %d"), error); - } else { - Energy.data_valid[0] = 0; - - - - - float value; - ((uint8_t*)&value)[3] = buffer[3]; - ((uint8_t*)&value)[2] = buffer[4]; - ((uint8_t*)&value)[1] = buffer[5]; - ((uint8_t*)&value)[0] = buffer[6]; - - switch(Ddsu666.read_state) { - case 0: - Energy.voltage[0] = value; - break; - - case 1: - Energy.current[0] = value; - break; - - case 2: - Energy.active_power[0] = value * 1000; - break; - - case 3: - Energy.reactive_power[0] = value * 1000; - break; - - case 4: - Energy.power_factor[0] = value; - break; - - case 5: - Energy.frequency[0] = value; - break; - - case 6: - Ddsu666.import_active = value; - break; - - case 7: - Energy.export_active = value; - break; - } - - Ddsu666.read_state++; - - if (Ddsu666.read_state == 8) { - Ddsu666.read_state = 0; - EnergyUpdateTotal(Ddsu666.import_active, true); - } - } - } - - if (0 == Ddsu666.send_retry || data_ready) { - Ddsu666.send_retry = 5; - Ddsu666Modbus->Send(DDSU666_ADDR, 0x04, Ddsu666_start_addresses[Ddsu666.read_state], 2); - } else { - Ddsu666.send_retry--; - } -} - -void Ddsu666SnsInit(void) -{ - Ddsu666Modbus = new TasmotaModbus(pin[GPIO_DDSU666_RX], pin[GPIO_DDSU666_TX]); - uint8_t result = Ddsu666Modbus->Begin(DDSU666_SPEED); - if (result) { - if (2 == result) { ClaimSerial(); } - } else { - energy_flg = ENERGY_NONE; - } -} - -void Ddsu666DrvInit(void) -{ - if ((pin[GPIO_DDSU666_RX] < 99) && (pin[GPIO_DDSU666_TX] < 99)) { - energy_flg = XNRG_11; - } -} - - - - - -bool Xnrg11(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - if (uptime > 4) { DDSU666Every250ms(); } - break; - case FUNC_INIT: - Ddsu666SnsInit(); - break; - case FUNC_PRE_INIT: - Ddsu666DrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_12_solaxX1.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_12_solaxX1.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_SOLAX_X1 - - - - -#define XNRG_12 12 - -#ifndef SOLAXX1_SPEED -#define SOLAXX1_SPEED 9600 -#endif - -#define INVERTER_ADDRESS 0x0A - -#define D_SOLAX_X1 "SolaxX1" - -#include - -enum solaxX1_Error -{ - solaxX1_ERR_NO_ERROR, - solaxX1_ERR_CRC_ERROR -}; - -union { - uint32_t ErrMessage; - struct { - - uint8_t TzProtectFault:1; - uint8_t MainsLostFault:1; - uint8_t GridVoltFault:1; - uint8_t GridFreqFault:1; - uint8_t PLLLostFault:1; - uint8_t BusVoltFault:1; - uint8_t ErrBit06:1; - uint8_t OciFault:1; - - uint8_t Dci_OCP_Fault:1; - uint8_t ResidualCurrentFault:1; - uint8_t PvVoltFault:1; - uint8_t Ac10Mins_Voltage_Fault:1; - uint8_t IsolationFault:1; - uint8_t TemperatureOverFault:1; - uint8_t FanFault:1; - uint8_t ErrBit15:1; - - uint8_t SpiCommsFault:1; - uint8_t SciCommsFault:1; - uint8_t ErrBit18:1; - uint8_t InputConfigFault:1; - uint8_t EepromFault:1; - uint8_t RelayFault:1; - uint8_t SampleConsistenceFault:1; - uint8_t ResidualCurrent_DeviceFault:1; - - uint8_t ErrBit24:1; - uint8_t ErrBit25:1; - uint8_t ErrBit26:1; - uint8_t ErrBit27:1; - uint8_t ErrBit28:1; - uint8_t DCI_DeviceFault:1; - uint8_t OtherDeviceFault:1; - uint8_t ErrBit31:1; - }; -} ErrCode; - -const char kSolaxMode[] PROGMEM = D_WAITING "|" D_CHECKING "|" D_WORKING "|" D_FAILURE; - -const char kSolaxError[] PROGMEM = - D_SOLAX_ERROR_0 "|" D_SOLAX_ERROR_1 "|" D_SOLAX_ERROR_2 "|" D_SOLAX_ERROR_3 "|" D_SOLAX_ERROR_4 "|" D_SOLAX_ERROR_5 "|" - D_SOLAX_ERROR_6 "|" D_SOLAX_ERROR_7 "|" D_SOLAX_ERROR_8; - - - -TasmotaSerial *solaxX1Serial; - -uint8_t solaxX1_Init = 1; - -struct SOLAXX1 { - float temperature = 0; - float energy_today = 0; - float dc1_voltage = 0; - float dc2_voltage = 0; - float dc1_current = 0; - float dc2_current = 0; - float energy_total = 0; - float runtime_total = 0; - float dc1_power = 0; - float dc2_power = 0; - - uint8_t status = 0; - uint32_t errorCode = 0; -} solaxX1; - -union { - uint8_t status; - struct { - uint8_t freeBit7:1; - uint8_t freeBit6:1; - uint8_t freeBit5:1; - uint8_t queryOffline:1; - uint8_t queryOfflineSend:1; - uint8_t hasAddress:1; - uint8_t inverterAddressSend:1; - uint8_t inverterSnReceived:1; - }; -} protocolStatus; - -uint8_t header[2] = {0xAA, 0x55}; -uint8_t source[2] = {0x00, 0x00}; -uint8_t destination[2] = {0x00, 0x00}; -uint8_t controlCode[1] = {0x00}; -uint8_t functionCode[1] = {0x00}; -uint8_t dataLength[1] = {0x00}; -uint8_t data[16] = {0}; - -uint8_t message[30]; - - - -bool solaxX1_RS485ReceiveReady(void) -{ - return (solaxX1Serial->available() > 1); -} - -void solaxX1_RS485Send(uint16_t msgLen) -{ - memcpy(message, header, 2); - memcpy(message + 2, source, 2); - memcpy(message + 4, destination, 2); - memcpy(message + 6, controlCode, 1); - memcpy(message + 7, functionCode, 1); - memcpy(message + 8, dataLength, 1); - memcpy(message + 9, data, sizeof(data)); - uint16_t crc = solaxX1_calculateCRC(message, msgLen); - - while (solaxX1Serial->available() > 0) - { - solaxX1Serial->read(); - } - - solaxX1Serial->flush(); - solaxX1Serial->write(message, msgLen); - solaxX1Serial->write(highByte(crc)); - solaxX1Serial->write(lowByte(crc)); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, msgLen); -} - -uint8_t solaxX1_RS485Receive(uint8_t *value) -{ - uint8_t len = 0; - - while (solaxX1Serial->available() > 0) - { - value[len++] = (uint8_t)solaxX1Serial->read(); - } - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, value, len); - - uint16_t crc = solaxX1_calculateCRC(value, len - 2); - - if (value[len - 1] == lowByte(crc) && value[len - 2] == highByte(crc)) - { - return solaxX1_ERR_NO_ERROR; - } - else - { - return solaxX1_ERR_CRC_ERROR; - } -} - -uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) -{ - uint8_t i; - uint16_t wChkSum; - wChkSum = 0; - - for (i = 0; i < bLen; i++) - { - wChkSum = wChkSum + bExternTxPackage[i]; - } - return wChkSum; -} - -void solaxX1_SendInverterAddress(void) -{ - source[0] = 0x00; - destination[0] = 0x00; - destination[1] = 0x00; - controlCode[0] = 0x10; - functionCode[0] = 0x01; - dataLength[0] = 0x0F; - data[14] = INVERTER_ADDRESS; - solaxX1_RS485Send(24); -} - -void solaxX1_QueryLiveData(void) -{ - source[0] = 0x01; - destination[0] = 0x00; - destination[1] = INVERTER_ADDRESS; - controlCode[0] = 0x11; - functionCode[0] = 0x02; - dataLength[0] = 0x00; - solaxX1_RS485Send(9); -} - -uint8_t solaxX1_ParseErrorCode(uint32_t code){ - ErrCode.ErrMessage = code; - - if (code == 0) return 0; - if (ErrCode.MainsLostFault) return 1; - if (ErrCode.GridVoltFault) return 2; - if (ErrCode.GridFreqFault) return 3; - if (ErrCode.PvVoltFault) return 4; - if (ErrCode.IsolationFault) return 5; - if (ErrCode.TemperatureOverFault) return 6; - if (ErrCode.FanFault) return 7; - if (ErrCode.OtherDeviceFault) return 8; -} - - - -uint8_t solaxX1_send_retry = 0; -uint8_t solaxX1_nodata_count = 0; - -void solaxX1250MSecond(void) -{ - uint8_t value[61] = {0}; - bool data_ready = solaxX1_RS485ReceiveReady(); - - if (protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) - { - if (data_ready) - { - uint8_t error = solaxX1_RS485Receive(value); - if (error) - { - DEBUG_SENSOR_LOG(PSTR("SX1: Data response CRC error")); - } - else - { - solaxX1_nodata_count = 0; - solaxX1_send_retry = 12; - Energy.data_valid[0] = 0; - - solaxX1.temperature = (float)((value[9] << 8) | value[10]); - solaxX1.energy_today = (float)((value[11] << 8) | value[12]) * 0.1f; - solaxX1.dc1_voltage = (float)((value[13] << 8) | value[14]) * 0.1f; - solaxX1.dc2_voltage = (float)((value[15] << 8) | value[16]) * 0.1f; - solaxX1.dc1_current = (float)((value[17] << 8) | value[18]) * 0.1f; - solaxX1.dc2_current = (float)((value[19] << 8) | value[20]) * 0.1f; - Energy.current[0] = (float)((value[21] << 8) | value[22]) * 0.1f; - Energy.voltage[0] = (float)((value[23] << 8) | value[24]) * 0.1f; - Energy.frequency[0] = (float)((value[25] << 8) | value[26]) * 0.01f; - Energy.active_power[0] = (float)((value[27] << 8) | value[28]); - - solaxX1.energy_total = (float)((value[31] << 8) | (value[32] << 8) | (value[33] << 8) | value[34]) * 0.1f; - solaxX1.runtime_total = (float)((value[35] << 8) | (value[36] << 8) | (value[37] << 8) | value[38]); - solaxX1.status = (uint8_t)((value[39] << 8) | value[40]); - - - - - - - - solaxX1.errorCode = (uint32_t)((value[58] << 8) | (value[57] << 8) | (value[56] << 8) | value[55]); - - solaxX1.dc1_power = solaxX1.dc1_voltage * solaxX1.dc1_current; - solaxX1.dc2_power = solaxX1.dc2_voltage * solaxX1.dc2_current; - - solaxX1_QueryLiveData(); - EnergyUpdateTotal(solaxX1.energy_total, true); - } - } - - if (0 == solaxX1_send_retry && 255 != solaxX1_nodata_count) { - solaxX1_send_retry = 12; - solaxX1_QueryLiveData(); - } - - - - if (255 == solaxX1_nodata_count) { - solaxX1_nodata_count = 0; - solaxX1_send_retry = 12; - } - } - else - { - if ((solaxX1_nodata_count % 4) == 0) { DEBUG_SENSOR_LOG(PSTR("SX1: No Data count: %d"), solaxX1_nodata_count); } - if (solaxX1_nodata_count < 10 * 4) - { - solaxX1_nodata_count++; - } - else if (255 != solaxX1_nodata_count) - { - - solaxX1_nodata_count = 255; - solaxX1_send_retry = 12; - protocolStatus.status = 0b00001000; - Energy.data_valid[0] = ENERGY_WATCHDOG; - - solaxX1.temperature = solaxX1.dc1_voltage = solaxX1.dc2_voltage = solaxX1.dc1_current = solaxX1.dc2_current = solaxX1.dc1_power = 0; - solaxX1.dc2_power = solaxX1.status = Energy.current[0] = Energy.voltage[0] = Energy.frequency[0] = Energy.active_power[0] = 0; - - } - } - - if (!protocolStatus.hasAddress && (data_ready || solaxX1_send_retry == 0)) - { - if (data_ready) - { - - if (protocolStatus.inverterAddressSend) - { - uint8_t error = solaxX1_RS485Receive(value); - if (error) - { - DEBUG_SENSOR_LOG(PSTR("SX1: Address confirmation response CRC error")); - } - else - { - if (value[6] == 0x10 && value[7] == 0x81 && value[9] == 0x06) - { - DEBUG_SENSOR_LOG(PSTR("SX1: Set hasAddress")); - protocolStatus.status = 0b00100000; - } - } - } - - - if (protocolStatus.queryOfflineSend) - { - uint8_t error = solaxX1_RS485Receive(value); - if (error) - { - DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline response CRC error")); - } - else - { - - if (value[6] == 0x10 && value[7] == 0x80 && protocolStatus.inverterSnReceived == false) - { - for (uint8_t i = 9; i <= 22; i++) - { - data[i - 9] = value[i]; - } - solaxX1_SendInverterAddress(); - protocolStatus.status = 0b1100000; - DEBUG_SENSOR_LOG(PSTR("SX1: Set inverterSnReceived and inverterAddressSend")); - } - } - } - } - - if (solaxX1_send_retry == 0) - { - if (protocolStatus.queryOfflineSend) - { - protocolStatus.status = 0b00001000; - DEBUG_SENSOR_LOG(PSTR("SX1: Set Query Offline")); - } - solaxX1_send_retry = 12; - } - - - if (protocolStatus.queryOffline) - { - - source[0] = 0x01; - destination[1] = 0x00; - controlCode[0] = 0x10; - functionCode[0] = 0x00; - dataLength[0] = 0x00; - solaxX1_RS485Send(9); - protocolStatus.status = 0b00010000; - DEBUG_SENSOR_LOG(PSTR("SX1: Query Offline Send")); - } - } - - if (!data_ready) - solaxX1_send_retry--; -} - -void solaxX1SnsInit(void) -{ - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SX1: Solax X1 Inverter Init")); - DEBUG_SENSOR_LOG(PSTR("SX1: RX pin: %d, TX pin: %d"), pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX]); - protocolStatus.status = 0b00100000; - - solaxX1Serial = new TasmotaSerial(pin[GPIO_SOLAXX1_RX], pin[GPIO_SOLAXX1_TX], 1); - if (solaxX1Serial->begin(SOLAXX1_SPEED)) { - if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } - } else { - energy_flg = ENERGY_NONE; - } -} - -void solaxX1DrvInit(void) -{ - if ((pin[GPIO_SOLAXX1_RX] < 99) && (pin[GPIO_SOLAXX1_TX] < 99)) { - energy_flg = XNRG_12; - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_solaxX1_DATA1[] PROGMEM = - "{s}" D_SOLAX_X1 " " D_SOLAR_POWER "{m}%s " D_UNIT_WATT "{e}" - "{s}" D_SOLAX_X1 " " D_PV1_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" - "{s}" D_SOLAX_X1 " " D_PV1_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" - "{s}" D_SOLAX_X1 " " D_PV1_POWER "{m}%s " D_UNIT_WATT "{e}"; -#ifdef SOLAXX1_PV2 -const char HTTP_SNS_solaxX1_DATA2[] PROGMEM = - "{s}" D_SOLAX_X1 " " D_PV2_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" - "{s}" D_SOLAX_X1 " " D_PV2_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" - "{s}" D_SOLAX_X1 " " D_PV2_POWER "{m}%s " D_UNIT_WATT "{e}"; -#endif -const char HTTP_SNS_solaxX1_DATA3[] PROGMEM = - "{s}" D_SOLAX_X1 " " D_UPTIME "{m}%s " D_UNIT_HOUR "{e}" - "{s}" D_SOLAX_X1 " " D_STATUS "{m}%s" - "{s}" D_SOLAX_X1 " " D_ERROR "{m}%s"; -#endif - -void solaxX1Show(bool json) -{ - char solar_power[33]; - dtostrfd(solaxX1.dc1_power + solaxX1.dc2_power, Settings.flag2.wattage_resolution, solar_power); - char pv1_voltage[33]; - dtostrfd(solaxX1.dc1_voltage, Settings.flag2.voltage_resolution, pv1_voltage); - char pv1_current[33]; - dtostrfd(solaxX1.dc1_current, Settings.flag2.current_resolution, pv1_current); - char pv1_power[33]; - dtostrfd(solaxX1.dc1_power, Settings.flag2.wattage_resolution, pv1_power); -#ifdef SOLAXX1_PV2 - char pv2_voltage[33]; - dtostrfd(solaxX1.dc2_voltage, Settings.flag2.voltage_resolution, pv2_voltage); - char pv2_current[33]; - dtostrfd(solaxX1.dc2_current, Settings.flag2.current_resolution, pv2_current); - char pv2_power[33]; - dtostrfd(solaxX1.dc2_power, Settings.flag2.wattage_resolution, pv2_power); -#endif - char temperature[33]; - dtostrfd(solaxX1.temperature, Settings.flag2.temperature_resolution, temperature); - char runtime[33]; - dtostrfd(solaxX1.runtime_total, 0, runtime); - char status[33]; - GetTextIndexed(status, sizeof(status), solaxX1.status, kSolaxMode); - - if (json) - { - ResponseAppend_P(PSTR(",\"" D_JSON_SOLAR_POWER "\":%s,\"" D_JSON_PV1_VOLTAGE "\":%s,\"" D_JSON_PV1_CURRENT "\":%s,\"" D_JSON_PV1_POWER "\":%s"), - solar_power, pv1_voltage, pv1_current, pv1_power); -#ifdef SOLAXX1_PV2 - ResponseAppend_P(PSTR(",\"" D_JSON_PV2_VOLTAGE "\":%s,\"" D_JSON_PV2_CURRENT "\":%s,\"" D_JSON_PV2_POWER "\":%s"), - pv2_voltage, pv2_current, pv2_power); -#endif - ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RUNTIME "\":%s,\"" D_JSON_STATUS "\":\"%s\",\"" D_JSON_ERROR "\":%d"), - temperature, runtime, status, solaxX1.errorCode); - -#ifdef USE_WEBSERVER - } - else - { - WSContentSend_PD(HTTP_SNS_solaxX1_DATA1, solar_power, pv1_voltage, pv1_current, pv1_power); -#ifdef SOLAXX1_PV2 - WSContentSend_PD(HTTP_SNS_solaxX1_DATA2, pv2_voltage, pv2_current, pv2_power); -#endif - WSContentSend_PD(HTTP_SNS_TEMP, D_SOLAX_X1, temperature, TempUnit()); - char errorCodeString[33]; - WSContentSend_PD(HTTP_SNS_solaxX1_DATA3, runtime, status, - GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError)); -#endif - } -} - - - - - -bool Xnrg12(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - if (uptime > 4) { solaxX1250MSecond(); } - break; - case FUNC_JSON_APPEND: - solaxX1Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - solaxX1Show(0); - break; -#endif - case FUNC_INIT: - solaxX1SnsInit(); - break; - case FUNC_PRE_INIT: - solaxX1DrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" -#ifdef USE_ENERGY_SENSOR -#ifdef USE_LE01MR -# 71 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" -#define XNRG_13 13 - - -#ifndef LE01MR_SPEED - #define LE01MR_SPEED 2400 -#endif - -#ifndef LE01MR_ADDR - #define LE01MR_ADDR 1 -#endif - -#include -TasmotaModbus *FifLEModbus; - -const uint8_t le01mr_table_sz = 9; - -const uint16_t le01mr_register_addresses[] { - - 0x0130, - 0x0131, - 0x0158, - 0x0139, - 0x0140, - 0x0148, - 0x0150, - 0xA000, - 0xA01E -}; - -struct LE01MR { - float total_active = 0; - float total_reactive = 0; - uint8_t read_state = 0; - uint8_t send_retry = 0; - uint8_t start_address_count = le01mr_table_sz; -} Le01mr; - - - -void FifLEEvery250ms(void) -{ - bool data_ready = FifLEModbus->ReceiveReady(); - - if (data_ready) { - uint8_t buffer[14]; - uint8_t reg_count = 2; - if (Le01mr.read_state < 3) { - reg_count=1; - } - - uint32_t error = FifLEModbus->ReceiveBuffer(buffer, reg_count); - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, FifLEModbus->ReceiveCount()); - - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("FiF-LE: LE01MR Modbus error %d"), error); - } else { - Energy.data_valid[0] = 0; -# 146 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_13_fif_le01mr.ino" - uint32_t value_buff = 0; - - if (Le01mr.read_state >= 0 && Le01mr.read_state < 3) { - value_buff = ((uint32_t)buffer[3])<<8 | buffer[4]; - } else { - value_buff = ((uint32_t)buffer[3])<<24 | ((uint32_t)buffer[4])<<16 | ((uint32_t)buffer[5])<<8 | buffer[6]; - } - - switch(Le01mr.read_state) { - case 0: - Energy.frequency[0] = value_buff * 0.01f; - break; - - case 1: - Energy.voltage[0] = value_buff * 0.01f; - break; - - case 2: - Energy.power_factor[0] = ((int16_t)value_buff) * 0.001f; - break; - - case 3: - Energy.current[0] = value_buff * 0.001f; - break; - - case 4: - Energy.active_power[0] = value_buff * 1.0f; - break; - - case 5: - Energy.reactive_power[0] = value_buff * 1.0f; - break; - - case 6: - Energy.apparent_power[0] = value_buff * 1.0f; - break; - - case 7: - Le01mr.total_active = value_buff * 0.01f; - break; - - case 8: - Le01mr.total_reactive = value_buff * 0.01f; - break; - } - - Le01mr.read_state++; - if (Le01mr.read_state == Le01mr.start_address_count) { - Le01mr.read_state = 0; - - EnergyUpdateTotal(Le01mr.total_active, true); - } - } - } - - if (0 == Le01mr.send_retry || data_ready) { - uint8_t reg_count = 2; - - Le01mr.send_retry = 5; - - if (Le01mr.read_state < 3) reg_count=1; - - FifLEModbus->Send(LE01MR_ADDR, 0x03, le01mr_register_addresses[Le01mr.read_state], reg_count); - } else { - Le01mr.send_retry--; - } -} - -void FifLESnsInit(void) -{ - FifLEModbus = new TasmotaModbus(pin[GPIO_LE01MR_RX], pin[GPIO_LE01MR_TX]); - uint8_t result = FifLEModbus->Begin(LE01MR_SPEED); - if (result) { - if (2 == result) { ClaimSerial(); } - } else { - energy_flg = ENERGY_NONE; - } -} - -void FifLEDrvInit(void) -{ - if ((pin[GPIO_LE01MR_RX] < 99) && (pin[GPIO_LE01MR_TX] < 99)) { - energy_flg = XNRG_13; - } -} - -void FifLEReset(void) -{ - Le01mr.total_active = 0; - Le01mr.total_reactive = 0; -} - -#ifdef USE_WEBSERVER -const char HTTP_ENERGY_LE01MR[] PROGMEM = - "{s}" D_TOTAL_ACTIVE "{m}%s " D_UNIT_KILOWATTHOUR "{e}" - "{s}" D_TOTAL_REACTIVE "{m}%s " D_UNIT_KWARH "{e}" - ; -#endif - -void FifLEShow(bool json) -{ - char total_reactive_chr[FLOATSZ]; - dtostrfd(Le01mr.total_reactive, Settings.flag2.energy_resolution, total_reactive_chr); - char total_active_chr[FLOATSZ]; - dtostrfd(Le01mr.total_active, Settings.flag2.energy_resolution, total_active_chr); - - if (json) { - ResponseAppend_P(PSTR(",\"" D_JSON_TOTAL_ACTIVE "\":%s,\"" D_JSON_TOTAL_REACTIVE "\":%s"), - total_active_chr, total_reactive_chr); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_ENERGY_LE01MR, total_active_chr, total_reactive_chr); -#endif - } -} - - - - - -bool Xnrg13(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - if (uptime > 4) { - FifLEEvery250ms(); - } - break; - case FUNC_JSON_APPEND: - FifLEShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - FifLEShow(0); - break; -#endif - case FUNC_ENERGY_RESET: - FifLEReset(); - break; - case FUNC_INIT: - FifLESnsInit(); - break; - case FUNC_PRE_INIT: - FifLEDrvInit(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_interface.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xnrg_interface.ino" -#ifdef USE_ENERGY_SENSOR - -#ifdef XFUNC_PTR_IN_ROM -bool (* const xnrg_func_ptr[])(uint8_t) PROGMEM = { -#else -bool (* const xnrg_func_ptr[])(uint8_t) = { -#endif - -#ifdef XNRG_01 - &Xnrg01, -#endif - -#ifdef XNRG_02 - &Xnrg02, -#endif - -#ifdef XNRG_03 - &Xnrg03, -#endif - -#ifdef XNRG_04 - &Xnrg04, -#endif - -#ifdef XNRG_05 - &Xnrg05, -#endif - -#ifdef XNRG_06 - &Xnrg06, -#endif - -#ifdef XNRG_07 - &Xnrg07, -#endif - -#ifdef XNRG_08 - &Xnrg08, -#endif - -#ifdef XNRG_09 - &Xnrg09, -#endif - -#ifdef XNRG_10 - &Xnrg10, -#endif - -#ifdef XNRG_11 - &Xnrg11, -#endif - -#ifdef XNRG_12 - &Xnrg12, -#endif - -#ifdef XNRG_13 - &Xnrg13, -#endif - -#ifdef XNRG_14 - &Xnrg14, -#endif - -#ifdef XNRG_15 - &Xnrg15, -#endif - -#ifdef XNRG_16 - &Xnrg16 -#endif -}; - -const uint8_t xnrg_present = sizeof(xnrg_func_ptr) / sizeof(xnrg_func_ptr[0]); - -uint8_t xnrg_active = 0; - -bool XnrgCall(uint8_t function) -{ - DEBUG_TRACE_LOG(PSTR("NRG: %d"), function); - - if (FUNC_PRE_INIT == function) { - for (uint32_t x = 0; x < xnrg_present; x++) { - xnrg_func_ptr[x](function); - if (energy_flg) { - xnrg_active = x; - return true; - } - } - } - else if (energy_flg) { - return xnrg_func_ptr[xnrg_active](function); - } - return false; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_01_counter.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_01_counter.ino" -#ifdef USE_COUNTER - - - - -#define XSNS_01 1 - -#define D_PRFX_COUNTER "Counter" -#define D_CMND_COUNTERTYPE "Type" -#define D_CMND_COUNTERDEBOUNCE "Debounce" - -const char kCounterCommands[] PROGMEM = D_PRFX_COUNTER "|" - "|" D_CMND_COUNTERTYPE "|" D_CMND_COUNTERDEBOUNCE ; - -void (* const CounterCommand[])(void) PROGMEM = { - &CmndCounter, &CmndCounterType, &CmndCounterDebounce }; - -struct COUNTER { - uint32_t timer[MAX_COUNTERS]; - uint8_t no_pullup = 0; - bool any_counter = false; -} Counter; - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 -void CounterUpdate(uint8_t index) ICACHE_RAM_ATTR; -void CounterUpdate1(void) ICACHE_RAM_ATTR; -void CounterUpdate2(void) ICACHE_RAM_ATTR; -void CounterUpdate3(void) ICACHE_RAM_ATTR; -void CounterUpdate4(void) ICACHE_RAM_ATTR; -#endif - -void CounterUpdate(uint8_t index) -{ - uint32_t time = micros(); - uint32_t debounce_time = time - Counter.timer[index]; - if (debounce_time > Settings.pulse_counter_debounce * 1000) { - Counter.timer[index] = time; - if (bitRead(Settings.pulse_counter_type, index)) { - RtcSettings.pulse_counter[index] = debounce_time; - } else { - RtcSettings.pulse_counter[index]++; - } - } -} - -void CounterUpdate1(void) -{ - CounterUpdate(0); -} - -void CounterUpdate2(void) -{ - CounterUpdate(1); -} - -void CounterUpdate3(void) -{ - CounterUpdate(2); -} - -void CounterUpdate4(void) -{ - CounterUpdate(3); -} - - - -bool CounterPinState(void) -{ - if ((XdrvMailbox.index >= GPIO_CNTR1_NP) && (XdrvMailbox.index < (GPIO_CNTR1_NP + MAX_COUNTERS))) { - bitSet(Counter.no_pullup, XdrvMailbox.index - GPIO_CNTR1_NP); - XdrvMailbox.index -= (GPIO_CNTR1_NP - GPIO_CNTR1); - return true; - } - return false; -} - -void CounterInit(void) -{ - typedef void (*function) () ; - function counter_callbacks[] = { CounterUpdate1, CounterUpdate2, CounterUpdate3, CounterUpdate4 }; - - for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { - Counter.any_counter = true; - pinMode(pin[GPIO_CNTR1 +i], bitRead(Counter.no_pullup, i) ? INPUT : INPUT_PULLUP); - attachInterrupt(pin[GPIO_CNTR1 +i], counter_callbacks[i], FALLING); - } - } -} - -void CounterEverySecond(void) -{ - for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { - if (bitRead(Settings.pulse_counter_type, i)) { - uint32_t time = micros() - Counter.timer[i]; - if (time > 4200000000) { - RtcSettings.pulse_counter[i] = 4200000000; - } - } - } - } -} - -void CounterSaveState(void) -{ - for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { - Settings.pulse_counter[i] = RtcSettings.pulse_counter[i]; - } - } -} - -void CounterShow(bool json) -{ - bool header = false; - uint8_t dsxflg = 0; - for (uint32_t i = 0; i < MAX_COUNTERS; i++) { - if (pin[GPIO_CNTR1 +i] < 99) { - char counter[33]; - if (bitRead(Settings.pulse_counter_type, i)) { - dtostrfd((double)RtcSettings.pulse_counter[i] / 1000000, 6, counter); - } else { - dsxflg++; - snprintf_P(counter, sizeof(counter), PSTR("%lu"), RtcSettings.pulse_counter[i]); - } - - if (json) { - if (!header) { - ResponseAppend_P(PSTR(",\"COUNTER\":{")); - } - ResponseAppend_P(PSTR("%s\"C%d\":%s"), (header)?",":"", i +1, counter); - header = true; -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (1 == dsxflg)) { - DomoticzSensor(DZ_COUNT, RtcSettings.pulse_counter[i]); - dsxflg++; - } -#endif - if ((0 == tele_period ) && (Settings.flag3.counter_reset_on_tele)) { - RtcSettings.pulse_counter[i] = 0; - } -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(PSTR("{s}" D_COUNTER "%d{m}%s%s{e}"), - i +1, counter, (bitRead(Settings.pulse_counter_type, i)) ? " " D_UNIT_SECOND : ""); -#endif - } - } - } - if (header) { - ResponseJsonEnd(); - } -} - - - - - -void CmndCounter(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { - if ((XdrvMailbox.data_len > 0) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { - if ((XdrvMailbox.data[0] == '-') || (XdrvMailbox.data[0] == '+')) { - RtcSettings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; - Settings.pulse_counter[XdrvMailbox.index -1] += XdrvMailbox.payload; - } else { - RtcSettings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload; - Settings.pulse_counter[XdrvMailbox.index -1] = XdrvMailbox.payload; - } - } - ResponseCmndIdxNumber(RtcSettings.pulse_counter[XdrvMailbox.index -1]); - } -} - -void CmndCounterType(void) -{ - if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_COUNTERS)) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1) && (pin[GPIO_CNTR1 + XdrvMailbox.index -1] < 99)) { - bitWrite(Settings.pulse_counter_type, XdrvMailbox.index -1, XdrvMailbox.payload &1); - RtcSettings.pulse_counter[XdrvMailbox.index -1] = 0; - Settings.pulse_counter[XdrvMailbox.index -1] = 0; - } - ResponseCmndIdxNumber(bitRead(Settings.pulse_counter_type, XdrvMailbox.index -1)); - } -} - -void CmndCounterDebounce(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32001)) { - Settings.pulse_counter_debounce = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.pulse_counter_debounce); -} - - - - - -bool Xsns01(uint8_t function) -{ - bool result = false; - - if (Counter.any_counter) { - switch (function) { - case FUNC_EVERY_SECOND: - CounterEverySecond(); - break; - case FUNC_JSON_APPEND: - CounterShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - CounterShow(0); - break; -#endif - case FUNC_SAVE_BEFORE_RESTART: - case FUNC_SAVE_AT_MIDNIGHT: - CounterSaveState(); - break; - case FUNC_COMMAND: - result = DecodeCommand(kCounterCommands, CounterCommand); - break; - } - } else { - switch (function) { - case FUNC_INIT: - CounterInit(); - break; - case FUNC_PIN_STATE: - result = CounterPinState(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_02_analog.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_02_analog.ino" -#ifndef USE_ADC_VCC - - - - -#define XSNS_02 2 - -#define TO_CELSIUS(x) ((x) - 273.15) -#define TO_KELVIN(x) ((x) + 273.15) - - -#define ANALOG_V33 3.3 -#define ANALOG_T0 TO_KELVIN(25.0) - - - - - -#define ANALOG_NTC_BRIDGE_RESISTANCE 32000 -#define ANALOG_NTC_RESISTANCE 10000 -#define ANALOG_NTC_B_COEFFICIENT 3350 - - - - - -#define ANALOG_LDR_BRIDGE_RESISTANCE 10000 -#define ANALOG_LDR_LUX_CALC_SCALAR 12518931 -#define ANALOG_LDR_LUX_CALC_EXPONENT -1.4050 -# 58 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_02_analog.ino" -#define ANALOG_CT_FLAGS 0 -#define ANALOG_CT_MULTIPLIER 2146 -#define ANALOG_CT_VOLTAGE 2300 - -#define CT_FLAG_ENERGY_RESET (1 << 0) - -struct { - float temperature = 0; - float current = 0; - float energy = 0; - uint32_t previous_millis = 0; - uint16_t last_value = 0; -} Adc; - -void AdcInit(void) -{ - if ((Settings.adc_param_type != my_adc0) || (Settings.adc_param1 > 1000000)) { - if (ADC0_TEMP == my_adc0) { - - Settings.adc_param_type = ADC0_TEMP; - Settings.adc_param1 = ANALOG_NTC_BRIDGE_RESISTANCE; - Settings.adc_param2 = ANALOG_NTC_RESISTANCE; - Settings.adc_param3 = ANALOG_NTC_B_COEFFICIENT * 10000; - } - else if (ADC0_LIGHT == my_adc0) { - Settings.adc_param_type = ADC0_LIGHT; - Settings.adc_param1 = ANALOG_LDR_BRIDGE_RESISTANCE; - Settings.adc_param2 = ANALOG_LDR_LUX_CALC_SCALAR; - Settings.adc_param3 = ANALOG_LDR_LUX_CALC_EXPONENT * 10000; - } - else if (ADC0_RANGE == my_adc0) { - Settings.adc_param_type = ADC0_RANGE; - Settings.adc_param1 = 0; - Settings.adc_param2 = 1023; - Settings.adc_param3 = 0; - Settings.adc_param4 = 100; - } - else if (ADC0_CT_POWER == my_adc0) { - Settings.adc_param_type = ADC0_CT_POWER; - Settings.adc_param1 = ANALOG_CT_FLAGS; - Settings.adc_param2 = ANALOG_CT_MULTIPLIER; - Settings.adc_param3 = ANALOG_CT_VOLTAGE; - } - } -} - -uint16_t AdcRead(uint8_t factor) -{ - - - - - - uint8_t samples = 1 << factor; - uint16_t analog = 0; - for (uint32_t i = 0; i < samples; i++) { - analog += analogRead(A0); - delay(1); - } - analog >>= factor; - return analog; -} - -#ifdef USE_RULES -void AdcEvery250ms(void) -{ - if (ADC0_INPUT == my_adc0) { - uint16_t new_value = AdcRead(5); - if ((new_value < Adc.last_value -10) || (new_value > Adc.last_value +10)) { - Adc.last_value = new_value; - uint16_t value = Adc.last_value / 10; - Response_P(PSTR("{\"ANALOG\":{\"A0div10\":%d}}"), (value > 99) ? 100 : value); - XdrvRulesProcess(); - } - } -} -#endif - -uint16_t AdcGetLux(void) -{ - int adc = AdcRead(2); - - double resistorVoltage = ((double)adc / 1023) * ANALOG_V33; - double ldrVoltage = ANALOG_V33 - resistorVoltage; - double ldrResistance = ldrVoltage / resistorVoltage * (double)Settings.adc_param1; - double ldrLux = (double)Settings.adc_param2 * FastPrecisePow(ldrResistance, (double)Settings.adc_param3 / 10000); - - return (uint16_t)ldrLux; -} - -uint16_t AdcGetRange(void) -{ - - - - int adc = AdcRead(2); - double adcrange = ( ((double)Settings.adc_param2 - (double)adc) / ( ((double)Settings.adc_param2 - (double)Settings.adc_param1)) * ((double)Settings.adc_param3 - (double)Settings.adc_param4) + (double)Settings.adc_param4 ); - return (uint16_t)adcrange; -} - -void AdcGetCurrentPower(uint8_t factor) -{ - - - - - - uint8_t samples = 1 << factor; - uint16_t analog = 0; - uint16_t analog_min = 1023; - uint16_t analog_max = 0; - for (uint32_t i = 0; i < samples; i++) { - analog = analogRead(A0); - if (analog < analog_min) { - analog_min = analog; - } - if (analog > analog_max) { - analog_max = analog; - } - delay(1); - } - - Adc.current = (float)(analog_max-analog_min) * ((float)(Settings.adc_param2) / 100000); - float power = Adc.current * (float)(Settings.adc_param3) / 10; - uint32_t current_millis = millis(); - Adc.energy = Adc.energy + ((power * (current_millis - Adc.previous_millis)) / 3600000000); - Adc.previous_millis = current_millis; -} - -void AdcEverySecond(void) -{ - if (ADC0_TEMP == my_adc0) { - int adc = AdcRead(2); - - double Rt = (adc * Settings.adc_param1) / (1024.0 * ANALOG_V33 - (double)adc); - double BC = (double)Settings.adc_param3 / 10000; - double T = BC / (BC / ANALOG_T0 + TaylorLog(Rt / (double)Settings.adc_param2)); - Adc.temperature = ConvertTemp(TO_CELSIUS(T)); - } - else if (ADC0_CT_POWER == my_adc0) { - AdcGetCurrentPower(5); - } -} - -void AdcShow(bool json) -{ - if (ADC0_INPUT == my_adc0) { - uint16_t analog = AdcRead(5); - - if (json) { - ResponseAppend_P(PSTR(",\"ANALOG\":{\"A0\":%d}"), analog); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_ANALOG, "", 0, analog); -#endif - } - } - - else if (ADC0_TEMP == my_adc0) { - char temperature[33]; - dtostrfd(Adc.temperature, Settings.flag2.temperature_resolution, temperature); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMP, "ANALOG", temperature); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_TEMP, temperature); - } -#endif -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, Adc.temperature); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, "", temperature, TempUnit()); -#endif - } - } - - else if (ADC0_LIGHT == my_adc0) { - uint16_t adc_light = AdcGetLux(); - - if (json) { - ResponseAppend_P(JSON_SNS_ILLUMINANCE, "ANALOG", adc_light); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_ILLUMINANCE, adc_light); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, "", adc_light); -#endif - } - } - - else if (ADC0_RANGE == my_adc0) { - uint16_t adc_range = AdcGetRange(); - - if (json) { - ResponseAppend_P(JSON_SNS_RANGE, "ANALOG", adc_range); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_RANGE, "", adc_range); -#endif - } - } - - else if (ADC0_CT_POWER == my_adc0) { - AdcGetCurrentPower(5); - - float voltage = (float)(Settings.adc_param3) / 10; - char voltage_chr[FLOATSZ]; - dtostrfd(voltage, Settings.flag2.voltage_resolution, voltage_chr); - char current_chr[FLOATSZ]; - dtostrfd(Adc.current, Settings.flag2.current_resolution, current_chr); - char power_chr[FLOATSZ]; - dtostrfd(voltage * Adc.current, Settings.flag2.wattage_resolution, power_chr); - char energy_chr[FLOATSZ]; - dtostrfd(Adc.energy, Settings.flag2.energy_resolution, energy_chr); - - if (json) { - ResponseAppend_P(PSTR(",\"ANALOG\":{\"" D_JSON_ENERGY "\":%s,\"" D_JSON_POWERUSAGE "\":%s,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s}"), - energy_chr, power_chr, voltage_chr, current_chr); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_POWER_ENERGY, power_chr); - DomoticzSensor(DZ_VOLTAGE, voltage_chr); - DomoticzSensor(DZ_CURRENT, current_chr); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_VOLTAGE, voltage_chr); - WSContentSend_PD(HTTP_SNS_CURRENT, current_chr); - WSContentSend_PD(HTTP_SNS_POWER, power_chr); - WSContentSend_PD(HTTP_SNS_ENERGY_TOTAL, energy_chr); -#endif - } - } - -} - - - - - -const char kAdcCommands[] PROGMEM = "|" - D_CMND_ADC "|" D_CMND_ADCS "|" D_CMND_ADCPARAM; - -void (* const AdcCommand[])(void) PROGMEM = { - &CmndAdc, &CmndAdcs, &CmndAdcParam }; - -void CmndAdc(void) -{ - if (ValidAdc() && (XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < ADC0_END)) { - Settings.my_adc0 = XdrvMailbox.payload; - restart_flag = 2; - } - char stemp1[TOPSZ]; - Response_P(PSTR("{\"" D_CMND_ADC "0\":{\"%d\":\"%s\"}}"), Settings.my_adc0, GetTextIndexed(stemp1, sizeof(stemp1), Settings.my_adc0, kAdc0Names)); -} - -void CmndAdcs(void) -{ - Response_P(PSTR("{\"" D_CMND_ADCS "\":{")); - bool jsflg = false; - char stemp1[TOPSZ]; - for (uint32_t i = 0; i < ADC0_END; i++) { - if (jsflg) { - ResponseAppend_P(PSTR(",")); - } - jsflg = true; - ResponseAppend_P(PSTR("\"%d\":\"%s\""), i, GetTextIndexed(stemp1, sizeof(stemp1), i, kAdc0Names)); - } - ResponseJsonEndEnd(); -} - -void CmndAdcParam(void) -{ - if (XdrvMailbox.data_len) { - if ((ADC0_TEMP == XdrvMailbox.payload) || - (ADC0_LIGHT == XdrvMailbox.payload) || - (ADC0_RANGE == XdrvMailbox.payload) || - (ADC0_CT_POWER == XdrvMailbox.payload)) { - if (strstr(XdrvMailbox.data, ",") != nullptr) { - char sub_string[XdrvMailbox.data_len +1]; - - - - Settings.adc_param_type = XdrvMailbox.payload; - Settings.adc_param1 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - Settings.adc_param2 = strtol(subStr(sub_string, XdrvMailbox.data, ",", 3), nullptr, 10); - if (ADC0_RANGE == XdrvMailbox.payload) { - Settings.adc_param3 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 4), nullptr, 10)); - Settings.adc_param4 = abs(strtol(subStr(sub_string, XdrvMailbox.data, ",", 5), nullptr, 10)); - } else { - Settings.adc_param3 = (int)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 4)) * 10000); - } - if (ADC0_CT_POWER == XdrvMailbox.payload) { - if ((Settings.adc_param1 & CT_FLAG_ENERGY_RESET) > 0) { - Adc.energy = 0; - Settings.adc_param1 ^= CT_FLAG_ENERGY_RESET; - } - } - } else { - - - - - Settings.adc_param_type = 0; - AdcInit(); - } - } - } - - - Response_P(PSTR("{\"" D_CMND_ADCPARAM "\":[%d,%d,%d"), Settings.adc_param_type, Settings.adc_param1, Settings.adc_param2); - if (ADC0_RANGE == my_adc0) { - ResponseAppend_P(PSTR(",%d,%d"), Settings.adc_param3, Settings.adc_param4); - } else { - int value = Settings.adc_param3; - uint8_t precision; - for (precision = 4; precision > 0; precision--) { - if (value % 10) { break; } - value /= 10; - } - char param3[33]; - dtostrfd(((double)Settings.adc_param3)/10000, precision, param3); - ResponseAppend_P(PSTR(",%s"), param3); - } - ResponseAppend_P(PSTR("]}")); -} - - - - - -bool Xsns02(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_COMMAND: - result = DecodeCommand(kAdcCommands, AdcCommand); - break; - default: - if ((ADC0_INPUT == my_adc0) || - (ADC0_TEMP == my_adc0) || - (ADC0_LIGHT == my_adc0) || - (ADC0_RANGE == my_adc0) || - (ADC0_CT_POWER == my_adc0)) { - switch (function) { -#ifdef USE_RULES - case FUNC_EVERY_250_MSECOND: - AdcEvery250ms(); - break; -#endif - case FUNC_EVERY_SECOND: - AdcEverySecond(); - break; - case FUNC_INIT: - AdcInit(); - break; - case FUNC_JSON_APPEND: - AdcShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - AdcShow(0); - break; -#endif - } - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_04_snfsc.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_04_snfsc.ino" -#ifdef USE_SONOFF_SC -# 57 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_04_snfsc.ino" -#define XSNS_04 4 - -uint16_t sc_value[5] = { 0 }; - -void SonoffScSend(const char *data) -{ - Serial.write(data); - Serial.write('\x1B'); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_TRANSMIT " %s"), data); -} - -void SonoffScInit(void) -{ - - SonoffScSend("AT+START"); - -} - -void SonoffScSerialInput(char *rcvstat) -{ - char *p; - char *str; - uint16_t value[5] = { 0 }; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_SERIAL D_RECEIVED " %s"), rcvstat); - - if (!strncasecmp_P(rcvstat, PSTR("AT+UPDATE="), 10)) { - int8_t i = -1; - for (str = strtok_r(rcvstat, ":", &p); str && i < 5; str = strtok_r(nullptr, ":", &p)) { - value[i++] = atoi(str); - } - if (value[0] > 0) { - for (uint32_t i = 0; i < 5; i++) { - sc_value[i] = value[i]; - } - sc_value[2] = (11 - sc_value[2]) * 10; - sc_value[3] *= 10; - sc_value[4] = (11 - sc_value[4]) * 10; - SonoffScSend("AT+SEND=ok"); - } else { - SonoffScSend("AT+SEND=fail"); - } - } - else if (!strcasecmp_P(rcvstat, PSTR("AT+STATUS?"))) { - SonoffScSend("AT+STATUS=4"); - } -} - - - -#ifdef USE_WEBSERVER -const char HTTP_SNS_SCPLUS[] PROGMEM = - "{s}" D_LIGHT "{m}%d%%{e}{s}" D_NOISE "{m}%d%%{e}{s}" D_AIR_QUALITY "{m}%d%%{e}"; -#endif - -void SonoffScShow(bool json) -{ - if (sc_value[0] > 0) { - float t = ConvertTemp(sc_value[1]); - float h = ConvertHumidity(sc_value[0]); - - char temperature[33]; - dtostrfd(t, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(h, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(PSTR(",\"SonoffSC\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s,\"" D_JSON_LIGHT "\":%d,\"" D_JSON_NOISE "\":%d,\"" D_JSON_AIRQUALITY "\":%d}"), - temperature, humidity, sc_value[2], sc_value[3], sc_value[4]); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzTempHumSensor(temperature, humidity); - DomoticzSensor(DZ_ILLUMINANCE, sc_value[2]); - DomoticzSensor(DZ_COUNT, sc_value[3]); - DomoticzSensor(DZ_AIRQUALITY, 500 + ((100 - sc_value[4]) * 20)); - } -#endif - -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, t); - KnxSensor(KNX_HUMIDITY, h); - } -#endif - -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, "", temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, "", humidity); - WSContentSend_PD(HTTP_SNS_SCPLUS, sc_value[2], sc_value[3], sc_value[4]); -#endif - } - } -} - - - - - -bool Xsns04(uint8_t function) -{ - bool result = false; - - if (SONOFF_SC == my_module_type) { - switch (function) { - case FUNC_JSON_APPEND: - SonoffScShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - SonoffScShow(0); - break; -#endif - case FUNC_INIT: - SonoffScInit(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_05_ds18x20.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_05_ds18x20.ino" -#ifdef USE_DS18x20 - - - - -#define XSNS_05 5 - - - -#define DS18S20_CHIPID 0x10 -#define DS1822_CHIPID 0x22 -#define DS18B20_CHIPID 0x28 -#define MAX31850_CHIPID 0x3B - -#define W1_SKIP_ROM 0xCC -#define W1_CONVERT_TEMP 0x44 -#define W1_WRITE_EEPROM 0x48 -#define W1_WRITE_SCRATCHPAD 0x4E -#define W1_READ_SCRATCHPAD 0xBE - -#define DS18X20_MAX_SENSORS 8 - -const char kDs18x20Types[] PROGMEM = "DS18x20|DS18S20|DS1822|DS18B20|MAX31850"; - -uint8_t ds18x20_chipids[] = { 0, DS18S20_CHIPID, DS1822_CHIPID, DS18B20_CHIPID, MAX31850_CHIPID }; - -struct DS18X20STRUCT { - uint8_t address[8]; - uint8_t index; - uint8_t valid; - float temperature; -} ds18x20_sensor[DS18X20_MAX_SENSORS]; -uint8_t ds18x20_sensors = 0; -uint8_t ds18x20_pin = 0; -uint8_t ds18x20_pin_out = 0; -bool ds18x20_dual_mode = false; -char ds18x20_types[12]; -#ifdef W1_PARASITE_POWER -uint8_t ds18x20_sensor_curr = 0; -unsigned long w1_power_until = 0; -#endif - - - - - -#define W1_MATCH_ROM 0x55 -#define W1_SEARCH_ROM 0xF0 - -uint8_t onewire_last_discrepancy = 0; -uint8_t onewire_last_family_discrepancy = 0; -bool onewire_last_device_flag = false; -unsigned char onewire_rom_id[8] = { 0 }; - - - -uint8_t OneWireReset(void) -{ - uint8_t retries = 125; - - if (!ds18x20_dual_mode) { - pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); - do { - if (--retries == 0) { - return 0; - } - delayMicroseconds(2); - } while (!digitalRead(ds18x20_pin)); - pinMode(ds18x20_pin, OUTPUT); - digitalWrite(ds18x20_pin, LOW); - delayMicroseconds(480); - pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); - } else { - digitalWrite(ds18x20_pin_out, HIGH); - do { - if (--retries == 0) { - return 0; - } - delayMicroseconds(2); - } while (!digitalRead(ds18x20_pin)); - digitalWrite(ds18x20_pin_out, LOW); - delayMicroseconds(480); - digitalWrite(ds18x20_pin_out, HIGH); - } - delayMicroseconds(70); - uint8_t r = !digitalRead(ds18x20_pin); - delayMicroseconds(410); - return r; -} - -void OneWireWriteBit(uint8_t v) -{ - static const uint8_t delay_low[2] = { 65, 10 }; - static const uint8_t delay_high[2] = { 5, 55 }; - - v &= 1; - if (!ds18x20_dual_mode) { - digitalWrite(ds18x20_pin, LOW); - pinMode(ds18x20_pin, OUTPUT); - delayMicroseconds(delay_low[v]); - digitalWrite(ds18x20_pin, HIGH); - } else { - digitalWrite(ds18x20_pin_out, LOW); - delayMicroseconds(delay_low[v]); - digitalWrite(ds18x20_pin_out, HIGH); - } - delayMicroseconds(delay_high[v]); -} - -uint8_t OneWireReadBit(void) -{ - if (!ds18x20_dual_mode) { - pinMode(ds18x20_pin, OUTPUT); - digitalWrite(ds18x20_pin, LOW); - delayMicroseconds(3); - pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); - } else { - digitalWrite(ds18x20_pin_out, LOW); - delayMicroseconds(3); - digitalWrite(ds18x20_pin_out, HIGH); - } - delayMicroseconds(10); - uint8_t r = digitalRead(ds18x20_pin); - delayMicroseconds(53); - return r; -} - - - -void OneWireWrite(uint8_t v) -{ - for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { - OneWireWriteBit((bit_mask & v) ? 1 : 0); - } -} - -uint8_t OneWireRead(void) -{ - uint8_t r = 0; - - for (uint8_t bit_mask = 0x01; bit_mask; bit_mask <<= 1) { - if (OneWireReadBit()) { - r |= bit_mask; - } - } - return r; -} - -void OneWireSelect(const uint8_t rom[8]) -{ - OneWireWrite(W1_MATCH_ROM); - for (uint32_t i = 0; i < 8; i++) { - OneWireWrite(rom[i]); - } -} - -void OneWireResetSearch(void) -{ - onewire_last_discrepancy = 0; - onewire_last_device_flag = false; - onewire_last_family_discrepancy = 0; - for (uint32_t i = 0; i < 8; i++) { - onewire_rom_id[i] = 0; - } -} - -uint8_t OneWireSearch(uint8_t *newAddr) -{ - uint8_t id_bit_number = 1; - uint8_t last_zero = 0; - uint8_t rom_byte_number = 0; - uint8_t search_result = 0; - uint8_t id_bit; - uint8_t cmp_id_bit; - unsigned char rom_byte_mask = 1; - unsigned char search_direction; - - if (!onewire_last_device_flag) { - if (!OneWireReset()) { - onewire_last_discrepancy = 0; - onewire_last_device_flag = false; - onewire_last_family_discrepancy = 0; - return false; - } - OneWireWrite(W1_SEARCH_ROM); - do { - id_bit = OneWireReadBit(); - cmp_id_bit = OneWireReadBit(); - - if ((id_bit == 1) && (cmp_id_bit == 1)) { - break; - } else { - if (id_bit != cmp_id_bit) { - search_direction = id_bit; - } else { - if (id_bit_number < onewire_last_discrepancy) { - search_direction = ((onewire_rom_id[rom_byte_number] & rom_byte_mask) > 0); - } else { - search_direction = (id_bit_number == onewire_last_discrepancy); - } - if (search_direction == 0) { - last_zero = id_bit_number; - if (last_zero < 9) { - onewire_last_family_discrepancy = last_zero; - } - } - } - if (search_direction == 1) { - onewire_rom_id[rom_byte_number] |= rom_byte_mask; - } else { - onewire_rom_id[rom_byte_number] &= ~rom_byte_mask; - } - OneWireWriteBit(search_direction); - id_bit_number++; - rom_byte_mask <<= 1; - if (rom_byte_mask == 0) { - rom_byte_number++; - rom_byte_mask = 1; - } - } - } while (rom_byte_number < 8); - if (!(id_bit_number < 65)) { - onewire_last_discrepancy = last_zero; - if (onewire_last_discrepancy == 0) { - onewire_last_device_flag = true; - } - search_result = true; - } - } - if (!search_result || !onewire_rom_id[0]) { - onewire_last_discrepancy = 0; - onewire_last_device_flag = false; - onewire_last_family_discrepancy = 0; - search_result = false; - } - for (uint32_t i = 0; i < 8; i++) { - newAddr[i] = onewire_rom_id[i]; - } - return search_result; -} - -bool OneWireCrc8(uint8_t *addr) -{ - uint8_t crc = 0; - uint8_t len = 8; - - while (len--) { - uint8_t inbyte = *addr++; - for (uint32_t i = 8; i; i--) { - uint8_t mix = (crc ^ inbyte) & 0x01; - crc >>= 1; - if (mix) { - crc ^= 0x8C; - } - inbyte >>= 1; - } - } - return (crc == *addr); -} - - - -void Ds18x20Init(void) -{ - uint64_t ids[DS18X20_MAX_SENSORS]; - - ds18x20_pin = pin[GPIO_DSB]; - if (pin[GPIO_DSB_OUT] < 99) { - ds18x20_pin_out = pin[GPIO_DSB_OUT]; - ds18x20_dual_mode = true; - pinMode(ds18x20_pin_out, OUTPUT); - pinMode(ds18x20_pin, Settings.flag3.ds18x20_internal_pullup ? INPUT_PULLUP : INPUT); - } - - OneWireResetSearch(); - - ds18x20_sensors = 0; - while (ds18x20_sensors < DS18X20_MAX_SENSORS) { - if (!OneWireSearch(ds18x20_sensor[ds18x20_sensors].address)) { - break; - } - if (OneWireCrc8(ds18x20_sensor[ds18x20_sensors].address) && - ((ds18x20_sensor[ds18x20_sensors].address[0] == DS18S20_CHIPID) || - (ds18x20_sensor[ds18x20_sensors].address[0] == DS1822_CHIPID) || - (ds18x20_sensor[ds18x20_sensors].address[0] == DS18B20_CHIPID) || - (ds18x20_sensor[ds18x20_sensors].address[0] == MAX31850_CHIPID))) { - ds18x20_sensor[ds18x20_sensors].index = ds18x20_sensors; - ids[ds18x20_sensors] = ds18x20_sensor[ds18x20_sensors].address[0]; - for (uint32_t j = 6; j > 0; j--) { - ids[ds18x20_sensors] = ids[ds18x20_sensors] << 8 | ds18x20_sensor[ds18x20_sensors].address[j]; - } - ds18x20_sensors++; - } - } - for (uint32_t i = 0; i < ds18x20_sensors; i++) { - for (uint32_t j = i + 1; j < ds18x20_sensors; j++) { - if (ids[ds18x20_sensor[i].index] > ids[ds18x20_sensor[j].index]) { - std::swap(ds18x20_sensor[i].index, ds18x20_sensor[j].index); - } - } - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSORS_FOUND " %d"), ds18x20_sensors); -} - -void Ds18x20Convert(void) -{ - OneWireReset(); -#ifdef W1_PARASITE_POWER - - if (++ds18x20_sensor_curr >= ds18x20_sensors) - ds18x20_sensor_curr = 0; - OneWireSelect(ds18x20_sensor[ds18x20_sensor_curr].address); -#else - OneWireWrite(W1_SKIP_ROM); -#endif - OneWireWrite(W1_CONVERT_TEMP); - -} - -bool Ds18x20Read(uint8_t sensor) -{ - uint8_t data[9]; - int8_t sign = 1; - - uint8_t index = ds18x20_sensor[sensor].index; - if (ds18x20_sensor[index].valid) { ds18x20_sensor[index].valid--; } - for (uint32_t retry = 0; retry < 3; retry++) { - OneWireReset(); - OneWireSelect(ds18x20_sensor[index].address); - OneWireWrite(W1_READ_SCRATCHPAD); - for (uint32_t i = 0; i < 9; i++) { - data[i] = OneWireRead(); - } - if (OneWireCrc8(data)) { - switch(ds18x20_sensor[index].address[0]) { - case DS18S20_CHIPID: { - if (data[1] > 0x80) { - data[0] = (~data[0]) +1; - sign = -1; - } - float temp9 = (float)(data[0] >> 1) * sign; - ds18x20_sensor[index].temperature = ConvertTemp((temp9 - 0.25) + ((16.0 - data[6]) / 16.0)); - ds18x20_sensor[index].valid = SENSOR_MAX_MISS; - return true; - } - case DS1822_CHIPID: - case DS18B20_CHIPID: { - if (data[4] != 0x7F) { - data[4] = 0x7F; - OneWireReset(); - OneWireSelect(ds18x20_sensor[index].address); - OneWireWrite(W1_WRITE_SCRATCHPAD); - OneWireWrite(data[2]); - OneWireWrite(data[3]); - OneWireWrite(data[4]); - OneWireSelect(ds18x20_sensor[index].address); - OneWireWrite(W1_WRITE_EEPROM); -#ifdef W1_PARASITE_POWER - w1_power_until = millis() + 10; -#endif - } - uint16_t temp12 = (data[1] << 8) + data[0]; - if (temp12 > 2047) { - temp12 = (~temp12) +1; - sign = -1; - } - ds18x20_sensor[index].temperature = ConvertTemp(sign * temp12 * 0.0625); - ds18x20_sensor[index].valid = SENSOR_MAX_MISS; - return true; - } - case MAX31850_CHIPID: { - int16_t temp14 = (data[1] << 8) + (data[0] & 0xFC); - ds18x20_sensor[index].temperature = ConvertTemp(temp14 * 0.0625); - ds18x20_sensor[index].valid = SENSOR_MAX_MISS; - return true; - } - } - } - } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DSB D_SENSOR_CRC_ERROR)); - return false; -} - -void Ds18x20Name(uint8_t sensor) -{ - uint8_t index = sizeof(ds18x20_chipids); - while (index) { - if (ds18x20_sensor[ds18x20_sensor[sensor].index].address[0] == ds18x20_chipids[index]) { - break; - } - index--; - } - GetTextIndexed(ds18x20_types, sizeof(ds18x20_types), index, kDs18x20Types); - if (ds18x20_sensors > 1) { - snprintf_P(ds18x20_types, sizeof(ds18x20_types), PSTR("%s%c%d"), ds18x20_types, IndexSeparator(), sensor +1); - } -} - - - -void Ds18x20EverySecond(void) -{ -#ifdef W1_PARASITE_POWER - - unsigned long now = millis(); - if (now < w1_power_until) - return; -#endif - if (uptime & 1 -#ifdef W1_PARASITE_POWER - - || ds18x20_sensors >= 2 -#endif - ) { - - Ds18x20Convert(); - } else { - for (uint32_t i = 0; i < ds18x20_sensors; i++) { - - if (!Ds18x20Read(i)) { - Ds18x20Name(i); - AddLogMissed(ds18x20_types, ds18x20_sensor[ds18x20_sensor[i].index].valid); -#ifdef USE_DS18x20_RECONFIGURE - if (!ds18x20_sensor[ds18x20_sensor[i].index].valid) { - memset(&ds18x20_sensor, 0, sizeof(ds18x20_sensor)); - Ds18x20Init(); - } -#endif - } - } - } -} - -void Ds18x20Show(bool json) -{ - for (uint32_t i = 0; i < ds18x20_sensors; i++) { - uint8_t index = ds18x20_sensor[i].index; - - if (ds18x20_sensor[index].valid) { - char temperature[33]; - dtostrfd(ds18x20_sensor[index].temperature, Settings.flag2.temperature_resolution, temperature); - - Ds18x20Name(i); - - if (json) { - char address[17]; - for (uint32_t j = 0; j < 6; j++) { - sprintf(address+2*j, "%02X", ds18x20_sensor[index].address[6-j]); - } - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ID "\":\"%s\",\"" D_JSON_TEMPERATURE "\":%s}"), ds18x20_types, address, temperature); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (0 == i)) { - DomoticzSensor(DZ_TEMP, temperature); - } -#endif -#ifdef USE_KNX - if ((0 == tele_period) && (0 == i)) { - KnxSensor(KNX_TEMPERATURE, ds18x20_sensor[index].temperature); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, ds18x20_types, temperature, TempUnit()); -#endif - } - } - } -} - - - - - -bool Xsns05(uint8_t function) -{ - bool result = false; - - if (pin[GPIO_DSB] < 99) { - switch (function) { - case FUNC_INIT: - Ds18x20Init(); - break; - case FUNC_EVERY_SECOND: - Ds18x20EverySecond(); - break; - case FUNC_JSON_APPEND: - Ds18x20Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Ds18x20Show(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_old.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_old.ino" -#ifdef USE_DHT_OLD -# 29 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_old.ino" -#define XSNS_06 6 - -#define DHT_MAX_SENSORS 4 -#define DHT_MAX_RETRY 8 - -uint32_t dht_max_cycles; -uint8_t dht_data[5]; -uint8_t dht_sensors = 0; -uint8_t dht_pin_out = 0; -bool dht_active = true; -bool dht_dual_mode = false; - -struct DHTSTRUCT { - uint8_t pin; - uint8_t type; - char stype[12]; - uint32_t lastreadtime; - uint8_t lastresult; - float t = NAN; - float h = NAN; -} Dht[DHT_MAX_SENSORS]; - -void DhtReadPrep(void) -{ - for (uint32_t i = 0; i < dht_sensors; i++) { - if (!dht_dual_mode) { - digitalWrite(Dht[i].pin, HIGH); - } else { - digitalWrite(dht_pin_out, HIGH); - } - } -} - -int32_t DhtExpectPulse(uint8_t sensor, bool level) -{ - int32_t count = 0; - - while (digitalRead(Dht[sensor].pin) == level) { - if (count++ >= (int32_t)dht_max_cycles) { - return -1; - } - } - return count; -} - -bool DhtRead(uint8_t sensor) -{ - int32_t cycles[80]; - uint8_t error = 0; - - dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; - - - - - if (Dht[sensor].lastresult > DHT_MAX_RETRY) { - Dht[sensor].lastresult = 0; - if (!dht_dual_mode) { - digitalWrite(Dht[sensor].pin, HIGH); - } else { - digitalWrite(dht_pin_out, HIGH); - } - delay(250); - } - if (!dht_dual_mode) { - pinMode(Dht[sensor].pin, OUTPUT); - digitalWrite(Dht[sensor].pin, LOW); - } else { - digitalWrite(dht_pin_out, LOW); - } - - if (GPIO_SI7021 == Dht[sensor].type) { - delayMicroseconds(500); - } else { - delay(20); - } - - noInterrupts(); - if (!dht_dual_mode) { - digitalWrite(Dht[sensor].pin, HIGH); - delayMicroseconds(40); - pinMode(Dht[sensor].pin, INPUT_PULLUP); - } else { - digitalWrite(dht_pin_out, HIGH); - delayMicroseconds(40); - } - delayMicroseconds(10); - if (-1 == DhtExpectPulse(sensor, LOW)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_LOW " " D_PULSE)); - error = 1; - } - else if (-1 == DhtExpectPulse(sensor, HIGH)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_HIGH " " D_PULSE)); - error = 1; - } - else { - for (uint32_t i = 0; i < 80; i += 2) { - cycles[i] = DhtExpectPulse(sensor, LOW); - cycles[i+1] = DhtExpectPulse(sensor, HIGH); - } - } - interrupts(); - - if (error) { return false; } - - for (uint32_t i = 0; i < 40; ++i) { - int32_t lowCycles = cycles[2*i]; - int32_t highCycles = cycles[2*i+1]; - if ((-1 == lowCycles) || (-1 == highCycles)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_PULSE)); - return false; - } - dht_data[i/8] <<= 1; - if (highCycles > lowCycles) { - dht_data[i / 8] |= 1; - } - } - - uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; - if (dht_data[4] != checksum) { - char hex_char[15]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), - ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); - return false; - } - - return true; -} - -void DhtReadTempHum(uint8_t sensor) -{ - if ((NAN == Dht[sensor].h) || (Dht[sensor].lastresult > DHT_MAX_RETRY)) { - Dht[sensor].t = NAN; - Dht[sensor].h = NAN; - } - if (DhtRead(sensor)) { - switch (Dht[sensor].type) { - case GPIO_DHT11: - Dht[sensor].h = dht_data[0]; - Dht[sensor].t = dht_data[2] + ((float)dht_data[3] * 0.1f); - break; - case GPIO_DHT22: - case GPIO_SI7021: - Dht[sensor].h = ((dht_data[0] << 8) | dht_data[1]) * 0.1; - Dht[sensor].t = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; - if (dht_data[2] & 0x80) { - Dht[sensor].t *= -1; - } - break; - } - Dht[sensor].t = ConvertTemp(Dht[sensor].t); - Dht[sensor].h = ConvertHumidity(Dht[sensor].h); - Dht[sensor].lastresult = 0; - } else { - Dht[sensor].lastresult++; - } -} - - - -bool DhtPinState() -{ - if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { - if (dht_sensors < DHT_MAX_SENSORS) { - Dht[dht_sensors].pin = XdrvMailbox.payload; - Dht[dht_sensors].type = XdrvMailbox.index; - dht_sensors++; - XdrvMailbox.index = GPIO_DHT11; - } else { - XdrvMailbox.index = 0; - } - return true; - } - return false; -} - -void DhtInit(void) -{ - if (dht_sensors) { - dht_max_cycles = microsecondsToClockCycles(1000); - - if (pin[GPIO_DHT11_OUT] < 99) { - dht_pin_out = pin[GPIO_DHT11_OUT]; - dht_dual_mode = true; - dht_sensors = 1; - pinMode(dht_pin_out, OUTPUT); - } - - for (uint32_t i = 0; i < dht_sensors; i++) { - pinMode(Dht[i].pin, INPUT_PULLUP); - Dht[i].lastreadtime = 0; - Dht[i].lastresult = 0; - GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); - if (dht_sensors > 1) { - snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); - } - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_SENSORS_FOUND " %d"), dht_sensors); - } else { - dht_active = false; - } -} - -void DhtEverySecond(void) -{ - if (uptime &1) { - - DhtReadPrep(); - } else { - for (uint32_t i = 0; i < dht_sensors; i++) { - - DhtReadTempHum(i); - } - } -} - -void DhtShow(bool json) -{ - for (uint32_t i = 0; i < dht_sensors; i++) { - char temperature[33]; - dtostrfd(Dht[i].t, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(Dht[i].h, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMPHUM, Dht[i].stype, temperature, humidity); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (0 == i)) { - DomoticzTempHumSensor(temperature, humidity); - } -#endif -#ifdef USE_KNX - if ((0 == tele_period) && (0 == i)) { - KnxSensor(KNX_TEMPERATURE, Dht[i].t); - KnxSensor(KNX_HUMIDITY, Dht[i].h); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, Dht[i].stype, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, Dht[i].stype, humidity); -#endif - } - } -} - - - - - -bool Xsns06(uint8_t function) -{ - bool result = false; - - if (dht_active) { - switch (function) { - case FUNC_EVERY_SECOND: - DhtEverySecond(); - break; - case FUNC_JSON_APPEND: - DhtShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - DhtShow(0); - break; -#endif - case FUNC_INIT: - DhtInit(); - break; - case FUNC_PIN_STATE: - result = DhtPinState(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v2.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v2.ino" -#ifdef USE_DHT_V2 -# 29 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v2.ino" -#define XSNS_06 6 - -#define DHT_MAX_SENSORS 4 -#define DHT_MAX_RETRY 8 - -uint32_t dht_max_cycles; -uint8_t dht_data[5]; -uint8_t dht_sensors = 0; -uint8_t dht_pin_out = 0; -bool dht_active = true; -bool dht_dual_mode = false; - -struct DHTSTRUCT { - uint8_t pin; - uint8_t type; - char stype[12]; - uint32_t lastreadtime; - uint8_t lastresult; - float t = NAN; - float h = NAN; -} Dht[DHT_MAX_SENSORS]; - -void DhtReadPrep(void) -{ - for (uint32_t i = 0; i < dht_sensors; i++) { - if (!dht_dual_mode) { - digitalWrite(Dht[i].pin, HIGH); - } else { - digitalWrite(dht_pin_out, HIGH); - } - } -} - -int32_t DhtExpectPulse(uint8_t sensor, bool level) -{ - int32_t count = 0; - - while (digitalRead(Dht[sensor].pin) == level) { - if (count++ >= (int32_t)dht_max_cycles) { - return -1; - } - } - return count; -} - -bool DhtRead(uint8_t sensor) -{ - int32_t cycles[80]; - uint8_t error = 0; - - dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; - - if (Dht[sensor].lastresult > DHT_MAX_RETRY) { - Dht[sensor].lastresult = 0; - if (!dht_dual_mode) { - digitalWrite(Dht[sensor].pin, HIGH); - } else { - digitalWrite(dht_pin_out, HIGH); - } - delay(250); - } - - - noInterrupts(); - if (!dht_dual_mode) { - pinMode(Dht[sensor].pin, OUTPUT); - digitalWrite(Dht[sensor].pin, LOW); - } else { - digitalWrite(dht_pin_out, LOW); - } - - switch (Dht[sensor].type) { - case GPIO_SI7021: -# 114 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v2.ino" - delayMicroseconds(500); - if (!dht_dual_mode) { - digitalWrite(Dht[sensor].pin, HIGH); - } else { - digitalWrite(dht_pin_out, HIGH); - } - delayMicroseconds(40); - break; - - case GPIO_DHT22: -# 133 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v2.ino" - delayMicroseconds(1100); - if (!dht_dual_mode) { - digitalWrite(Dht[sensor].pin, HIGH); - } else { - digitalWrite(dht_pin_out, HIGH); - } - delayMicroseconds(30); - break; - - case GPIO_DHT11: -# 151 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v2.ino" - default: - - delay(20); - if (!dht_dual_mode) { - digitalWrite(Dht[sensor].pin, HIGH); - } else { - digitalWrite(dht_pin_out, HIGH); - } - delayMicroseconds(30); - break; - } - - - pinMode(Dht[sensor].pin, INPUT_PULLUP); - - if (-1 == DhtExpectPulse(sensor, LOW)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_LOW " " D_PULSE)); - error = 1; - } - else if (-1 == DhtExpectPulse(sensor, HIGH)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_HIGH " " D_PULSE)); - error = 1; - } - else { - for (uint32_t i = 0; i < 80; i += 2) { - cycles[i] = DhtExpectPulse(sensor, LOW); - cycles[i+1] = DhtExpectPulse(sensor, HIGH); - } - } - interrupts(); - if (error) { return false; } - - - for (uint32_t i = 0; i < 40; ++i) { - int32_t lowCycles = cycles[2*i]; - int32_t highCycles = cycles[2*i+1]; - if ((-1 == lowCycles) || (-1 == highCycles)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_PULSE)); - return false; - } - dht_data[i/8] <<= 1; - if (highCycles > lowCycles) { - dht_data[i / 8] |= 1; - } - } - - - uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; - if (dht_data[4] != checksum) { - char hex_char[15]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), - ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); - return false; - } - - return true; -} - -void DhtReadTempHum(uint8_t sensor) -{ - if ((NAN == Dht[sensor].h) || (Dht[sensor].lastresult > DHT_MAX_RETRY)) { - Dht[sensor].t = NAN; - Dht[sensor].h = NAN; - } - if (DhtRead(sensor)) { - switch (Dht[sensor].type) { - case GPIO_DHT11: - Dht[sensor].h = dht_data[0]; - Dht[sensor].t = dht_data[2] + ((float)dht_data[3] * 0.1f); - break; - case GPIO_DHT22: - case GPIO_SI7021: - Dht[sensor].h = ((dht_data[0] << 8) | dht_data[1]) * 0.1; - Dht[sensor].t = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; - if (dht_data[2] & 0x80) { - Dht[sensor].t *= -1; - } - break; - } - Dht[sensor].t = ConvertTemp(Dht[sensor].t); - Dht[sensor].h = ConvertHumidity(Dht[sensor].h); - Dht[sensor].lastresult = 0; - } else { - Dht[sensor].lastresult++; - } -} - - - -bool DhtPinState() -{ - if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { - if (dht_sensors < DHT_MAX_SENSORS) { - Dht[dht_sensors].pin = XdrvMailbox.payload; - Dht[dht_sensors].type = XdrvMailbox.index; - dht_sensors++; - XdrvMailbox.index = GPIO_DHT11; - } else { - XdrvMailbox.index = 0; - } - return true; - } - return false; -} - -void DhtInit(void) -{ - if (dht_sensors) { - dht_max_cycles = microsecondsToClockCycles(1000); - - if (pin[GPIO_DHT11_OUT] < 99) { - dht_pin_out = pin[GPIO_DHT11_OUT]; - dht_dual_mode = true; - dht_sensors = 1; - pinMode(dht_pin_out, OUTPUT); - } - - for (uint32_t i = 0; i < dht_sensors; i++) { - pinMode(Dht[i].pin, INPUT_PULLUP); - Dht[i].lastreadtime = 0; - Dht[i].lastresult = 0; - GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); - if (dht_sensors > 1) { - snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); - } - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v2) " D_SENSORS_FOUND " %d"), dht_sensors); - } else { - dht_active = false; - } -} - -void DhtEverySecond(void) -{ - if (uptime &1) { - - DhtReadPrep(); - } else { - for (uint32_t i = 0; i < dht_sensors; i++) { - - DhtReadTempHum(i); - } - } -} - -void DhtShow(bool json) -{ - for (uint32_t i = 0; i < dht_sensors; i++) { - char temperature[33]; - dtostrfd(Dht[i].t, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(Dht[i].h, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMPHUM, Dht[i].stype, temperature, humidity); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (0 == i)) { - DomoticzTempHumSensor(temperature, humidity); - } -#endif -#ifdef USE_KNX - if ((0 == tele_period) && (0 == i)) { - KnxSensor(KNX_TEMPERATURE, Dht[i].t); - KnxSensor(KNX_HUMIDITY, Dht[i].h); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, Dht[i].stype, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, Dht[i].stype, humidity); -#endif - } - } -} - - - - - -bool Xsns06(uint8_t function) -{ - bool result = false; - - if (dht_active) { - switch (function) { - case FUNC_EVERY_SECOND: - DhtEverySecond(); - break; - case FUNC_JSON_APPEND: - DhtShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - DhtShow(0); - break; -#endif - case FUNC_INIT: - DhtInit(); - break; - case FUNC_PIN_STATE: - result = DhtPinState(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v3.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v3.ino" -#ifdef USE_DHT_V3 -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v3.ino" -#define XSNS_06 6 - -#define DHT_MAX_SENSORS 4 -#define DHT_MAX_RETRY 8 - -uint8_t dht_data[5]; -uint8_t dht_sensors = 0; -uint8_t dht_pin_out = 0; -bool dht_active = true; -bool dht_dual_mode = false; - -struct DHTSTRUCT { - uint8_t pin; - uint8_t type; - char stype[12]; - uint32_t lastreadtime; - uint8_t lastresult; - float t = NAN; - float h = NAN; -} Dht[DHT_MAX_SENSORS]; - -bool DhtExpectPulse(uint8_t sensor, int level) -{ - unsigned long timeout = micros() + 100; - while (digitalRead(Dht[sensor].pin) != level) { - if (micros() > timeout) { return false; } - delayMicroseconds(1); - } - return true; -} - -int DhtReadDat(uint8_t sensor) -{ - uint8_t result = 0; - for (uint32_t i = 0; i < 8; i++) { - if (!DhtExpectPulse(sensor, HIGH)) { return -1; } - - delayMicroseconds(35); - if (digitalRead(Dht[sensor].pin)) { - result |= (1 << (7 - i)); - } - - if (!DhtExpectPulse(sensor, LOW)) { return -1; } - } - return result; -} - -bool DhtRead(uint8_t sensor) -{ - dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; - - if (!dht_dual_mode) { - pinMode(Dht[sensor].pin, OUTPUT); - digitalWrite(Dht[sensor].pin, LOW); - } else { - digitalWrite(dht_pin_out, LOW); - } - - switch (Dht[sensor].type) { - case GPIO_DHT11: - delay(19); - break; - case GPIO_DHT22: - delay(2); - break; - case GPIO_SI7021: - delayMicroseconds(500); - break; - } - - if (!dht_dual_mode) { - pinMode(Dht[sensor].pin, INPUT_PULLUP); - } else { - digitalWrite(dht_pin_out, HIGH); - } - - switch (Dht[sensor].type) { - case GPIO_DHT11: - case GPIO_DHT22: - delayMicroseconds(50); - break; - case GPIO_SI7021: - - delayMicroseconds(20); - break; - } - - noInterrupts(); - if (!DhtExpectPulse(sensor, LOW)) { - interrupts(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_LOW " " D_PULSE)); - return false; - } - if (!DhtExpectPulse(sensor, HIGH)) { - interrupts(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_HIGH " " D_PULSE)); - return false; - } - if (!DhtExpectPulse(sensor, LOW)) { - interrupts(); - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_START_SIGNAL_LOW " " D_PULSE)); - return false; - } - - int data = 0; - for (uint32_t i = 0; i < 5; i++) { - data = DhtReadDat(sensor); - if (-1 == data) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " " D_PULSE)); - break; - } - dht_data[i] = data; - } - interrupts(); - if (-1 == data) { return false; } - - uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; - if (dht_data[4] != checksum) { - char hex_char[15]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), - ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); - return false; - } - - return true; -} - -void DhtReadTempHum(uint8_t sensor) -{ - if ((NAN == Dht[sensor].h) || (Dht[sensor].lastresult > DHT_MAX_RETRY)) { - Dht[sensor].t = NAN; - Dht[sensor].h = NAN; - } - if (DhtRead(sensor)) { - switch (Dht[sensor].type) { - case GPIO_DHT11: - Dht[sensor].h = dht_data[0]; - Dht[sensor].t = dht_data[2] + ((float)dht_data[3] * 0.1f); - break; - case GPIO_DHT22: - case GPIO_SI7021: - Dht[sensor].h = ((dht_data[0] << 8) | dht_data[1]) * 0.1; - Dht[sensor].t = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; - if (dht_data[2] & 0x80) { - Dht[sensor].t *= -1; - } - break; - } - Dht[sensor].t = ConvertTemp(Dht[sensor].t); - Dht[sensor].h = ConvertHumidity(Dht[sensor].h); - Dht[sensor].lastresult = 0; - } else { - Dht[sensor].lastresult++; - } -} - - - -bool DhtPinState() -{ - if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { - if (dht_sensors < DHT_MAX_SENSORS) { - Dht[dht_sensors].pin = XdrvMailbox.payload; - Dht[dht_sensors].type = XdrvMailbox.index; - dht_sensors++; - XdrvMailbox.index = GPIO_DHT11; - } else { - XdrvMailbox.index = 0; - } - return true; - } - return false; -} - -void DhtInit(void) -{ - if (dht_sensors) { - if (pin[GPIO_DHT11_OUT] < 99) { - dht_pin_out = pin[GPIO_DHT11_OUT]; - dht_dual_mode = true; - dht_sensors = 1; - pinMode(dht_pin_out, OUTPUT); - } - - for (uint32_t i = 0; i < dht_sensors; i++) { - pinMode(Dht[i].pin, INPUT_PULLUP); - Dht[i].lastreadtime = 0; - Dht[i].lastresult = 0; - GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); - if (dht_sensors > 1) { - snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); - } - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v3) " D_SENSORS_FOUND " %d"), dht_sensors); - } else { - dht_active = false; - } -} - -void DhtEverySecond(void) -{ - if (uptime &1) { - - - } else { - for (uint32_t i = 0; i < dht_sensors; i++) { - - DhtReadTempHum(i); - } - } -} - -void DhtShow(bool json) -{ - for (uint32_t i = 0; i < dht_sensors; i++) { - char temperature[33]; - dtostrfd(Dht[i].t, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(Dht[i].h, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMPHUM, Dht[i].stype, temperature, humidity); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (0 == i)) { - DomoticzTempHumSensor(temperature, humidity); - } -#endif -#ifdef USE_KNX - if ((0 == tele_period) && (0 == i)) { - KnxSensor(KNX_TEMPERATURE, Dht[i].t); - KnxSensor(KNX_HUMIDITY, Dht[i].h); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, Dht[i].stype, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, Dht[i].stype, humidity); -#endif - } - } -} - - - - - -bool Xsns06(uint8_t function) -{ - bool result = false; - - if (dht_active) { - switch (function) { - case FUNC_EVERY_SECOND: - DhtEverySecond(); - break; - case FUNC_JSON_APPEND: - DhtShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - DhtShow(0); - break; -#endif - case FUNC_INIT: - DhtInit(); - break; - case FUNC_PIN_STATE: - result = DhtPinState(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v4.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v4.ino" -#ifdef USE_DHT_V4 -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v4.ino" -#define XSNS_06 6 - -#define DHT_MAX_SENSORS 4 -#define DHT_MAX_RETRY 8 - -uint8_t dht_data[5]; -uint8_t dht_sensors = 0; -uint8_t dht_pin_out = 0; -bool dht_active = true; -bool dht_dual_mode = false; - -struct DHTSTRUCT { - uint8_t pin; - uint8_t type; - char stype[12]; - uint32_t lastreadtime; - uint8_t lastresult; - float t = NAN; - float h = NAN; -} Dht[DHT_MAX_SENSORS]; - -bool DhtExpectPulse(uint32_t sensor, uint32_t level) -{ - unsigned long timeout = micros() + 100; - while (digitalRead(Dht[sensor].pin) != level) { - if (micros() > timeout) { return false; } - delayMicroseconds(1); - } - return true; -} - -bool DhtRead(uint32_t sensor) -{ - dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; - - if (!dht_dual_mode) { - pinMode(Dht[sensor].pin, OUTPUT); - digitalWrite(Dht[sensor].pin, LOW); - } else { - digitalWrite(dht_pin_out, LOW); - } - - switch (Dht[sensor].type) { - case GPIO_DHT11: - delay(19); - break; - case GPIO_DHT22: - delay(2); - break; - case GPIO_SI7021: - delayMicroseconds(500); - break; - } - - if (!dht_dual_mode) { - pinMode(Dht[sensor].pin, INPUT_PULLUP); - } else { - digitalWrite(dht_pin_out, HIGH); - } - - switch (Dht[sensor].type) { - case GPIO_DHT11: - case GPIO_DHT22: - delayMicroseconds(50); - break; - case GPIO_SI7021: - delayMicroseconds(20); - break; - } - - uint32_t level = 9; - noInterrupts(); - for (uint32_t i = 0; i < 3; i++) { - level = i &1; - if (!DhtExpectPulse(sensor, level)) { break; } - level = 9; - } - if (9 == level) { - int data = 0; - for (uint32_t i = 0; i < 5; i++) { - data = 0; - for (uint32_t j = 0; j < 8; j++) { - level = 1; - if (!DhtExpectPulse(sensor, level)) { break; } - - delayMicroseconds(35); - if (digitalRead(Dht[sensor].pin)) { - data |= (1 << (7 - j)); - } - - level = 0; - if (!DhtExpectPulse(sensor, level)) { break; } - level = 9; - } - if (level < 2) { break; } - - dht_data[i] = data; - } - } - interrupts(); - if (level < 2) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), (0 == level) ? D_START_SIGNAL_LOW : D_START_SIGNAL_HIGH); - return false; - } - - uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; - if (dht_data[4] != checksum) { - char hex_char[15]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), - ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); - return false; - } - - return true; -} - -void DhtReadTempHum(uint32_t sensor) -{ - if ((NAN == Dht[sensor].h) || (Dht[sensor].lastresult > DHT_MAX_RETRY)) { - Dht[sensor].t = NAN; - Dht[sensor].h = NAN; - } - if (DhtRead(sensor)) { - switch (Dht[sensor].type) { - case GPIO_DHT11: - Dht[sensor].h = dht_data[0]; - Dht[sensor].t = dht_data[2] + ((float)dht_data[3] * 0.1f); - break; - case GPIO_DHT22: - case GPIO_SI7021: - Dht[sensor].h = ((dht_data[0] << 8) | dht_data[1]) * 0.1; - Dht[sensor].t = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; - if (dht_data[2] & 0x80) { - Dht[sensor].t *= -1; - } - break; - } - Dht[sensor].t = ConvertTemp(Dht[sensor].t); - if (Dht[sensor].h > 100) { Dht[sensor].h = 100.0; } - if (Dht[sensor].h < 0) { Dht[sensor].h = 0.0; } - Dht[sensor].h = ConvertHumidity(Dht[sensor].h); - Dht[sensor].lastresult = 0; - } else { - Dht[sensor].lastresult++; - } -} - - - -bool DhtPinState() -{ - if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { - if (dht_sensors < DHT_MAX_SENSORS) { - Dht[dht_sensors].pin = XdrvMailbox.payload; - Dht[dht_sensors].type = XdrvMailbox.index; - dht_sensors++; - XdrvMailbox.index = GPIO_DHT11; - } else { - XdrvMailbox.index = 0; - } - return true; - } - return false; -} - -void DhtInit(void) -{ - if (dht_sensors) { - if (pin[GPIO_DHT11_OUT] < 99) { - dht_pin_out = pin[GPIO_DHT11_OUT]; - dht_dual_mode = true; - dht_sensors = 1; - pinMode(dht_pin_out, OUTPUT); - } - - for (uint32_t i = 0; i < dht_sensors; i++) { - pinMode(Dht[i].pin, INPUT_PULLUP); - Dht[i].lastreadtime = 0; - Dht[i].lastresult = 0; - GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); - if (dht_sensors > 1) { - snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); - } - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v4) " D_SENSORS_FOUND " %d"), dht_sensors); - } else { - dht_active = false; - } -} - -void DhtEverySecond(void) -{ - if (uptime &1) { - } else { - for (uint32_t i = 0; i < dht_sensors; i++) { - - DhtReadTempHum(i); - } - } -} - -void DhtShow(bool json) -{ - for (uint32_t i = 0; i < dht_sensors; i++) { - char temperature[33]; - dtostrfd(Dht[i].t, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(Dht[i].h, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMPHUM, Dht[i].stype, temperature, humidity); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (0 == i)) { - DomoticzTempHumSensor(temperature, humidity); - } -#endif -#ifdef USE_KNX - if ((0 == tele_period) && (0 == i)) { - KnxSensor(KNX_TEMPERATURE, Dht[i].t); - KnxSensor(KNX_HUMIDITY, Dht[i].h); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, Dht[i].stype, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, Dht[i].stype, humidity); -#endif - } - } -} - - - - - -bool Xsns06(uint8_t function) -{ - bool result = false; - - if (dht_active) { - switch (function) { - case FUNC_EVERY_SECOND: - DhtEverySecond(); - break; - case FUNC_JSON_APPEND: - DhtShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - DhtShow(0); - break; -#endif - case FUNC_INIT: - DhtInit(); - break; - case FUNC_PIN_STATE: - result = DhtPinState(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v5.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v5.ino" -#ifdef USE_DHT -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_06_dht_v5.ino" -#define XSNS_06 6 - -#define DHT_MAX_SENSORS 4 -#define DHT_MAX_RETRY 8 - -uint8_t dht_data[5]; -uint8_t dht_sensors = 0; -uint8_t dht_pin_out = 0; -bool dht_active = true; -bool dht_dual_mode = false; - -struct DHTSTRUCT { - uint8_t pin; - uint8_t type; - uint8_t lastresult; - char stype[12]; - float t = NAN; - float h = NAN; -} Dht[DHT_MAX_SENSORS]; - -bool DhtWaitState(uint32_t sensor, uint32_t level) -{ - unsigned long timeout = micros() + 100; - while (digitalRead(Dht[sensor].pin) != level) { - if (TimeReachedUsec(timeout)) { - PrepLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), - (level) ? D_START_SIGNAL_HIGH : D_START_SIGNAL_LOW); - return false; - } - delayMicroseconds(1); - } - return true; -} - -bool DhtRead(uint32_t sensor) -{ - dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; - - if (!dht_dual_mode) { - pinMode(Dht[sensor].pin, OUTPUT); - digitalWrite(Dht[sensor].pin, LOW); - } else { - digitalWrite(dht_pin_out, LOW); - } - - switch (Dht[sensor].type) { - case GPIO_DHT11: - delay(19); - break; - case GPIO_DHT22: - delay(2); - break; - case GPIO_SI7021: - delayMicroseconds(500); - break; - } - - if (!dht_dual_mode) { - pinMode(Dht[sensor].pin, INPUT_PULLUP); - } else { - digitalWrite(dht_pin_out, HIGH); - } - - switch (Dht[sensor].type) { - case GPIO_DHT11: - case GPIO_DHT22: - delayMicroseconds(50); - break; - case GPIO_SI7021: - delayMicroseconds(20); - break; - } - - bool error = false; - noInterrupts(); - if (DhtWaitState(sensor, 0) && DhtWaitState(sensor, 1) && DhtWaitState(sensor, 0)) { - for (uint32_t i = 0; i < 5; i++) { - int data = 0; - for (uint32_t j = 0; j < 8; j++) { - if (!DhtWaitState(sensor, 1)) { - error = true; - break; - } - delayMicroseconds(35); - if (digitalRead(Dht[sensor].pin)) { - data |= (1 << (7 - j)); - } - if (!DhtWaitState(sensor, 0)) { - error = true; - break; - } - } - if (error) { break; } - dht_data[i] = data; - } - } else { - error = true; - } - interrupts(); - if (error) { return false; } - - uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; - if (dht_data[4] != checksum) { - char hex_char[15]; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), - ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); - return false; - } - - float temperature = NAN; - float humidity = NAN; - switch (Dht[sensor].type) { - case GPIO_DHT11: - humidity = dht_data[0]; - temperature = dht_data[2] + ((float)dht_data[3] * 0.1f); - break; - case GPIO_DHT22: - case GPIO_SI7021: - humidity = ((dht_data[0] << 8) | dht_data[1]) * 0.1; - temperature = (((dht_data[2] & 0x7F) << 8 ) | dht_data[3]) * 0.1; - if (dht_data[2] & 0x80) { - temperature *= -1; - } - break; - } - if (isnan(temperature) || isnan(humidity)) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Invalid NAN reading")); - return false; - } - - if (humidity > 100) { humidity = 100.0; } - if (humidity < 0) { humidity = 0.1; } - Dht[sensor].h = ConvertHumidity(humidity); - Dht[sensor].t = ConvertTemp(temperature); - Dht[sensor].lastresult = 0; - - return true; -} - - - -bool DhtPinState() -{ - if ((XdrvMailbox.index >= GPIO_DHT11) && (XdrvMailbox.index <= GPIO_SI7021)) { - if (dht_sensors < DHT_MAX_SENSORS) { - Dht[dht_sensors].pin = XdrvMailbox.payload; - Dht[dht_sensors].type = XdrvMailbox.index; - dht_sensors++; - XdrvMailbox.index = GPIO_DHT11; - } else { - XdrvMailbox.index = 0; - } - return true; - } - return false; -} - -void DhtInit(void) -{ - if (dht_sensors) { - if (pin[GPIO_DHT11_OUT] < 99) { - dht_pin_out = pin[GPIO_DHT11_OUT]; - dht_dual_mode = true; - dht_sensors = 1; - pinMode(dht_pin_out, OUTPUT); - } - - for (uint32_t i = 0; i < dht_sensors; i++) { - pinMode(Dht[i].pin, INPUT_PULLUP); - Dht[i].lastresult = DHT_MAX_RETRY; - GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); - if (dht_sensors > 1) { - snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); - } - } - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v5) " D_SENSORS_FOUND " %d"), dht_sensors); - } else { - dht_active = false; - } -} - -void DhtEverySecond(void) -{ - if (uptime &1) { - for (uint32_t sensor = 0; sensor < dht_sensors; sensor++) { - - if (!DhtRead(sensor)) { - Dht[sensor].lastresult++; - if (Dht[sensor].lastresult > DHT_MAX_RETRY) { - Dht[sensor].t = NAN; - Dht[sensor].h = NAN; - } - } - } - } -} - -void DhtShow(bool json) -{ - for (uint32_t i = 0; i < dht_sensors; i++) { - char temperature[33]; - dtostrfd(Dht[i].t, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(Dht[i].h, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMPHUM, Dht[i].stype, temperature, humidity); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (0 == i)) { - DomoticzTempHumSensor(temperature, humidity); - } -#endif -#ifdef USE_KNX - if ((0 == tele_period) && (0 == i)) { - KnxSensor(KNX_TEMPERATURE, Dht[i].t); - KnxSensor(KNX_HUMIDITY, Dht[i].h); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, Dht[i].stype, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, Dht[i].stype, humidity); -#endif - } - } -} - - - - - -bool Xsns06(uint8_t function) -{ - bool result = false; - - if (dht_active) { - switch (function) { - case FUNC_EVERY_SECOND: - DhtEverySecond(); - break; - case FUNC_JSON_APPEND: - DhtShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - DhtShow(0); - break; -#endif - case FUNC_INIT: - DhtInit(); - break; - case FUNC_PIN_STATE: - result = DhtPinState(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_07_sht1x.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_07_sht1x.ino" -#ifdef USE_I2C -#ifdef USE_SHT -# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_07_sht1x.ino" -#define XSNS_07 7 -#define XI2C_08 8 - -enum { - SHT1X_CMD_MEASURE_TEMP = B00000011, - SHT1X_CMD_MEASURE_RH = B00000101, - SHT1X_CMD_SOFT_RESET = B00011110 -}; - -uint8_t sht_sda_pin; -uint8_t sht_scl_pin; -uint8_t sht_type = 0; -char sht_types[] = "SHT1X"; -uint8_t sht_valid = 0; -float sht_temperature = 0; -float sht_humidity = 0; - -bool ShtReset(void) -{ - pinMode(sht_sda_pin, INPUT_PULLUP); - pinMode(sht_scl_pin, OUTPUT); - delay(11); - for (uint32_t i = 0; i < 9; i++) { - digitalWrite(sht_scl_pin, HIGH); - digitalWrite(sht_scl_pin, LOW); - } - bool success = ShtSendCommand(SHT1X_CMD_SOFT_RESET); - delay(11); - return success; -} - -bool ShtSendCommand(const uint8_t cmd) -{ - pinMode(sht_sda_pin, OUTPUT); - - digitalWrite(sht_sda_pin, HIGH); - digitalWrite(sht_scl_pin, HIGH); - digitalWrite(sht_sda_pin, LOW); - digitalWrite(sht_scl_pin, LOW); - digitalWrite(sht_scl_pin, HIGH); - digitalWrite(sht_sda_pin, HIGH); - digitalWrite(sht_scl_pin, LOW); - - shiftOut(sht_sda_pin, sht_scl_pin, MSBFIRST, cmd); - - bool ackerror = false; - digitalWrite(sht_scl_pin, HIGH); - pinMode(sht_sda_pin, INPUT_PULLUP); - if (digitalRead(sht_sda_pin) != LOW) { - ackerror = true; - } - digitalWrite(sht_scl_pin, LOW); - delayMicroseconds(1); - if (digitalRead(sht_sda_pin) != HIGH) { - ackerror = true; - } - if (ackerror) { - - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_DID_NOT_ACK_COMMAND)); - } - return (!ackerror); -} - -bool ShtAwaitResult(void) -{ - - for (uint32_t i = 0; i < 16; i++) { - if (LOW == digitalRead(sht_sda_pin)) { - return true; - } - delay(20); - } - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_SHT1 D_SENSOR_BUSY)); - - return false; -} - -int ShtReadData(void) -{ - int val = 0; - - - val = shiftIn(sht_sda_pin, sht_scl_pin, 8); - val <<= 8; - - pinMode(sht_sda_pin, OUTPUT); - digitalWrite(sht_sda_pin, LOW); - digitalWrite(sht_scl_pin, HIGH); - digitalWrite(sht_scl_pin, LOW); - pinMode(sht_sda_pin, INPUT_PULLUP); - - val |= shiftIn(sht_sda_pin, sht_scl_pin, 8); - - digitalWrite(sht_scl_pin, HIGH); - digitalWrite(sht_scl_pin, LOW); - return val; -} - -bool ShtRead(void) -{ - if (sht_valid) { sht_valid--; } - if (!ShtReset()) { return false; } - if (!ShtSendCommand(SHT1X_CMD_MEASURE_TEMP)) { return false; } - if (!ShtAwaitResult()) { return false; } - float tempRaw = ShtReadData(); - if (!ShtSendCommand(SHT1X_CMD_MEASURE_RH)) { return false; } - if (!ShtAwaitResult()) { return false; } - float humRaw = ShtReadData(); - - - const float d1 = -39.7; - const float d2 = 0.01; - sht_temperature = d1 + (tempRaw * d2); - const float c1 = -2.0468; - const float c2 = 0.0367; - const float c3 = -1.5955E-6; - const float t1 = 0.01; - const float t2 = 0.00008; - float rhLinear = c1 + c2 * humRaw + c3 * humRaw * humRaw; - sht_humidity = (sht_temperature - 25) * (t1 + t2 * humRaw) + rhLinear; - sht_temperature = ConvertTemp(sht_temperature); - ConvertHumidity(sht_humidity); - - sht_valid = SENSOR_MAX_MISS; - return true; -} - - - -void ShtDetect(void) -{ - sht_sda_pin = pin[GPIO_I2C_SDA]; - sht_scl_pin = pin[GPIO_I2C_SCL]; - if (ShtRead()) { - sht_type = 1; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_SHT1X_FOUND)); - } else { - Wire.begin(sht_sda_pin, sht_scl_pin); - sht_type = 0; - } -} - -void ShtEverySecond(void) -{ - if (!(uptime %4)) { - - if (!ShtRead()) { - AddLogMissed(sht_types, sht_valid); - } - } -} - -void ShtShow(bool json) -{ - if (sht_valid) { - char temperature[33]; - dtostrfd(sht_temperature, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(sht_humidity, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMPHUM, sht_types, temperature, humidity); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzTempHumSensor(temperature, humidity); - } -#endif -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, sht_temperature); - KnxSensor(KNX_HUMIDITY, sht_humidity); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, sht_types, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, sht_types, humidity); -#endif - } - } -} - - - - - -bool Xsns07(uint8_t function) -{ - if (!I2cEnabled(XI2C_08)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - ShtDetect(); - } - else if (sht_type) { - switch (function) { - case FUNC_EVERY_SECOND: - ShtEverySecond(); - break; - case FUNC_JSON_APPEND: - ShtShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - ShtShow(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_08_htu21.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_08_htu21.ino" -#ifdef USE_I2C -#ifdef USE_HTU -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_08_htu21.ino" -#define XSNS_08 8 -#define XI2C_09 9 - -#define HTU21_ADDR 0x40 - -#define SI7013_CHIPID 0x0D -#define SI7020_CHIPID 0x14 -#define SI7021_CHIPID 0x15 -#define HTU21_CHIPID 0x32 - -#define HTU21_READTEMP 0xE3 -#define HTU21_READHUM 0xE5 -#define HTU21_WRITEREG 0xE6 -#define HTU21_READREG 0xE7 -#define HTU21_RESET 0xFE -#define HTU21_HEATER_WRITE 0x51 -#define HTU21_HEATER_READ 0x11 -#define HTU21_SERIAL2_READ1 0xFC -#define HTU21_SERIAL2_READ2 0xC9 - -#define HTU21_HEATER_ON 0x04 -#define HTU21_HEATER_OFF 0xFB - -#define HTU21_RES_RH12_T14 0x00 -#define HTU21_RES_RH8_T12 0x01 -#define HTU21_RES_RH10_T13 0x80 -#define HTU21_RES_RH11_T11 0x81 - -#define HTU21_CRC8_POLYNOM 0x13100 - -const char kHtuTypes[] PROGMEM = "HTU21|SI7013|SI7020|SI7021|T/RH?"; - -uint8_t htu_address; -uint8_t htu_type = 0; -uint8_t htu_delay_temp; -uint8_t htu_delay_humidity = 50; -uint8_t htu_valid = 0; -float htu_temperature = 0; -float htu_humidity = 0; -char htu_types[7]; - -uint8_t HtuCheckCrc8(uint16_t data) -{ - for (uint32_t bit = 0; bit < 16; bit++) { - if (data & 0x8000) { - data = (data << 1) ^ HTU21_CRC8_POLYNOM; - } else { - data <<= 1; - } - } - return data >>= 8; -} - -uint8_t HtuReadDeviceId(void) -{ - uint16_t deviceID = 0; - uint8_t checksum = 0; - - Wire.beginTransmission(HTU21_ADDR); - Wire.write(HTU21_SERIAL2_READ1); - Wire.write(HTU21_SERIAL2_READ2); - Wire.endTransmission(); - - Wire.requestFrom(HTU21_ADDR, 3); - deviceID = Wire.read() << 8; - deviceID |= Wire.read(); - checksum = Wire.read(); - if (HtuCheckCrc8(deviceID) == checksum) { - deviceID = deviceID >> 8; - } else { - deviceID = 0; - } - return (uint8_t)deviceID; -} - -void HtuSetResolution(uint8_t resolution) -{ - uint8_t current = I2cRead8(HTU21_ADDR, HTU21_READREG); - current &= 0x7E; - current |= resolution; - I2cWrite8(HTU21_ADDR, HTU21_WRITEREG, current); -} - -void HtuReset(void) -{ - Wire.beginTransmission(HTU21_ADDR); - Wire.write(HTU21_RESET); - Wire.endTransmission(); - delay(15); -} - -void HtuHeater(uint8_t heater) -{ - uint8_t current = I2cRead8(HTU21_ADDR, HTU21_READREG); - - switch(heater) - { - case HTU21_HEATER_ON : current |= heater; - break; - case HTU21_HEATER_OFF : current &= heater; - break; - default : current &= heater; - break; - } - I2cWrite8(HTU21_ADDR, HTU21_WRITEREG, current); -} - -void HtuInit(void) -{ - HtuReset(); - HtuHeater(HTU21_HEATER_OFF); - HtuSetResolution(HTU21_RES_RH12_T14); -} - -bool HtuRead(void) -{ - uint8_t checksum = 0; - uint16_t sensorval = 0; - - if (htu_valid) { htu_valid--; } - - Wire.beginTransmission(HTU21_ADDR); - Wire.write(HTU21_READTEMP); - if (Wire.endTransmission() != 0) { return false; } - delay(htu_delay_temp); - - Wire.requestFrom(HTU21_ADDR, 3); - if (3 == Wire.available()) { - sensorval = Wire.read() << 8; - sensorval |= Wire.read(); - checksum = Wire.read(); - } - if (HtuCheckCrc8(sensorval) != checksum) { return false; } - - htu_temperature = ConvertTemp(0.002681 * (float)sensorval - 46.85); - - Wire.beginTransmission(HTU21_ADDR); - Wire.write(HTU21_READHUM); - if (Wire.endTransmission() != 0) { return false; } - delay(htu_delay_humidity); - - Wire.requestFrom(HTU21_ADDR, 3); - if (3 <= Wire.available()) { - sensorval = Wire.read() << 8; - sensorval |= Wire.read(); - checksum = Wire.read(); - } - if (HtuCheckCrc8(sensorval) != checksum) { return false; } - - sensorval ^= 0x02; - htu_humidity = 0.001907 * (float)sensorval - 6; - if (htu_humidity > 100) { htu_humidity = 100.0; } - if (htu_humidity < 0) { htu_humidity = 0.01; } - - if ((0.00 == htu_humidity) && (0.00 == htu_temperature)) { - htu_humidity = 0.0; - } - if ((htu_temperature > 0.00) && (htu_temperature < 80.00)) { - htu_humidity = (-0.15) * (25 - htu_temperature) + htu_humidity; - } - ConvertHumidity(htu_humidity); - - htu_valid = SENSOR_MAX_MISS; - return true; -} - - - -void HtuDetect(void) -{ - htu_address = HTU21_ADDR; - if (I2cActive(htu_address)) { return; } - - htu_type = HtuReadDeviceId(); - if (htu_type) { - uint8_t index = 0; - HtuInit(); - switch (htu_type) { - case HTU21_CHIPID: - htu_delay_temp = 50; - htu_delay_humidity = 16; - break; - case SI7021_CHIPID: - index++; - case SI7020_CHIPID: - index++; - case SI7013_CHIPID: - index++; - htu_delay_temp = 12; - htu_delay_humidity = 23; - break; - default: - index = 4; - htu_delay_temp = 50; - htu_delay_humidity = 23; - } - GetTextIndexed(htu_types, sizeof(htu_types), index, kHtuTypes); - I2cSetActiveFound(htu_address, htu_types); - } -} - -void HtuEverySecond(void) -{ - if (uptime &1) { - - if (!HtuRead()) { - AddLogMissed(htu_types, htu_valid); - } - } -} - -void HtuShow(bool json) -{ - if (htu_valid) { - char temperature[33]; - dtostrfd(htu_temperature, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(htu_humidity, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMPHUM, htu_types, temperature, humidity); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzTempHumSensor(temperature, humidity); - } -#endif -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, htu_temperature); - KnxSensor(KNX_HUMIDITY, htu_humidity); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, htu_types, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, htu_types, humidity); -#endif - } - } -} - - - - - -bool Xsns08(uint8_t function) -{ - if (!I2cEnabled(XI2C_09)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - HtuDetect(); - } - else if (htu_type) { - switch (function) { - case FUNC_EVERY_SECOND: - HtuEverySecond(); - break; - case FUNC_JSON_APPEND: - HtuShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - HtuShow(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_09_bmp.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_09_bmp.ino" -#ifdef USE_I2C -#ifdef USE_BMP -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_09_bmp.ino" -#define XSNS_09 9 -#define XI2C_10 10 - -#define BMP_ADDR1 0x76 -#define BMP_ADDR2 0x77 - -#define BMP180_CHIPID 0x55 -#define BMP280_CHIPID 0x58 -#define BME280_CHIPID 0x60 -#define BME680_CHIPID 0x61 - -#define BMP_REGISTER_CHIPID 0xD0 - -#define BMP_REGISTER_RESET 0xE0 - -#define BMP_CMND_RESET 0xB6 - -#define BMP_MAX_SENSORS 2 - -const char kBmpTypes[] PROGMEM = "BMP180|BMP280|BME280|BME680"; - -typedef struct { - uint8_t bmp_address; - char bmp_name[7]; - uint8_t bmp_type; - uint8_t bmp_model; -#ifdef USE_BME680 - uint8_t bme680_state; - float bmp_gas_resistance; -#endif - float bmp_temperature; - float bmp_pressure; - float bmp_humidity; -} bmp_sensors_t; - -uint8_t bmp_addresses[] = { BMP_ADDR1, BMP_ADDR2 }; -uint8_t bmp_count = 0; -uint8_t bmp_once = 1; - -bmp_sensors_t *bmp_sensors = nullptr; - - - - - -#define BMP180_REG_CONTROL 0xF4 -#define BMP180_REG_RESULT 0xF6 -#define BMP180_TEMPERATURE 0x2E -#define BMP180_PRESSURE3 0xF4 - -#define BMP180_AC1 0xAA -#define BMP180_AC2 0xAC -#define BMP180_AC3 0xAE -#define BMP180_AC4 0xB0 -#define BMP180_AC5 0xB2 -#define BMP180_AC6 0xB4 -#define BMP180_VB1 0xB6 -#define BMP180_VB2 0xB8 -#define BMP180_MB 0xBA -#define BMP180_MC 0xBC -#define BMP180_MD 0xBE - -#define BMP180_OSS 3 - -typedef struct { - int16_t cal_ac1; - int16_t cal_ac2; - int16_t cal_ac3; - int16_t cal_b1; - int16_t cal_b2; - int16_t cal_mc; - int16_t cal_md; - uint16_t cal_ac4; - uint16_t cal_ac5; - uint16_t cal_ac6; -} bmp180_cal_data_t; - -bmp180_cal_data_t *bmp180_cal_data = nullptr; - -bool Bmp180Calibration(uint8_t bmp_idx) -{ - if (!bmp180_cal_data) { - bmp180_cal_data = (bmp180_cal_data_t*)malloc(BMP_MAX_SENSORS * sizeof(bmp180_cal_data_t)); - } - if (!bmp180_cal_data) { return false; } - - bmp180_cal_data[bmp_idx].cal_ac1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC1); - bmp180_cal_data[bmp_idx].cal_ac2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC2); - bmp180_cal_data[bmp_idx].cal_ac3 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC3); - bmp180_cal_data[bmp_idx].cal_ac4 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC4); - bmp180_cal_data[bmp_idx].cal_ac5 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC5); - bmp180_cal_data[bmp_idx].cal_ac6 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_AC6); - bmp180_cal_data[bmp_idx].cal_b1 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_VB1); - bmp180_cal_data[bmp_idx].cal_b2 = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_VB2); - bmp180_cal_data[bmp_idx].cal_mc = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_MC); - bmp180_cal_data[bmp_idx].cal_md = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_MD); - - - if (!bmp180_cal_data[bmp_idx].cal_ac1 | - !bmp180_cal_data[bmp_idx].cal_ac2 | - !bmp180_cal_data[bmp_idx].cal_ac3 | - !bmp180_cal_data[bmp_idx].cal_ac4 | - !bmp180_cal_data[bmp_idx].cal_ac5 | - !bmp180_cal_data[bmp_idx].cal_ac6 | - !bmp180_cal_data[bmp_idx].cal_b1 | - !bmp180_cal_data[bmp_idx].cal_b2 | - !bmp180_cal_data[bmp_idx].cal_mc | - !bmp180_cal_data[bmp_idx].cal_md) { - return false; - } - - if ((bmp180_cal_data[bmp_idx].cal_ac1 == (int16_t)0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_ac2 == (int16_t)0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_ac3 == (int16_t)0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_ac4 == 0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_ac5 == 0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_ac6 == 0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_b1 == (int16_t)0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_b2 == (int16_t)0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_mc == (int16_t)0xFFFF) | - (bmp180_cal_data[bmp_idx].cal_md == (int16_t)0xFFFF)) { - return false; - } - return true; -} - -void Bmp180Read(uint8_t bmp_idx) -{ - if (!bmp180_cal_data) { return; } - - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_TEMPERATURE); - delay(5); - int ut = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT); - int32_t xt1 = (ut - (int32_t)bmp180_cal_data[bmp_idx].cal_ac6) * ((int32_t)bmp180_cal_data[bmp_idx].cal_ac5) >> 15; - int32_t xt2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_mc << 11) / (xt1 + (int32_t)bmp180_cal_data[bmp_idx].cal_md); - int32_t bmp180_b5 = xt1 + xt2; - bmp_sensors[bmp_idx].bmp_temperature = ((bmp180_b5 + 8) >> 4) / 10.0; - - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_CONTROL, BMP180_PRESSURE3); - delay(2 + (4 << BMP180_OSS)); - uint32_t up = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BMP180_REG_RESULT); - up >>= (8 - BMP180_OSS); - - int32_t b6 = bmp180_b5 - 4000; - int32_t x1 = ((int32_t)bmp180_cal_data[bmp_idx].cal_b2 * ((b6 * b6) >> 12)) >> 11; - int32_t x2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_ac2 * b6) >> 11; - int32_t x3 = x1 + x2; - int32_t b3 = ((((int32_t)bmp180_cal_data[bmp_idx].cal_ac1 * 4 + x3) << BMP180_OSS) + 2) >> 2; - - x1 = ((int32_t)bmp180_cal_data[bmp_idx].cal_ac3 * b6) >> 13; - x2 = ((int32_t)bmp180_cal_data[bmp_idx].cal_b1 * ((b6 * b6) >> 12)) >> 16; - x3 = ((x1 + x2) + 2) >> 2; - uint32_t b4 = ((uint32_t)bmp180_cal_data[bmp_idx].cal_ac4 * (uint32_t)(x3 + 32768)) >> 15; - uint32_t b7 = ((uint32_t)up - b3) * (uint32_t)(50000UL >> BMP180_OSS); - - int32_t p; - if (b7 < 0x80000000) { - p = (b7 * 2) / b4; - } - else { - p = (b7 / b4) * 2; - } - x1 = (p >> 8) * (p >> 8); - x1 = (x1 * 3038) >> 16; - x2 = (-7357 * p) >> 16; - p += ((x1 + x2 + (int32_t)3791) >> 4); - bmp_sensors[bmp_idx].bmp_pressure = (float)p / 100.0; -} - - - - - - - -#define BME280_REGISTER_CONTROLHUMID 0xF2 -#define BME280_REGISTER_CONTROL 0xF4 -#define BME280_REGISTER_CONFIG 0xF5 -#define BME280_REGISTER_PRESSUREDATA 0xF7 -#define BME280_REGISTER_TEMPDATA 0xFA -#define BME280_REGISTER_HUMIDDATA 0xFD - -#define BME280_REGISTER_DIG_T1 0x88 -#define BME280_REGISTER_DIG_T2 0x8A -#define BME280_REGISTER_DIG_T3 0x8C -#define BME280_REGISTER_DIG_P1 0x8E -#define BME280_REGISTER_DIG_P2 0x90 -#define BME280_REGISTER_DIG_P3 0x92 -#define BME280_REGISTER_DIG_P4 0x94 -#define BME280_REGISTER_DIG_P5 0x96 -#define BME280_REGISTER_DIG_P6 0x98 -#define BME280_REGISTER_DIG_P7 0x9A -#define BME280_REGISTER_DIG_P8 0x9C -#define BME280_REGISTER_DIG_P9 0x9E -#define BME280_REGISTER_DIG_H1 0xA1 -#define BME280_REGISTER_DIG_H2 0xE1 -#define BME280_REGISTER_DIG_H3 0xE3 -#define BME280_REGISTER_DIG_H4 0xE4 -#define BME280_REGISTER_DIG_H5 0xE5 -#define BME280_REGISTER_DIG_H6 0xE7 - -typedef struct { - uint16_t dig_T1; - int16_t dig_T2; - int16_t dig_T3; - uint16_t dig_P1; - int16_t dig_P2; - int16_t dig_P3; - int16_t dig_P4; - int16_t dig_P5; - int16_t dig_P6; - int16_t dig_P7; - int16_t dig_P8; - int16_t dig_P9; - int16_t dig_H2; - int16_t dig_H4; - int16_t dig_H5; - uint8_t dig_H1; - uint8_t dig_H3; - int8_t dig_H6; -} Bme280CalibrationData_t; - -Bme280CalibrationData_t *Bme280CalibrationData = nullptr; - -bool Bmx280Calibrate(uint8_t bmp_idx) -{ - - - if (!Bme280CalibrationData) { - Bme280CalibrationData = (Bme280CalibrationData_t*)malloc(BMP_MAX_SENSORS * sizeof(Bme280CalibrationData_t)); - } - if (!Bme280CalibrationData) { return false; } - - Bme280CalibrationData[bmp_idx].dig_T1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T1); - Bme280CalibrationData[bmp_idx].dig_T2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T2); - Bme280CalibrationData[bmp_idx].dig_T3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_T3); - Bme280CalibrationData[bmp_idx].dig_P1 = I2cRead16LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P1); - Bme280CalibrationData[bmp_idx].dig_P2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P2); - Bme280CalibrationData[bmp_idx].dig_P3 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P3); - Bme280CalibrationData[bmp_idx].dig_P4 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P4); - Bme280CalibrationData[bmp_idx].dig_P5 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P5); - Bme280CalibrationData[bmp_idx].dig_P6 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P6); - Bme280CalibrationData[bmp_idx].dig_P7 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P7); - Bme280CalibrationData[bmp_idx].dig_P8 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P8); - Bme280CalibrationData[bmp_idx].dig_P9 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_P9); - if (BME280_CHIPID == bmp_sensors[bmp_idx].bmp_type) { - Bme280CalibrationData[bmp_idx].dig_H1 = I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H1); - Bme280CalibrationData[bmp_idx].dig_H2 = I2cReadS16_LE(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H2); - Bme280CalibrationData[bmp_idx].dig_H3 = I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H3); - Bme280CalibrationData[bmp_idx].dig_H4 = (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H4) << 4) | (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H4 + 1) & 0xF); - Bme280CalibrationData[bmp_idx].dig_H5 = (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H5 + 1) << 4) | (I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H5) >> 4); - Bme280CalibrationData[bmp_idx].dig_H6 = (int8_t)I2cRead8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_DIG_H6); - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0x00); - - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROLHUMID, 0x01); - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONFIG, 0xA0); - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0x27); - } else { - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_CONTROL, 0xB7); - } - - return true; -} - -void Bme280Read(uint8_t bmp_idx) -{ - if (!Bme280CalibrationData) { return; } - - int32_t adc_T = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_TEMPDATA); - adc_T >>= 4; - - int32_t vart1 = ((((adc_T >> 3) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1 << 1))) * ((int32_t)Bme280CalibrationData[bmp_idx].dig_T2)) >> 11; - int32_t vart2 = (((((adc_T >> 4) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1)) * ((adc_T >> 4) - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T1))) >> 12) * - ((int32_t)Bme280CalibrationData[bmp_idx].dig_T3)) >> 14; - int32_t t_fine = vart1 + vart2; - float T = (t_fine * 5 + 128) >> 8; - bmp_sensors[bmp_idx].bmp_temperature = T / 100.0; - - int32_t adc_P = I2cRead24(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_PRESSUREDATA); - adc_P >>= 4; - - int64_t var1 = ((int64_t)t_fine) - 128000; - int64_t var2 = var1 * var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P6; - var2 = var2 + ((var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P5) << 17); - var2 = var2 + (((int64_t)Bme280CalibrationData[bmp_idx].dig_P4) << 35); - var1 = ((var1 * var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P3) >> 8) + ((var1 * (int64_t)Bme280CalibrationData[bmp_idx].dig_P2) << 12); - var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)Bme280CalibrationData[bmp_idx].dig_P1) >> 33; - if (0 == var1) { - return; - } - int64_t p = 1048576 - adc_P; - p = (((p << 31) - var2) * 3125) / var1; - var1 = (((int64_t)Bme280CalibrationData[bmp_idx].dig_P9) * (p >> 13) * (p >> 13)) >> 25; - var2 = (((int64_t)Bme280CalibrationData[bmp_idx].dig_P8) * p) >> 19; - p = ((p + var1 + var2) >> 8) + (((int64_t)Bme280CalibrationData[bmp_idx].dig_P7) << 4); - bmp_sensors[bmp_idx].bmp_pressure = (float)p / 25600.0; - - if (BMP280_CHIPID == bmp_sensors[bmp_idx].bmp_type) { return; } - - int32_t adc_H = I2cRead16(bmp_sensors[bmp_idx].bmp_address, BME280_REGISTER_HUMIDDATA); - - int32_t v_x1_u32r = (t_fine - ((int32_t)76800)); - v_x1_u32r = (((((adc_H << 14) - (((int32_t)Bme280CalibrationData[bmp_idx].dig_H4) << 20) - - (((int32_t)Bme280CalibrationData[bmp_idx].dig_H5) * v_x1_u32r)) + ((int32_t)16384)) >> 15) * - (((((((v_x1_u32r * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H6)) >> 10) * - (((v_x1_u32r * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + - ((int32_t)2097152)) * ((int32_t)Bme280CalibrationData[bmp_idx].dig_H2) + 8192) >> 14)); - v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * - ((int32_t)Bme280CalibrationData[bmp_idx].dig_H1)) >> 4)); - v_x1_u32r = (v_x1_u32r < 0) ? 0 : v_x1_u32r; - v_x1_u32r = (v_x1_u32r > 419430400) ? 419430400 : v_x1_u32r; - float h = (v_x1_u32r >> 12); - bmp_sensors[bmp_idx].bmp_humidity = h / 1024.0; -} - -#ifdef USE_BME680 - - - - -#include - -struct bme680_dev *gas_sensor = nullptr; - -static void BmeDelayMs(uint32_t ms) -{ - delay(ms); -} - -bool Bme680Init(uint8_t bmp_idx) -{ - if (!gas_sensor) { - gas_sensor = (bme680_dev*)malloc(BMP_MAX_SENSORS * sizeof(bme680_dev)); - } - if (!gas_sensor) { return false; } - - gas_sensor[bmp_idx].dev_id = bmp_sensors[bmp_idx].bmp_address; - gas_sensor[bmp_idx].intf = BME680_I2C_INTF; - gas_sensor[bmp_idx].read = &I2cReadBuffer; - gas_sensor[bmp_idx].write = &I2cWriteBuffer; - gas_sensor[bmp_idx].delay_ms = BmeDelayMs; - - - - gas_sensor[bmp_idx].amb_temp = 25; - - int8_t rslt = BME680_OK; - rslt = bme680_init(&gas_sensor[bmp_idx]); - if (rslt != BME680_OK) { return false; } - - - gas_sensor[bmp_idx].tph_sett.os_hum = BME680_OS_2X; - gas_sensor[bmp_idx].tph_sett.os_pres = BME680_OS_4X; - gas_sensor[bmp_idx].tph_sett.os_temp = BME680_OS_8X; - gas_sensor[bmp_idx].tph_sett.filter = BME680_FILTER_SIZE_3; - - - gas_sensor[bmp_idx].gas_sett.run_gas = BME680_ENABLE_GAS_MEAS; - - gas_sensor[bmp_idx].gas_sett.heatr_temp = 320; - gas_sensor[bmp_idx].gas_sett.heatr_dur = 150; - - - - gas_sensor[bmp_idx].power_mode = BME680_FORCED_MODE; - - - uint8_t set_required_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_FILTER_SEL | BME680_GAS_SENSOR_SEL; - - - rslt = bme680_set_sensor_settings(set_required_settings,&gas_sensor[bmp_idx]); - if (rslt != BME680_OK) { return false; } - - bmp_sensors[bmp_idx].bme680_state = 0; - - return true; -} - -void Bme680Read(uint8_t bmp_idx) -{ - if (!gas_sensor) { return; } - - int8_t rslt = BME680_OK; - - if (BME680_CHIPID == bmp_sensors[bmp_idx].bmp_type) { - if (0 == bmp_sensors[bmp_idx].bme680_state) { - - rslt = bme680_set_sensor_mode(&gas_sensor[bmp_idx]); - if (rslt != BME680_OK) { return; } - - - - - - - - bmp_sensors[bmp_idx].bme680_state = 1; - } else { - bmp_sensors[bmp_idx].bme680_state = 0; - - struct bme680_field_data data; - rslt = bme680_get_sensor_data(&data, &gas_sensor[bmp_idx]); - if (rslt != BME680_OK) { return; } - - bmp_sensors[bmp_idx].bmp_temperature = data.temperature / 100.0; - bmp_sensors[bmp_idx].bmp_humidity = data.humidity / 1000.0; - bmp_sensors[bmp_idx].bmp_pressure = data.pressure / 100.0; - - if (data.status & BME680_GASM_VALID_MSK) { - bmp_sensors[bmp_idx].bmp_gas_resistance = data.gas_resistance / 1000.0; - } else { - bmp_sensors[bmp_idx].bmp_gas_resistance = 0; - } - } - } - return; -} - -#endif - - - -void BmpDetect(void) -{ - int bmp_sensor_size = BMP_MAX_SENSORS * sizeof(bmp_sensors_t); - if (!bmp_sensors) { - bmp_sensors = (bmp_sensors_t*)malloc(bmp_sensor_size); - } - if (!bmp_sensors) { return; } - memset(bmp_sensors, 0, bmp_sensor_size); - - for (uint32_t i = 0; i < BMP_MAX_SENSORS; i++) { - if (I2cActive(bmp_addresses[i])) { continue; } - uint8_t bmp_type = I2cRead8(bmp_addresses[i], BMP_REGISTER_CHIPID); - if (bmp_type) { - bmp_sensors[bmp_count].bmp_address = bmp_addresses[i]; - bmp_sensors[bmp_count].bmp_type = bmp_type; - bmp_sensors[bmp_count].bmp_model = 0; - - bool success = false; - switch (bmp_type) { - case BMP180_CHIPID: - success = Bmp180Calibration(bmp_count); - break; - case BME280_CHIPID: - bmp_sensors[bmp_count].bmp_model++; - case BMP280_CHIPID: - bmp_sensors[bmp_count].bmp_model++; - success = Bmx280Calibrate(bmp_count); - break; -#ifdef USE_BME680 - case BME680_CHIPID: - bmp_sensors[bmp_count].bmp_model = 3; - success = Bme680Init(bmp_count); - break; -#endif - } - if (success) { - GetTextIndexed(bmp_sensors[bmp_count].bmp_name, sizeof(bmp_sensors[bmp_count].bmp_name), bmp_sensors[bmp_count].bmp_model, kBmpTypes); - I2cSetActiveFound(bmp_sensors[bmp_count].bmp_address, bmp_sensors[bmp_count].bmp_name); - bmp_count++; - } - } - } -} - -void BmpRead(void) -{ - for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { - switch (bmp_sensors[bmp_idx].bmp_type) { - case BMP180_CHIPID: - Bmp180Read(bmp_idx); - break; - case BMP280_CHIPID: - case BME280_CHIPID: - Bme280Read(bmp_idx); - break; -#ifdef USE_BME680 - case BME680_CHIPID: - Bme680Read(bmp_idx); - break; -#endif - } - } - ConvertTemp(bmp_sensors[0].bmp_temperature); - ConvertHumidity(bmp_sensors[0].bmp_humidity); -} - -void BmpShow(bool json) -{ - for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { - if (bmp_sensors[bmp_idx].bmp_type) { - float bmp_sealevel = 0.0; - if (bmp_sensors[bmp_idx].bmp_pressure != 0.0) { - bmp_sealevel = (bmp_sensors[bmp_idx].bmp_pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0), 5.255)) - 21.6; - bmp_sealevel = ConvertPressure(bmp_sealevel); - } - float bmp_temperature = ConvertTemp(bmp_sensors[bmp_idx].bmp_temperature); - float bmp_pressure = ConvertPressure(bmp_sensors[bmp_idx].bmp_pressure); - - char name[10]; - strlcpy(name, bmp_sensors[bmp_idx].bmp_name, sizeof(name)); - if (bmp_count > 1) { - snprintf_P(name, sizeof(name), PSTR("%s%c%02X"), name, IndexSeparator(), bmp_sensors[bmp_idx].bmp_address); - } - - char temperature[33]; - dtostrfd(bmp_temperature, Settings.flag2.temperature_resolution, temperature); - char pressure[33]; - dtostrfd(bmp_pressure, Settings.flag2.pressure_resolution, pressure); - char sea_pressure[33]; - dtostrfd(bmp_sealevel, Settings.flag2.pressure_resolution, sea_pressure); - char humidity[33]; - dtostrfd(bmp_sensors[bmp_idx].bmp_humidity, Settings.flag2.humidity_resolution, humidity); -#ifdef USE_BME680 - char gas_resistance[33]; - dtostrfd(bmp_sensors[bmp_idx].bmp_gas_resistance, 2, gas_resistance); -#endif - - if (json) { - char json_humidity[40]; - snprintf_P(json_humidity, sizeof(json_humidity), PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity); - char json_sealevel[40]; - snprintf_P(json_sealevel, sizeof(json_sealevel), PSTR(",\"" D_JSON_PRESSUREATSEALEVEL "\":%s"), sea_pressure); -#ifdef USE_BME680 - char json_gas[40]; - snprintf_P(json_gas, sizeof(json_gas), PSTR(",\"" D_JSON_GAS "\":%s"), gas_resistance); - - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s%s}"), - name, - temperature, - (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", - pressure, - (Settings.altitude != 0) ? json_sealevel : "", - (bmp_sensors[bmp_idx].bmp_model >= 3) ? json_gas : ""); -#else - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s,\"" D_JSON_PRESSURE "\":%s%s}"), - name, temperature, (bmp_sensors[bmp_idx].bmp_model >= 2) ? json_humidity : "", pressure, (Settings.altitude != 0) ? json_sealevel : ""); -#endif - -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (0 == bmp_idx)) { - DomoticzTempHumPressureSensor(temperature, humidity, pressure); -#ifdef USE_BME680 - if (bmp_sensors[bmp_idx].bmp_model >= 3) { DomoticzSensor(DZ_AIRQUALITY, (uint32_t)bmp_sensors[bmp_idx].bmp_gas_resistance); } -#endif - } -#endif - -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, bmp_temperature); - KnxSensor(KNX_HUMIDITY, bmp_sensors[bmp_idx].bmp_humidity); - } -#endif - -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, name, temperature, TempUnit()); - if (bmp_sensors[bmp_idx].bmp_model >= 2) { - WSContentSend_PD(HTTP_SNS_HUM, name, humidity); - } - WSContentSend_PD(HTTP_SNS_PRESSURE, name, pressure, PressureUnit().c_str()); - if (Settings.altitude != 0) { - WSContentSend_PD(HTTP_SNS_SEAPRESSURE, name, sea_pressure, PressureUnit().c_str()); - } -#ifdef USE_BME680 - if (bmp_sensors[bmp_idx].bmp_model >= 3) { - WSContentSend_PD(PSTR("{s}%s " D_GAS "{m}%s " D_UNIT_KILOOHM "{e}"), name, gas_resistance); - } -#endif - -#endif - } - } - } -} - -#ifdef USE_DEEPSLEEP - -void BMP_EnterSleep(void) -{ - for (uint32_t bmp_idx = 0; bmp_idx < bmp_count; bmp_idx++) { - switch (bmp_sensors[bmp_idx].bmp_type) { - case BMP180_CHIPID: - case BMP280_CHIPID: - case BME280_CHIPID: - I2cWrite8(bmp_sensors[bmp_idx].bmp_address, BMP_REGISTER_RESET, BMP_CMND_RESET); - break; - default: - break; - } - } -} - -#endif - - - - - -bool Xsns09(uint8_t function) -{ - if (!I2cEnabled(XI2C_10)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - BmpDetect(); - } - else if (bmp_count) { - switch (function) { - case FUNC_EVERY_SECOND: - BmpRead(); - break; - case FUNC_JSON_APPEND: - BmpShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - BmpShow(0); - break; -#endif -#ifdef USE_DEEPSLEEP - case FUNC_SAVE_BEFORE_RESTART: - BMP_EnterSleep(); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_10_bh1750.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_10_bh1750.ino" -#ifdef USE_I2C -#ifdef USE_BH1750 - - - - - - -#define XSNS_10 10 -#define XI2C_11 11 - -#define BH1750_ADDR1 0x23 -#define BH1750_ADDR2 0x5C - -#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 - -uint8_t bh1750_address; -uint8_t bh1750_addresses[] = { BH1750_ADDR1, BH1750_ADDR2 }; -uint8_t bh1750_type = 0; -uint8_t bh1750_valid = 0; -uint16_t bh1750_illuminance = 0; -char bh1750_types[] = "BH1750"; - -bool Bh1750Read(void) -{ - if (bh1750_valid) { bh1750_valid--; } - - if (2 != Wire.requestFrom(bh1750_address, (uint8_t)2)) { return false; } - uint8_t msb = Wire.read(); - uint8_t lsb = Wire.read(); - bh1750_illuminance = ((msb << 8) | lsb) / 1.2; - bh1750_valid = SENSOR_MAX_MISS; - return true; -} - - - -void Bh1750Detect(void) -{ - for (uint32_t i = 0; i < sizeof(bh1750_addresses); i++) { - bh1750_address = bh1750_addresses[i]; - if (I2cActive(bh1750_address)) { continue; } - Wire.beginTransmission(bh1750_address); - Wire.write(BH1750_CONTINUOUS_HIGH_RES_MODE); - if (!Wire.endTransmission()) { - I2cSetActiveFound(bh1750_address, bh1750_types); - bh1750_type = 1; - break; - } - } -} - -void Bh1750EverySecond(void) -{ - - if (!Bh1750Read()) { - AddLogMissed(bh1750_types, bh1750_valid); - } -} - -void Bh1750Show(bool json) -{ - if (bh1750_valid) { - if (json) { - ResponseAppend_P(JSON_SNS_ILLUMINANCE, bh1750_types, bh1750_illuminance); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_ILLUMINANCE, bh1750_illuminance); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, bh1750_types, bh1750_illuminance); -#endif - } - } -} - - - - - -bool Xsns10(uint8_t function) -{ - if (!I2cEnabled(XI2C_11)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Bh1750Detect(); - } - else if (bh1750_type) { - switch (function) { - case FUNC_EVERY_SECOND: - Bh1750EverySecond(); - break; - case FUNC_JSON_APPEND: - Bh1750Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Bh1750Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_11_veml6070.ino" -# 89 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_11_veml6070.ino" -#ifdef USE_I2C -#ifdef USE_VEML6070 - - - - - - -#define XSNS_11 11 -#define XI2C_12 12 - -#define VEML6070_ADDR_H 0x39 -#define VEML6070_ADDR_L 0x38 -#define VEML6070_INTEGRATION_TIME 3 -#define VEML6070_ENABLE 1 -#define VEML6070_DISABLE 0 -#define VEML6070_RSET_DEFAULT 270000 -#define VEML6070_UV_MAX_INDEX 15 -#define VEML6070_UV_MAX_DEFAULT 11 -#define VEML6070_POWER_COEFFCIENT 0.025 -#define VEML6070_TABLE_COEFFCIENT 32.86270591 - - - - - -const char kVemlTypes[] PROGMEM = "VEML6070"; -double uv_risk_map[VEML6070_UV_MAX_INDEX] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; -double uvrisk = 0; -double uvpower = 0; -uint16_t uvlevel = 0; -uint8_t veml6070_addr_low = VEML6070_ADDR_L; -uint8_t veml6070_addr_high = VEML6070_ADDR_H; -uint8_t itime = VEML6070_INTEGRATION_TIME; -uint8_t veml6070_type = 0; -char veml6070_name[9]; -char str_uvrisk_text[10]; - - - -void Veml6070Detect(void) -{ - if (I2cActive(VEML6070_ADDR_L)) { return; } - - - Wire.beginTransmission(VEML6070_ADDR_L); - Wire.write((itime << 2) | 0x02); - uint8_t status = Wire.endTransmission(); - - if (!status) { - veml6070_type = 1; - Veml6070UvTableInit(); - uint8_t veml_model = 0; - GetTextIndexed(veml6070_name, sizeof(veml6070_name), veml_model, kVemlTypes); - I2cSetActiveFound(VEML6070_ADDR_L, veml6070_name); - } -} - - - -void Veml6070UvTableInit(void) -{ - - for (uint32_t i = 0; i < VEML6070_UV_MAX_INDEX; i++) { -#ifdef USE_VEML6070_RSET - if ( (USE_VEML6070_RSET >= 220000) && (USE_VEML6070_RSET <= 1000000) ) { - uv_risk_map[i] = ( (USE_VEML6070_RSET / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); - } else { - uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor error %d"), USE_VEML6070_RSET); - } -#else - uv_risk_map[i] = ( (VEML6070_RSET_DEFAULT / VEML6070_TABLE_COEFFCIENT) / VEML6070_UV_MAX_DEFAULT ) * (i+1); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 resistor default used %d"), VEML6070_RSET_DEFAULT); -#endif - } -} - - - -void Veml6070EverySecond(void) -{ - - Veml6070ModeCmd(1); - uvlevel = Veml6070ReadUv(); - uvrisk = Veml6070UvRiskLevel(uvlevel); - uvpower = Veml6070UvPower(uvrisk); - Veml6070ModeCmd(0); -} - - - -void Veml6070ModeCmd(bool mode_cmd) -{ - - - Wire.beginTransmission(VEML6070_ADDR_L); - Wire.write((mode_cmd << 0) | 0x02 | (itime << 2)); - uint8_t status = Wire.endTransmission(); - - if (!status) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 mode_cmd")); - } -} - - - -uint16_t Veml6070ReadUv(void) -{ - uint16_t uv_raw = 0; - - if (Wire.requestFrom(VEML6070_ADDR_H, 1) != 1) { - return -1; - } - uv_raw = Wire.read(); - uv_raw <<= 8; - - if (Wire.requestFrom(VEML6070_ADDR_L, 1) != 1) { - return -1; - } - uv_raw |= Wire.read(); - - return uv_raw; -} - - - -double Veml6070UvRiskLevel(uint16_t uv_level) -{ - double risk = 0; - if (uv_level < uv_risk_map[VEML6070_UV_MAX_INDEX-1]) { - risk = (double)uv_level / uv_risk_map[0]; - - if ( (risk >= 0) && (risk <= 2.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_1); } - else if ( (risk >= 3.0) && (risk <= 5.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_2); } - else if ( (risk >= 6.0) && (risk <= 7.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_3); } - else if ( (risk >= 8.0) && (risk <= 10.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_4); } - else if ( (risk >= 11.0) && (risk <= 12.9) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_5); } - else if ( (risk >= 13.0) && (risk <= 25.0) ) { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_6); } - else { snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); } - return risk; - } else { - - snprintf_P(str_uvrisk_text, sizeof(str_uvrisk_text), D_UV_INDEX_7); - return ( risk = 99 ); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "VEML6070 out of range %d"), risk); - } -} - - - -double Veml6070UvPower(double uvrisk) -{ - - double power = 0; - return ( power = VEML6070_POWER_COEFFCIENT * uvrisk ); -} - - - - -#ifdef USE_WEBSERVER - -#ifdef USE_VEML6070_SHOW_RAW - const char HTTP_SNS_UV_LEVEL[] PROGMEM = "{s}VEML6070 " D_UV_LEVEL "{m}%s " D_UNIT_INCREMENTS "{e}"; -#endif - - const char HTTP_SNS_UV_INDEX[] PROGMEM = "{s}VEML6070 " D_UV_INDEX "{m}%s %s{e}"; - const char HTTP_SNS_UV_POWER[] PROGMEM = "{s}VEML6070 " D_UV_POWER "{m}%s " D_UNIT_WATT_METER_QUADRAT "{e}"; -#endif - - - -void Veml6070Show(bool json) -{ - - char str_uvlevel[33]; - dtostrfd((double)uvlevel, 0, str_uvlevel); - char str_uvrisk[33]; - dtostrfd(uvrisk, 2, str_uvrisk); - char str_uvpower[33]; - dtostrfd(uvpower, 3, str_uvpower); - if (json) { -#ifdef USE_VEML6070_SHOW_RAW - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_UV_LEVEL "\":%s,\"" D_JSON_UV_INDEX "\":%s,\"" D_JSON_UV_INDEX_TEXT "\":\"%s\",\"" D_JSON_UV_POWER "\":%s}"), - veml6070_name, str_uvlevel, str_uvrisk, str_uvrisk_text, str_uvpower); -#else - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_UV_INDEX "\":%s,\"" D_JSON_UV_INDEX_TEXT "\":\"%s\",\"" D_JSON_UV_POWER "\":%s}"), - veml6070_name, str_uvrisk, str_uvrisk_text, str_uvpower); -#endif -#ifdef USE_DOMOTICZ - if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, uvlevel); } -#endif -#ifdef USE_WEBSERVER - } else { -#ifdef USE_VEML6070_SHOW_RAW - WSContentSend_PD(HTTP_SNS_UV_LEVEL, str_uvlevel); -#endif - WSContentSend_PD(HTTP_SNS_UV_INDEX, str_uvrisk, str_uvrisk_text); - WSContentSend_PD(HTTP_SNS_UV_POWER, str_uvpower); -#endif - } -} - - - - - -bool Xsns11(uint8_t function) -{ - if (!I2cEnabled(XI2C_12)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Veml6070Detect(); - } - else if (veml6070_type) { - switch (function) { - case FUNC_EVERY_SECOND: - Veml6070EverySecond(); - break; - case FUNC_JSON_APPEND: - Veml6070Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Veml6070Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_12_ads1115.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_12_ads1115.ino" -#ifdef USE_I2C -#ifdef USE_ADS1115 -# 43 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_12_ads1115.ino" -#define XSNS_12 12 -#define XI2C_13 13 - -#define ADS1115_ADDRESS_ADDR_GND 0x48 -#define ADS1115_ADDRESS_ADDR_VDD 0x49 -#define ADS1115_ADDRESS_ADDR_SDA 0x4A -#define ADS1115_ADDRESS_ADDR_SCL 0x4B - -#define ADS1115_CONVERSIONDELAY (8) - - - - -#define ADS1115_REG_POINTER_MASK (0x03) -#define ADS1115_REG_POINTER_CONVERT (0x00) -#define ADS1115_REG_POINTER_CONFIG (0x01) -#define ADS1115_REG_POINTER_LOWTHRESH (0x02) -#define ADS1115_REG_POINTER_HITHRESH (0x03) - - - - -#define ADS1115_REG_CONFIG_OS_MASK (0x8000) -#define ADS1115_REG_CONFIG_OS_SINGLE (0x8000) -#define ADS1115_REG_CONFIG_OS_BUSY (0x0000) -#define ADS1115_REG_CONFIG_OS_NOTBUSY (0x8000) - -#define ADS1115_REG_CONFIG_MUX_MASK (0x7000) -#define ADS1115_REG_CONFIG_MUX_DIFF_0_1 (0x0000) -#define ADS1115_REG_CONFIG_MUX_DIFF_0_3 (0x1000) -#define ADS1115_REG_CONFIG_MUX_DIFF_1_3 (0x2000) -#define ADS1115_REG_CONFIG_MUX_DIFF_2_3 (0x3000) -#define ADS1115_REG_CONFIG_MUX_SINGLE_0 (0x4000) -#define ADS1115_REG_CONFIG_MUX_SINGLE_1 (0x5000) -#define ADS1115_REG_CONFIG_MUX_SINGLE_2 (0x6000) -#define ADS1115_REG_CONFIG_MUX_SINGLE_3 (0x7000) - -#define ADS1115_REG_CONFIG_PGA_MASK (0x0E00) -#define ADS1115_REG_CONFIG_PGA_6_144V (0x0000) -#define ADS1115_REG_CONFIG_PGA_4_096V (0x0200) -#define ADS1115_REG_CONFIG_PGA_2_048V (0x0400) -#define ADS1115_REG_CONFIG_PGA_1_024V (0x0600) -#define ADS1115_REG_CONFIG_PGA_0_512V (0x0800) -#define ADS1115_REG_CONFIG_PGA_0_256V (0x0A00) - -#define ADS1115_REG_CONFIG_MODE_MASK (0x0100) -#define ADS1115_REG_CONFIG_MODE_CONTIN (0x0000) -#define ADS1115_REG_CONFIG_MODE_SINGLE (0x0100) - -#define ADS1115_REG_CONFIG_DR_MASK (0x00E0) -#define ADS1115_REG_CONFIG_DR_128SPS (0x0000) -#define ADS1115_REG_CONFIG_DR_250SPS (0x0020) -#define ADS1115_REG_CONFIG_DR_490SPS (0x0040) -#define ADS1115_REG_CONFIG_DR_920SPS (0x0060) -#define ADS1115_REG_CONFIG_DR_1600SPS (0x0080) -#define ADS1115_REG_CONFIG_DR_2400SPS (0x00A0) -#define ADS1115_REG_CONFIG_DR_3300SPS (0x00C0) -#define ADS1115_REG_CONFIG_DR_6000SPS (0x00E0) - -#define ADS1115_REG_CONFIG_CMODE_MASK (0x0010) -#define ADS1115_REG_CONFIG_CMODE_TRAD (0x0000) -#define ADS1115_REG_CONFIG_CMODE_WINDOW (0x0010) - -#define ADS1115_REG_CONFIG_CPOL_MASK (0x0008) -#define ADS1115_REG_CONFIG_CPOL_ACTVLOW (0x0000) -#define ADS1115_REG_CONFIG_CPOL_ACTVHI (0x0008) - -#define ADS1115_REG_CONFIG_CLAT_MASK (0x0004) -#define ADS1115_REG_CONFIG_CLAT_NONLAT (0x0000) -#define ADS1115_REG_CONFIG_CLAT_LATCH (0x0004) - -#define ADS1115_REG_CONFIG_CQUE_MASK (0x0003) -#define ADS1115_REG_CONFIG_CQUE_1CONV (0x0000) -#define ADS1115_REG_CONFIG_CQUE_2CONV (0x0001) -#define ADS1115_REG_CONFIG_CQUE_4CONV (0x0002) -#define ADS1115_REG_CONFIG_CQUE_NONE (0x0003) - -struct ADS1115 { - uint8_t count = 0; - uint8_t address; - uint8_t addresses[4] = { ADS1115_ADDRESS_ADDR_GND, ADS1115_ADDRESS_ADDR_VDD, ADS1115_ADDRESS_ADDR_SDA, ADS1115_ADDRESS_ADDR_SCL }; - uint8_t found[4] = {false,false,false,false}; -} Ads1115; - - - -void Ads1115StartComparator(uint8_t channel, uint16_t mode) -{ - - uint16_t config = mode | - ADS1115_REG_CONFIG_CQUE_NONE | - ADS1115_REG_CONFIG_CLAT_NONLAT | - ADS1115_REG_CONFIG_PGA_6_144V | - ADS1115_REG_CONFIG_CPOL_ACTVLOW | - ADS1115_REG_CONFIG_CMODE_TRAD | - ADS1115_REG_CONFIG_DR_6000SPS; - - - config |= (ADS1115_REG_CONFIG_MUX_SINGLE_0 + (0x1000 * channel)); - - - I2cWrite16(Ads1115.address, ADS1115_REG_POINTER_CONFIG, config); -} - -int16_t Ads1115GetConversion(uint8_t channel) -{ - Ads1115StartComparator(channel, ADS1115_REG_CONFIG_MODE_SINGLE); - - delay(ADS1115_CONVERSIONDELAY); - - I2cRead16(Ads1115.address, ADS1115_REG_POINTER_CONVERT); - - Ads1115StartComparator(channel, ADS1115_REG_CONFIG_MODE_CONTIN); - delay(ADS1115_CONVERSIONDELAY); - - uint16_t res = I2cRead16(Ads1115.address, ADS1115_REG_POINTER_CONVERT); - return (int16_t)res; -} - - - -void Ads1115Detect(void) -{ - for (uint32_t i = 0; i < sizeof(Ads1115.addresses); i++) { - if (!Ads1115.found[i]) { - Ads1115.address = Ads1115.addresses[i]; - if (I2cActive(Ads1115.address)) { continue; } - uint16_t buffer; - if (I2cValidRead16(&buffer, Ads1115.address, ADS1115_REG_POINTER_CONVERT) && - I2cValidRead16(&buffer, Ads1115.address, ADS1115_REG_POINTER_CONFIG)) { - Ads1115StartComparator(i, ADS1115_REG_CONFIG_MODE_CONTIN); - I2cSetActiveFound(Ads1115.address, "ADS1115"); - Ads1115.found[i] = 1; - Ads1115.count++; - } - } - } -} - -void Ads1115Show(bool json) -{ - int16_t values[4]; - - for (uint32_t t = 0; t < sizeof(Ads1115.addresses); t++) { - - if (Ads1115.found[t]) { - - uint8_t old_address = Ads1115.address; - Ads1115.address = Ads1115.addresses[t]; - for (uint32_t i = 0; i < 4; i++) { - values[i] = Ads1115GetConversion(i); - - } - Ads1115.address = old_address; - - char label[15]; - if (1 == Ads1115.count) { - - snprintf_P(label, sizeof(label), PSTR("ADS1115")); - } else { - - snprintf_P(label, sizeof(label), PSTR("ADS1115%c%02x"), IndexSeparator(), Ads1115.addresses[t]); - } - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{"), label); - for (uint32_t i = 0; i < 4; i++) { - ResponseAppend_P(PSTR("%s\"A%d\":%d"), (0 == i) ? "" : ",", i, values[i]); - } - ResponseJsonEnd(); - } -#ifdef USE_WEBSERVER - else { - for (uint32_t i = 0; i < 4; i++) { - WSContentSend_PD(HTTP_SNS_ANALOG, label, i, values[i]); - } - } -#endif - } - } -} - - - - - -bool Xsns12(uint8_t function) -{ - if (!I2cEnabled(XI2C_13)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Ads1115Detect(); - } - else if (Ads1115.count) { - switch (function) { - case FUNC_JSON_APPEND: - Ads1115Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Ads1115Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_13_ina219.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_13_ina219.ino" -#ifdef USE_I2C -#ifdef USE_INA219 -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_13_ina219.ino" -#define XSNS_13 13 -#define XI2C_14 14 - -#define INA219_ADDRESS1 (0x40) -#define INA219_ADDRESS2 (0x41) -#define INA219_ADDRESS3 (0x44) -#define INA219_ADDRESS4 (0x45) - -#define INA219_READ (0x01) -#define INA219_REG_CONFIG (0x00) - -#define INA219_CONFIG_RESET (0x8000) - -#define INA219_CONFIG_BVOLTAGERANGE_MASK (0x2000) -#define INA219_CONFIG_BVOLTAGERANGE_16V (0x0000) -#define INA219_CONFIG_BVOLTAGERANGE_32V (0x2000) - -#define INA219_CONFIG_GAIN_MASK (0x1800) -#define INA219_CONFIG_GAIN_1_40MV (0x0000) -#define INA219_CONFIG_GAIN_2_80MV (0x0800) -#define INA219_CONFIG_GAIN_4_160MV (0x1000) -#define INA219_CONFIG_GAIN_8_320MV (0x1800) - -#define INA219_CONFIG_BADCRES_MASK (0x0780) -#define INA219_CONFIG_BADCRES_9BIT (0x0080) -#define INA219_CONFIG_BADCRES_10BIT (0x0100) -#define INA219_CONFIG_BADCRES_11BIT (0x0200) -#define INA219_CONFIG_BADCRES_12BIT (0x0400) - -#define INA219_CONFIG_SADCRES_MASK (0x0078) -#define INA219_CONFIG_SADCRES_9BIT_1S_84US (0x0000) -#define INA219_CONFIG_SADCRES_10BIT_1S_148US (0x0008) -#define INA219_CONFIG_SADCRES_11BIT_1S_276US (0x0010) -#define INA219_CONFIG_SADCRES_12BIT_1S_532US (0x0018) -#define INA219_CONFIG_SADCRES_12BIT_2S_1060US (0x0048) -#define INA219_CONFIG_SADCRES_12BIT_4S_2130US (0x0050) -#define INA219_CONFIG_SADCRES_12BIT_8S_4260US (0x0058) -#define INA219_CONFIG_SADCRES_12BIT_16S_8510US (0x0060) -#define INA219_CONFIG_SADCRES_12BIT_32S_17MS (0x0068) -#define INA219_CONFIG_SADCRES_12BIT_64S_34MS (0x0070) -#define INA219_CONFIG_SADCRES_12BIT_128S_69MS (0x0078) - -#define INA219_CONFIG_MODE_MASK (0x0007) -#define INA219_CONFIG_MODE_POWERDOWN (0x0000) -#define INA219_CONFIG_MODE_SVOLT_TRIGGERED (0x0001) -#define INA219_CONFIG_MODE_BVOLT_TRIGGERED (0x0002) -#define INA219_CONFIG_MODE_SANDBVOLT_TRIGGERED (0x0003) -#define INA219_CONFIG_MODE_ADCOFF (0x0004) -#define INA219_CONFIG_MODE_SVOLT_CONTINUOUS (0x0005) -#define INA219_CONFIG_MODE_BVOLT_CONTINUOUS (0x0006) -#define INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS (0x0007) - -#define INA219_REG_SHUNTVOLTAGE (0x01) -#define INA219_REG_BUSVOLTAGE (0x02) -#define INA219_REG_POWER (0x03) -#define INA219_REG_CURRENT (0x04) -#define INA219_REG_CALIBRATION (0x05) - -uint8_t ina219_type[4] = {0,0,0,0}; -uint8_t ina219_addresses[] = { INA219_ADDRESS1, INA219_ADDRESS2, INA219_ADDRESS3, INA219_ADDRESS4 }; - -uint32_t ina219_cal_value = 0; - -uint32_t ina219_current_divider_ma = 0; - -uint8_t ina219_valid[4] = {0,0,0,0}; -float ina219_voltage[4] = {0,0,0,0}; -float ina219_current[4] = {0,0,0,0}; -char ina219_types[] = "INA219"; -uint8_t ina219_count = 0; - -bool Ina219SetCalibration(uint8_t mode, uint16_t addr) -{ - uint16_t config = 0; - - switch (mode &3) { - case 0: - case 3: - ina219_cal_value = 4096; - ina219_current_divider_ma = 10; - config = INA219_CONFIG_BVOLTAGERANGE_32V | INA219_CONFIG_GAIN_8_320MV | INA219_CONFIG_BADCRES_12BIT | INA219_CONFIG_SADCRES_12BIT_1S_532US | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; - break; - case 1: - ina219_cal_value = 10240; - ina219_current_divider_ma = 25; - config |= INA219_CONFIG_BVOLTAGERANGE_32V | INA219_CONFIG_GAIN_8_320MV | INA219_CONFIG_BADCRES_12BIT | INA219_CONFIG_SADCRES_12BIT_1S_532US | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; - break; - case 2: - ina219_cal_value = 8192; - ina219_current_divider_ma = 20; - config |= INA219_CONFIG_BVOLTAGERANGE_16V | INA219_CONFIG_GAIN_1_40MV | INA219_CONFIG_BADCRES_12BIT | INA219_CONFIG_SADCRES_12BIT_1S_532US | INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS; - break; - } - - bool success = I2cWrite16(addr, INA219_REG_CALIBRATION, ina219_cal_value); - if (success) { - - I2cWrite16(addr, INA219_REG_CONFIG, config); - } - return success; -} - -float Ina219GetShuntVoltage_mV(uint16_t addr) -{ - - int16_t value = I2cReadS16(addr, INA219_REG_SHUNTVOLTAGE); - - return value * 0.01; -} - -float Ina219GetBusVoltage_V(uint16_t addr) -{ - - - int16_t value = (int16_t)(((uint16_t)I2cReadS16(addr, INA219_REG_BUSVOLTAGE) >> 3) * 4); - - return value * 0.001; -} - -float Ina219GetCurrent_mA(uint16_t addr) -{ - - - - I2cWrite16(addr, INA219_REG_CALIBRATION, ina219_cal_value); - - - float value = I2cReadS16(addr, INA219_REG_CURRENT); - value /= ina219_current_divider_ma; - - return value; -} - -bool Ina219Read(void) -{ - for (int i=0; i= 0) && (XdrvMailbox.payload <= 2)) { - Settings.ina219_mode = XdrvMailbox.payload; - restart_flag = 2; - } - Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_13, Settings.ina219_mode); - - return true; -} - - - -void Ina219Detect(void) -{ - for (uint32_t i = 0; i < sizeof(ina219_type); i++) { - uint16_t addr = ina219_addresses[i]; - if (I2cActive(addr)) { continue; } - if (Ina219SetCalibration(Settings.ina219_mode, addr)) { - I2cSetActiveFound(addr, ina219_types); - ina219_type[i] = 1; - ina219_count++; - } - } -} - -void Ina219EverySecond(void) -{ - - Ina219Read(); -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_INA219_DATA[] PROGMEM = - "{s}%s " D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" - "{s}%s " D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" - "{s}%s " D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; -#endif - -void Ina219Show(bool json) -{ - int num_found=0; - for (int i=0; i1) - snprintf_P(name, sizeof(name), PSTR("%s%c%d"), ina219_types, IndexSeparator(), sensor_num); - else - snprintf_P(name, sizeof(name), PSTR("%s"), ina219_types); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"Id\":%02x,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), - name, ina219_addresses[i], voltage, current, power); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_VOLTAGE, voltage); - DomoticzSensor(DZ_CURRENT, current); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_INA219_DATA, name, voltage, name, current, name, power); -#endif - } - } -} - - - - - -bool Xsns13(uint8_t function) -{ - if (!I2cEnabled(XI2C_14)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Ina219Detect(); - } - else if (ina219_count) { - switch (function) { - case FUNC_COMMAND_SENSOR: - if (XSNS_13 == XdrvMailbox.index) { - result = Ina219CommandSensor(); - } - break; - case FUNC_EVERY_SECOND: - Ina219EverySecond(); - break; - case FUNC_JSON_APPEND: - Ina219Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Ina219Show(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_14_sht3x.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_14_sht3x.ino" -#ifdef USE_I2C -#ifdef USE_SHT3X - - - - - - -#define XSNS_14 14 -#define XI2C_15 15 - -#define SHT3X_ADDR_GND 0x44 -#define SHT3X_ADDR_VDD 0x45 -#define SHTC3_ADDR 0x70 - -#define SHT3X_MAX_SENSORS 3 - -const char kShtTypes[] PROGMEM = "SHT3X|SHT3X|SHTC3"; -uint8_t sht3x_addresses[] = { SHT3X_ADDR_GND, SHT3X_ADDR_VDD, SHTC3_ADDR }; - -uint8_t sht3x_count = 0; -struct SHT3XSTRUCT { - uint8_t address; - char types[6]; -} sht3x_sensors[SHT3X_MAX_SENSORS]; - -bool Sht3xRead(float &t, float &h, uint8_t sht3x_address) -{ - unsigned int data[6]; - - t = NAN; - h = NAN; - - Wire.beginTransmission(sht3x_address); - if (SHTC3_ADDR == sht3x_address) { - Wire.write(0x35); - Wire.write(0x17); - Wire.endTransmission(); - Wire.beginTransmission(sht3x_address); - Wire.write(0x78); - Wire.write(0x66); - } else { - Wire.write(0x2C); - Wire.write(0x06); - } - if (Wire.endTransmission() != 0) { - return false; - } - delay(30); - Wire.requestFrom(sht3x_address, (uint8_t)6); - for (uint32_t i = 0; i < 6; i++) { - data[i] = Wire.read(); - }; - t = ConvertTemp((float)((((data[0] << 8) | data[1]) * 175) / 65535.0) - 45); - h = ConvertHumidity((float)((((data[3] << 8) | data[4]) * 100) / 65535.0)); - return (!isnan(t) && !isnan(h) && (h != 0)); -} - - - -void Sht3xDetect(void) -{ - for (uint32_t i = 0; i < SHT3X_MAX_SENSORS; i++) { - if (I2cActive(sht3x_addresses[i])) { continue; } - float t; - float h; - if (Sht3xRead(t, h, sht3x_addresses[i])) { - sht3x_sensors[sht3x_count].address = sht3x_addresses[i]; - GetTextIndexed(sht3x_sensors[sht3x_count].types, sizeof(sht3x_sensors[sht3x_count].types), i, kShtTypes); - I2cSetActiveFound(sht3x_sensors[sht3x_count].address, sht3x_sensors[sht3x_count].types); - sht3x_count++; - } - } -} - -void Sht3xShow(bool json) -{ - for (uint32_t i = 0; i < sht3x_count; i++) { - float t; - float h; - if (Sht3xRead(t, h, sht3x_sensors[i].address)) { - char temperature[33]; - dtostrfd(t, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(h, Settings.flag2.humidity_resolution, humidity); - char types[11]; - snprintf_P(types, sizeof(types), PSTR("%s%c0x%02X"), sht3x_sensors[i].types, IndexSeparator(), sht3x_sensors[i].address); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMPHUM, types, temperature, humidity); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && (0 == i)) { - DomoticzTempHumSensor(temperature, humidity); - } -#endif - -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, t); - KnxSensor(KNX_HUMIDITY, h); - } -#endif - -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, types, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, types, humidity); -#endif - } - } - } -} - - - - - -bool Xsns14(uint8_t function) -{ - if (!I2cEnabled(XI2C_15)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Sht3xDetect(); - } - else if (sht3x_count) { - switch (function) { - case FUNC_JSON_APPEND: - Sht3xShow(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Sht3xShow(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_15_mhz19.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_15_mhz19.ino" -#ifdef USE_MHZ19 -# 33 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_15_mhz19.ino" -#define XSNS_15 15 - -enum MhzFilterOptions {MHZ19_FILTER_OFF, MHZ19_FILTER_OFF_ALLSAMPLES, MHZ19_FILTER_FAST, MHZ19_FILTER_MEDIUM, MHZ19_FILTER_SLOW}; - -#define MHZ19_FILTER_OPTION MHZ19_FILTER_FAST -# 58 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_15_mhz19.ino" -#include - -#ifndef CO2_LOW -#define CO2_LOW 800 -#endif -#ifndef CO2_HIGH -#define CO2_HIGH 1200 -#endif - -#define MHZ19_READ_TIMEOUT 400 -#define MHZ19_RETRY_COUNT 8 - -TasmotaSerial *MhzSerial; - -const char kMhzModels[] PROGMEM = "|B"; - -const char ABC_ENABLED[] = "ABC is Enabled"; -const char ABC_DISABLED[] = "ABC is Disabled"; - -enum MhzCommands { MHZ_CMND_READPPM, MHZ_CMND_ABCENABLE, MHZ_CMND_ABCDISABLE, MHZ_CMND_ZEROPOINT, MHZ_CMND_RESET, MHZ_CMND_RANGE_1000, MHZ_CMND_RANGE_2000, MHZ_CMND_RANGE_3000, MHZ_CMND_RANGE_5000 }; -const uint8_t kMhzCommands[][4] PROGMEM = { - - {0x86,0x00,0x00,0x00}, - {0x79,0xA0,0x00,0x00}, - {0x79,0x00,0x00,0x00}, - {0x87,0x00,0x00,0x00}, - {0x8D,0x00,0x00,0x00}, - {0x99,0x00,0x03,0xE8}, - {0x99,0x00,0x07,0xD0}, - {0x99,0x00,0x0B,0xB8}, - {0x99,0x00,0x13,0x88}}; - -uint8_t mhz_type = 1; -uint16_t mhz_last_ppm = 0; -uint8_t mhz_filter = MHZ19_FILTER_OPTION; -bool mhz_abc_must_apply = false; - -float mhz_temperature = 0; -uint8_t mhz_retry = MHZ19_RETRY_COUNT; -uint8_t mhz_received = 0; -uint8_t mhz_state = 0; - - - -uint8_t MhzCalculateChecksum(uint8_t *array) -{ - uint8_t checksum = 0; - for (uint32_t i = 1; i < 8; i++) { - checksum += array[i]; - } - checksum = 255 - checksum; - return (checksum +1); -} - -size_t MhzSendCmd(uint8_t command_id) -{ - uint8_t mhz_send[9] = { 0 }; - - mhz_send[0] = 0xFF; - mhz_send[1] = 0x01; - memcpy_P(&mhz_send[2], kMhzCommands[command_id], sizeof(uint16_t)); - - - - - memcpy_P(&mhz_send[6], kMhzCommands[command_id] + sizeof(uint16_t), sizeof(uint16_t)); - mhz_send[8] = MhzCalculateChecksum(mhz_send); - - - - return MhzSerial->write(mhz_send, sizeof(mhz_send)); -} - - - -bool MhzCheckAndApplyFilter(uint16_t ppm, uint8_t s) -{ - if (1 == s) { - return false; - } - if (mhz_last_ppm < 400 || mhz_last_ppm > 5000) { - - - mhz_last_ppm = ppm; - return true; - } - int32_t difference = ppm - mhz_last_ppm; - if (s > 0 && s < 64 && mhz_filter != MHZ19_FILTER_OFF) { -# 154 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_15_mhz19.ino" - difference *= s; - difference /= 64; - } - if (MHZ19_FILTER_OFF == mhz_filter) { - if (s != 0 && s != 64) { - return false; - } - } else { - difference >>= (mhz_filter -1); - } - mhz_last_ppm = static_cast(mhz_last_ppm + difference); - return true; -} - -void MhzEverySecond(void) -{ - mhz_state++; - if (8 == mhz_state) { - mhz_state = 0; - - if (mhz_retry) { - mhz_retry--; - if (!mhz_retry) { - mhz_last_ppm = 0; - mhz_temperature = 0; - } - } - - MhzSerial->flush(); - MhzSendCmd(MHZ_CMND_READPPM); - mhz_received = 0; - } - - if ((mhz_state > 2) && !mhz_received) { - uint8_t mhz_response[9]; - - unsigned long start = millis(); - uint8_t counter = 0; - while (((millis() - start) < MHZ19_READ_TIMEOUT) && (counter < 9)) { - if (MhzSerial->available() > 0) { - mhz_response[counter++] = MhzSerial->read(); - } else { - delay(5); - } - } - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, mhz_response, counter); - - if (counter < 9) { - - return; - } - - uint8_t crc = MhzCalculateChecksum(mhz_response); - if (mhz_response[8] != crc) { - - return; - } - if (0xFF != mhz_response[0] || 0x86 != mhz_response[1]) { - - return; - } - - mhz_received = 1; - - uint16_t u = (mhz_response[6] << 8) | mhz_response[7]; - if (15000 == u) { - if (Settings.SensorBits1.mhz19b_abc_disable) { - - - mhz_abc_must_apply = true; - } - } else { - uint16_t ppm = (mhz_response[2] << 8) | mhz_response[3]; - mhz_temperature = ConvertTemp((float)mhz_response[4] - 40); - uint8_t s = mhz_response[5]; - mhz_type = (s) ? 1 : 2; - if (MhzCheckAndApplyFilter(ppm, s)) { - mhz_retry = MHZ19_RETRY_COUNT; - LightSetSignal(CO2_LOW, CO2_HIGH, mhz_last_ppm); - - if (0 == s || 64 == s) { - if (mhz_abc_must_apply) { - mhz_abc_must_apply = false; - if (!Settings.SensorBits1.mhz19b_abc_disable) { - MhzSendCmd(MHZ_CMND_ABCENABLE); - } else { - MhzSendCmd(MHZ_CMND_ABCDISABLE); - } - } - } - - } - } - - } -} -# 266 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_15_mhz19.ino" -#define D_JSON_RANGE_1000 "1000 ppm range" -#define D_JSON_RANGE_2000 "2000 ppm range" -#define D_JSON_RANGE_3000 "3000 ppm range" -#define D_JSON_RANGE_5000 "5000 ppm range" - -bool MhzCommandSensor(void) -{ - bool serviced = true; - - switch (XdrvMailbox.payload) { - case 0: - Settings.SensorBits1.mhz19b_abc_disable = true; - MhzSendCmd(MHZ_CMND_ABCDISABLE); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_DISABLED); - break; - case 1: - Settings.SensorBits1.mhz19b_abc_disable = false; - MhzSendCmd(MHZ_CMND_ABCENABLE); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); - break; - case 2: - MhzSendCmd(MHZ_CMND_ZEROPOINT); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_ZERO_POINT_CALIBRATION); - break; - case 9: - MhzSendCmd(MHZ_CMND_RESET); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RESET); - break; - case 1000: - MhzSendCmd(MHZ_CMND_RANGE_1000); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_1000); - break; - case 2000: - MhzSendCmd(MHZ_CMND_RANGE_2000); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_2000); - break; - case 3000: - MhzSendCmd(MHZ_CMND_RANGE_3000); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_3000); - break; - case 5000: - MhzSendCmd(MHZ_CMND_RANGE_5000); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, D_JSON_RANGE_5000); - break; - default: - if (!Settings.SensorBits1.mhz19b_abc_disable) { - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_ENABLED); - } else { - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_15, ABC_DISABLED); - } - } - - return serviced; -} - - - -void MhzInit(void) -{ - mhz_type = 0; - if ((pin[GPIO_MHZ_RXD] < 99) && (pin[GPIO_MHZ_TXD] < 99)) { - MhzSerial = new TasmotaSerial(pin[GPIO_MHZ_RXD], pin[GPIO_MHZ_TXD], 1); - if (MhzSerial->begin(9600)) { - if (MhzSerial->hardwareSerial()) { ClaimSerial(); } - mhz_type = 1; - } - - } -} - -void MhzShow(bool json) -{ - char types[7] = "MHZ19B"; - char temperature[33]; - dtostrfd(mhz_temperature, Settings.flag2.temperature_resolution, temperature); - char model[3]; - GetTextIndexed(model, sizeof(model), mhz_type -1, kMhzModels); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_MODEL "\":\"%s\",\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s}"), types, model, mhz_last_ppm, temperature); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_AIRQUALITY, mhz_last_ppm); - DomoticzSensor(DZ_TEMP, temperature); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_CO2, types, mhz_last_ppm); - WSContentSend_PD(HTTP_SNS_TEMP, types, temperature, TempUnit()); -#endif - } -} - - - - - -bool Xsns15(uint8_t function) -{ - bool result = false; - - if (mhz_type) { - switch (function) { - case FUNC_INIT: - MhzInit(); - break; - case FUNC_EVERY_SECOND: - MhzEverySecond(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_15 == XdrvMailbox.index) { - result = MhzCommandSensor(); - } - break; - case FUNC_JSON_APPEND: - MhzShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MhzShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_16_tsl2561.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_16_tsl2561.ino" -#ifdef USE_I2C -#ifdef USE_TSL2561 -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_16_tsl2561.ino" -#define XSNS_16 16 -#define XI2C_16 16 - -#include - -Tsl2561 Tsl(Wire); - -uint8_t tsl2561_type = 0; -uint8_t tsl2561_valid = 0; -uint32_t tsl2561_milliLux = 0; -char tsl2561_types[] = "TSL2561"; - -bool Tsl2561Read(void) -{ - if (tsl2561_valid) { tsl2561_valid--; } - - uint8_t id; - bool gain; - Tsl2561::exposure_t exposure; - uint16_t scaledFull, scaledIr; - uint32_t full, ir; - - if (Tsl.on()) { - if (Tsl.id(id) - && Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr) - && Tsl2561Util::normalizedLuminosity(gain, exposure, full = scaledFull, ir = scaledIr) - && Tsl2561Util::milliLux(full, ir, tsl2561_milliLux, Tsl2561::packageCS(id))) { - } else{ - tsl2561_milliLux = 0; - } - } - tsl2561_valid = SENSOR_MAX_MISS; - return true; -} - -void Tsl2561Detect(void) -{ - if (I2cSetDevice(0x29) || I2cSetDevice(0x39) || I2cSetDevice(0x49)) { - uint8_t id; - Tsl.begin(); - if (!Tsl.id(id)) return; - if (Tsl.on()) { - tsl2561_type = 1; - I2cSetActiveFound(Tsl.address(), tsl2561_types); - } - } -} - -void Tsl2561EverySecond(void) -{ - if (!(uptime %2)) { - - if (!Tsl2561Read()) { - AddLogMissed(tsl2561_types, tsl2561_valid); - } - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_TSL2561[] PROGMEM = - "{s}TSL2561 " D_ILLUMINANCE "{m}%u.%03u " D_UNIT_LUX "{e}"; -#endif - -void Tsl2561Show(bool json) -{ - if (tsl2561_valid) { - if (json) { - ResponseAppend_P(PSTR(",\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u}"), - tsl2561_milliLux / 1000, tsl2561_milliLux % 1000); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, (tsl2561_milliLux + 500) / 1000); } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TSL2561, tsl2561_milliLux / 1000, tsl2561_milliLux % 1000); -#endif - } - } -} - - - - - -bool Xsns16(uint8_t function) -{ - if (!I2cEnabled(XI2C_16)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Tsl2561Detect(); - } - else if (tsl2561_type) { - switch (function) { - case FUNC_EVERY_SECOND: - Tsl2561EverySecond(); - break; - case FUNC_JSON_APPEND: - Tsl2561Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Tsl2561Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_17_senseair.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_17_senseair.ino" -#ifdef USE_SENSEAIR -# 29 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_17_senseair.ino" -#define XSNS_17 17 - -#define SENSEAIR_MODBUS_SPEED 9600 -#define SENSEAIR_DEVICE_ADDRESS 0xFE -#define SENSEAIR_READ_REGISTER 0x04 - -#ifndef CO2_LOW -#define CO2_LOW 800 -#endif -#ifndef CO2_HIGH -#define CO2_HIGH 1200 -#endif - -#include -TasmotaModbus *SenseairModbus; - -const char kSenseairTypes[] PROGMEM = "Kx0|S8"; - -uint8_t senseair_type = 1; -char senseair_types[7]; - -uint16_t senseair_co2 = 0; -float senseair_temperature = 0; -float senseair_humidity = 0; - - - -const uint8_t start_addresses[] { 0x1A, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x0A }; - -uint8_t senseair_read_state = 0; -uint8_t senseair_send_retry = 0; - -void Senseair250ms(void) -{ - - - - - uint16_t value = 0; - bool data_ready = SenseairModbus->ReceiveReady(); - - if (data_ready) { - uint8_t error = SenseairModbus->Receive16BitRegister(&value); - if (error) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir response error %d"), error); - } else { - switch(senseair_read_state) { - case 0: - senseair_type = 2; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir type id low %04X"), value); - break; - case 1: - if (value) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir error %04X"), value); - } - break; - case 2: - senseair_co2 = value; - LightSetSignal(CO2_LOW, CO2_HIGH, senseair_co2); - break; - case 3: - senseair_temperature = ConvertTemp((float)value / 100); - break; - case 4: - senseair_humidity = ConvertHumidity((float)value / 100); - break; - case 5: - { - bool relay_state = value >> 8 & 1; - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir relay state %d"), relay_state); - break; - } - case 6: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "SenseAir temp adjustment %d"), value); - break; - } - } - senseair_read_state++; - if (2 == senseair_type) { - if (3 == senseair_read_state) { - senseair_read_state = 1; - } - } else { - if (sizeof(start_addresses) == senseair_read_state) { - senseair_read_state = 1; - } - } - } - - if (0 == senseair_send_retry || data_ready) { - senseair_send_retry = 5; - SenseairModbus->Send(SENSEAIR_DEVICE_ADDRESS, SENSEAIR_READ_REGISTER, (uint16_t)start_addresses[senseair_read_state], 1); - } else { - senseair_send_retry--; - } - - -} - - - -void SenseairInit(void) -{ - senseair_type = 0; - if ((pin[GPIO_SAIR_RX] < 99) && (pin[GPIO_SAIR_TX] < 99)) { - SenseairModbus = new TasmotaModbus(pin[GPIO_SAIR_RX], pin[GPIO_SAIR_TX]); - uint8_t result = SenseairModbus->Begin(SENSEAIR_MODBUS_SPEED); - if (result) { - if (2 == result) { ClaimSerial(); } - senseair_type = 1; - } - } -} - -void SenseairShow(bool json) -{ - char temperature[33]; - dtostrfd(senseair_temperature, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(senseair_humidity, Settings.flag2.temperature_resolution, humidity); - GetTextIndexed(senseair_types, sizeof(senseair_types), senseair_type -1, kSenseairTypes); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_CO2 "\":%d"), senseair_types, senseair_co2); - if (senseair_type != 2) { - ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s"), temperature, humidity); - } - ResponseJsonEnd(); -#ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, senseair_co2); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_CO2, senseair_types, senseair_co2); - if (senseair_type != 2) { - WSContentSend_PD(HTTP_SNS_TEMP, senseair_types, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, senseair_types, humidity); - } -#endif - } -} - - - - - -bool Xsns17(uint8_t function) -{ - bool result = false; - - if (senseair_type) { - switch (function) { - case FUNC_INIT: - SenseairInit(); - break; - case FUNC_EVERY_250_MSECOND: - Senseair250ms(); - break; - case FUNC_JSON_APPEND: - SenseairShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - SenseairShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_18_pms5003.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_18_pms5003.ino" -#ifdef USE_PMS5003 -# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_18_pms5003.ino" -#define XSNS_18 18 - -#include - -TasmotaSerial *PmsSerial; - -uint8_t pms_type = 1; -uint8_t pms_valid = 0; - -struct pmsX003data { - uint16_t framelen; - uint16_t pm10_standard, pm25_standard, pm100_standard; - uint16_t pm10_env, pm25_env, pm100_env; -#ifdef PMS_MODEL_PMS3003 - uint16_t reserved1, reserved2, reserved3; -#else - uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um; - uint16_t unused; -#endif - uint16_t checksum; -} pms_data; - - - -bool PmsReadData(void) -{ - if (! PmsSerial->available()) { - return false; - } - while ((PmsSerial->peek() != 0x42) && PmsSerial->available()) { - PmsSerial->read(); - } -#ifdef PMS_MODEL_PMS3003 - if (PmsSerial->available() < 22) { -#else - if (PmsSerial->available() < 32) { -#endif - return false; - } - -#ifdef PMS_MODEL_PMS3003 - uint8_t buffer[22]; - PmsSerial->readBytes(buffer, 22); -#else - uint8_t buffer[32]; - PmsSerial->readBytes(buffer, 32); -#endif - uint16_t sum = 0; - PmsSerial->flush(); - -#ifdef PMS_MODEL_PMS3003 - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 22); -#else - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, buffer, 32); -#endif - - -#ifdef PMS_MODEL_PMS3003 - for (uint32_t i = 0; i < 20; i++) { -#else - for (uint32_t i = 0; i < 30; i++) { -#endif - sum += buffer[i]; - } - -#ifdef PMS_MODEL_PMS3003 - uint16_t buffer_u16[10]; - for (uint32_t i = 0; i < 10; i++) { -#else - uint16_t buffer_u16[15]; - for (uint32_t i = 0; i < 15; i++) { -#endif - buffer_u16[i] = buffer[2 + i*2 + 1]; - buffer_u16[i] += (buffer[2 + i*2] << 8); - } -#ifdef PMS_MODEL_PMS3003 - if (sum != buffer_u16[9]) { -#else - if (sum != buffer_u16[14]) { -#endif - AddLog_P(LOG_LEVEL_DEBUG, PSTR("PMS: " D_CHECKSUM_FAILURE)); - return false; - } - -#ifdef PMS_MODEL_PMS3003 - memcpy((void *)&pms_data, (void *)buffer_u16, 20); -#else - memcpy((void *)&pms_data, (void *)buffer_u16, 30); -#endif - pms_valid = 10; - - return true; -} - - - -void PmsSecond(void) -{ - if (PmsReadData()) { - pms_valid = 10; - } else { - if (pms_valid) { - pms_valid--; - } - } -} - - - -void PmsInit(void) -{ - pms_type = 0; - if (pin[GPIO_PMS5003] < 99) { - PmsSerial = new TasmotaSerial(pin[GPIO_PMS5003], -1, 1); - if (PmsSerial->begin(9600)) { - if (PmsSerial->hardwareSerial()) { ClaimSerial(); } - pms_type = 1; - } - } -} - -#ifdef USE_WEBSERVER -#ifdef PMS_MODEL_PMS3003 -const char HTTP_PMS3003_SNS[] PROGMEM = - - - - "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}PMS3003 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; -#else -const char HTTP_PMS5003_SNS[] PROGMEM = - - - - "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}PMS5003 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}PMS5003 " D_PARTICALS_BEYOND " 0.3 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}PMS5003 " D_PARTICALS_BEYOND " 0.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}PMS5003 " D_PARTICALS_BEYOND " 1 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}PMS5003 " D_PARTICALS_BEYOND " 2.5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}PMS5003 " D_PARTICALS_BEYOND " 5 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}" - "{s}PMS5003 " D_PARTICALS_BEYOND " 10 " D_UNIT_MICROMETER "{m}%d " D_UNIT_PARTS_PER_DECILITER "{e}"; -#endif -#endif - -void PmsShow(bool json) -{ - if (pms_valid) { - if (json) { -#ifdef PMS_MODEL_PMS3003 - ResponseAppend_P(PSTR(",\"PMS3003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d}"), - pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, - pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env); -#else - ResponseAppend_P(PSTR(",\"PMS5003\":{\"CF1\":%d,\"CF2.5\":%d,\"CF10\":%d,\"PM1\":%d,\"PM2.5\":%d,\"PM10\":%d,\"PB0.3\":%d,\"PB0.5\":%d,\"PB1\":%d,\"PB2.5\":%d,\"PB5\":%d,\"PB10\":%d}"), - pms_data.pm10_standard, pms_data.pm25_standard, pms_data.pm100_standard, - pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, - pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); -#endif -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_COUNT, pms_data.pm10_env); - DomoticzSensor(DZ_VOLTAGE, pms_data.pm25_env); - DomoticzSensor(DZ_CURRENT, pms_data.pm100_env); - } -#endif -#ifdef USE_WEBSERVER - } else { - -#ifdef PMS_MODEL_PMS3003 - WSContentSend_PD(HTTP_PMS3003_SNS, - - pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env); -#else - WSContentSend_PD(HTTP_PMS5003_SNS, - - pms_data.pm10_env, pms_data.pm25_env, pms_data.pm100_env, - pms_data.particles_03um, pms_data.particles_05um, pms_data.particles_10um, pms_data.particles_25um, pms_data.particles_50um, pms_data.particles_100um); -#endif -#endif - } - } -} - - - - - -bool Xsns18(uint8_t function) -{ - bool result = false; - - if (pms_type) { - switch (function) { - case FUNC_INIT: - PmsInit(); - break; - case FUNC_EVERY_SECOND: - PmsSecond(); - break; - case FUNC_JSON_APPEND: - PmsShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - PmsShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_19_mgs.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_19_mgs.ino" -#ifdef USE_I2C -#ifdef USE_MGS - - - - - - - -#define XSNS_19 19 -#define XI2C_17 17 - -#ifndef MGS_SENSOR_ADDR -#define MGS_SENSOR_ADDR 0x04 -#endif - -#include "MutichannelGasSensor.h" - -bool mgs_detected = false; - -void MGSInit(void) { - gas.begin(MGS_SENSOR_ADDR); -} - -void MGSPrepare(void) -{ - if (I2cActive(MGS_SENSOR_ADDR)) { return; } - - gas.begin(MGS_SENSOR_ADDR); - if (!gas.isError()) { - I2cSetActiveFound(MGS_SENSOR_ADDR, "MultiGas"); - mgs_detected = true; - } -} - -char* measure_gas(int gas_type, char* buffer) -{ - float f = gas.calcGas(gas_type); - dtostrfd(f, 2, buffer); - return buffer; -} - -#ifdef USE_WEBSERVER -const char HTTP_MGS_GAS[] PROGMEM = "{s}MGS %s{m}%s " D_UNIT_PARTS_PER_MILLION "{e}"; -#endif - -void MGSShow(bool json) -{ - char buffer[33]; - if (json) { - ResponseAppend_P(PSTR(",\"MGS\":{\"NH3\":%s"), measure_gas(NH3, buffer)); - ResponseAppend_P(PSTR(",\"CO\":%s"), measure_gas(CO, buffer)); - ResponseAppend_P(PSTR(",\"NO2\":%s"), measure_gas(NO2, buffer)); - ResponseAppend_P(PSTR(",\"C3H8\":%s"), measure_gas(C3H8, buffer)); - ResponseAppend_P(PSTR(",\"C4H10\":%s"), measure_gas(C4H10, buffer)); - ResponseAppend_P(PSTR(",\"CH4\":%s"), measure_gas(GAS_CH4, buffer)); - ResponseAppend_P(PSTR(",\"H2\":%s"), measure_gas(H2, buffer)); - ResponseAppend_P(PSTR(",\"C2H5OH\":%s}"), measure_gas(C2H5OH, buffer)); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_MGS_GAS, "NH3", measure_gas(NH3, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "CO", measure_gas(CO, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "NO2", measure_gas(NO2, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "C3H8", measure_gas(C3H8, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "C4H10", measure_gas(C4H10, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "CH4", measure_gas(GAS_CH4, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "H2", measure_gas(H2, buffer)); - WSContentSend_PD(HTTP_MGS_GAS, "C2H5OH", measure_gas(C2H5OH, buffer)); -#endif - } -} - - - - - -bool Xsns19(uint8_t function) -{ - if (!I2cEnabled(XI2C_17)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - MGSPrepare(); - } - else if (mgs_detected) { - switch (function) { - case FUNC_JSON_APPEND: - MGSShow(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MGSShow(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_20_novasds.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_20_novasds.ino" -#ifdef USE_NOVA_SDS -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_20_novasds.ino" -#define XSNS_20 20 - -#include - -#ifndef STARTING_OFFSET -#define STARTING_OFFSET 30 -#endif -#if STARTING_OFFSET < 10 -#error "Please set STARTING_OFFSET >= 10" -#endif -#ifndef NOVA_SDS_RECDATA_TIMEOUT -#define NOVA_SDS_RECDATA_TIMEOUT 150 -#endif -#ifndef NOVA_SDS_DEVICE_ID -#define NOVA_SDS_DEVICE_ID 0xFFFF -#endif - -TasmotaSerial *NovaSdsSerial; - -uint8_t novasds_type = 1; -uint8_t novasds_valid = 0; -uint8_t cont_mode = 1; - -struct sds011data { - uint16_t pm100; - uint16_t pm25; -} novasds_data; -uint16_t pm100_sum; -uint16_t pm25_sum; - - -#define NOVA_SDS_REPORTING_MODE 2 -#define NOVA_SDS_QUERY_DATA 4 -#define NOVA_SDS_SET_DEVICE_ID 5 -#define NOVA_SDS_SLEEP_AND_WORK 6 -#define NOVA_SDS_WORKING_PERIOD 8 -#define NOVA_SDS_CHECK_FIRMWARE_VER 7 - #define NOVA_SDS_QUERY_MODE 0 - #define NOVA_SDS_SET_MODE 1 - #define NOVA_SDS_REPORT_ACTIVE 0 - #define NOVA_SDS_REPORT_QUERY 1 - #define NOVA_SDS_SLEEP 0 - #define NOVA_SDS_WORK 1 - - -bool NovaSdsCommand(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint16_t sensorid, uint8_t *buffer) -{ - uint8_t novasds_cmnd[19] = {0xAA, 0xB4, byte1, byte2, byte3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (uint8_t)(sensorid & 0xFF), (uint8_t)((sensorid>>8) & 0xFF), 0x00, 0xAB}; - - - for (uint32_t i = 2; i < 17; i++) { - novasds_cmnd[17] += novasds_cmnd[i]; - } - - - - - - NovaSdsSerial->write(novasds_cmnd, sizeof(novasds_cmnd)); - NovaSdsSerial->flush(); - - - unsigned long cmndtime = millis(); - while ( (TimePassedSince(cmndtime) < NOVA_SDS_RECDATA_TIMEOUT) && ( ! NovaSdsSerial->available() ) ); - if ( ! NovaSdsSerial->available() ) { - - return false; - } - uint8_t recbuf[10]; - memset(recbuf, 0, sizeof(recbuf)); - - while ( (TimePassedSince(cmndtime) < NOVA_SDS_RECDATA_TIMEOUT) && ( NovaSdsSerial->available() > 0) && (0xAA != (recbuf[0] = NovaSdsSerial->read())) ); - if ( 0xAA != recbuf[0] ) { - - return false; - } - - - NovaSdsSerial->readBytes(&recbuf[1], 9); - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, recbuf, sizeof(recbuf)); - - if ( nullptr != buffer ) { - - memcpy(buffer, recbuf, sizeof(recbuf)); - } - - - if ((0xAB != recbuf[9] ) || (recbuf[8] != ((recbuf[2] + recbuf[3] + recbuf[4] + recbuf[5] + recbuf[6] + recbuf[7]) & 0xFF))) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("SDS: " D_CHECKSUM_FAILURE)); - return false; - } - - return true; -} - -void NovaSdsSetWorkPeriod(void) -{ - - NovaSdsCommand(NOVA_SDS_WORKING_PERIOD, NOVA_SDS_SET_MODE, 0, NOVA_SDS_DEVICE_ID, nullptr); - - NovaSdsCommand(NOVA_SDS_REPORTING_MODE, NOVA_SDS_SET_MODE, NOVA_SDS_REPORT_QUERY, NOVA_SDS_DEVICE_ID, nullptr); -} - -bool NovaSdsReadData(void) -{ - uint8_t d[10]; - if ( ! NovaSdsCommand(NOVA_SDS_QUERY_DATA, 0, 0, NOVA_SDS_DEVICE_ID, d) ) { - return false; - } - novasds_data.pm25 = (d[2] + 256 * d[3]); - novasds_data.pm100 = (d[4] + 256 * d[5]); - - return true; -} - - - -void NovaSdsSecond(void) -{ - if (!novasds_valid) - { - NovaSdsSetWorkPeriod(); - novasds_valid=1; - } - if((Settings.tele_period - Settings.novasds_startingoffset <= 0)) - { - if(!cont_mode) - { - cont_mode = 1; - NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_WORK, NOVA_SDS_DEVICE_ID, nullptr); - } - } - else - cont_mode = 0; - - if(tele_period == Settings.tele_period - Settings.novasds_startingoffset && !cont_mode) - { - NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_WORK, NOVA_SDS_DEVICE_ID, nullptr); - } - if(tele_period >= Settings.tele_period-5 && tele_period <= Settings.tele_period-2) - { - if(!(NovaSdsReadData())) novasds_valid=0; - pm100_sum += novasds_data.pm100; - pm25_sum += novasds_data.pm25; - } - if(tele_period == Settings.tele_period-1) - { - novasds_data.pm100 = pm100_sum >> 2; - novasds_data.pm25 = pm25_sum >> 2; - if(!cont_mode) - NovaSdsCommand(NOVA_SDS_SLEEP_AND_WORK, NOVA_SDS_SET_MODE, NOVA_SDS_SLEEP, NOVA_SDS_DEVICE_ID, nullptr); - pm100_sum = pm25_sum = 0; - } -} - - - - - - - -bool NovaSdsCommandSensor(void) -{ - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 256)) { - if( XdrvMailbox.payload < 10 ) Settings.novasds_startingoffset = 10; - else Settings.novasds_startingoffset = XdrvMailbox.payload; - } - Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_20, Settings.novasds_startingoffset); - - return true; -} - -void NovaSdsInit(void) -{ - novasds_type = 0; - if (pin[GPIO_SDS0X1_RX] < 99 && pin[GPIO_SDS0X1_TX] < 99) { - NovaSdsSerial = new TasmotaSerial(pin[GPIO_SDS0X1_RX], pin[GPIO_SDS0X1_TX], 1); - if (NovaSdsSerial->begin(9600)) { - if (NovaSdsSerial->hardwareSerial()) { - ClaimSerial(); - } - novasds_type = 1; - NovaSdsSetWorkPeriod(); - } - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SDS0X1_SNS[] PROGMEM = - "{s}SDS0X1 " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}SDS0X1 " D_ENVIRONMENTAL_CONCENTRATION " 10 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; -#endif - -void NovaSdsShow(bool json) -{ - if (novasds_valid) { - float pm10f = (float)(novasds_data.pm100) / 10.0f; - float pm2_5f = (float)(novasds_data.pm25) / 10.0f; - char pm10[33]; - dtostrfd(pm10f, 1, pm10); - char pm2_5[33]; - dtostrfd(pm2_5f, 1, pm2_5); - if (json) { - ResponseAppend_P(PSTR(",\"SDS0X1\":{\"PM2.5\":%s,\"PM10\":%s}"), pm2_5, pm10); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_VOLTAGE, pm2_5); - DomoticzSensor(DZ_CURRENT, pm10); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SDS0X1_SNS, pm2_5, pm10); -#endif - } - } -} - - - - - -bool Xsns20(uint8_t function) -{ - bool result = false; - - if (novasds_type) { - switch (function) { - case FUNC_INIT: - NovaSdsInit(); - break; - case FUNC_EVERY_SECOND: - NovaSdsSecond(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_20 == XdrvMailbox.index) { - result = NovaSdsCommandSensor(); - } - break; - case FUNC_JSON_APPEND: - NovaSdsShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - NovaSdsShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_21_sgp30.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_21_sgp30.ino" -#ifdef USE_I2C -#ifdef USE_SGP30 -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_21_sgp30.ino" -#define XSNS_21 21 -#define XI2C_18 18 - -#define SGP30_ADDRESS 0x58 - -#include "Adafruit_SGP30.h" -Adafruit_SGP30 sgp; - -bool sgp30_type = false; -bool sgp30_ready = false; -float sgp30_abshum; - - - -void sgp30_Init(void) -{ - if (I2cActive(SGP30_ADDRESS)) { return; } - - if (sgp.begin()) { - sgp30_type = true; - - I2cSetActiveFound(SGP30_ADDRESS, "SGP30"); - } -} - - -#define POW_FUNC FastPrecisePow - -float sgp30_AbsoluteHumidity(float temperature, float humidity,char tempUnit) { - - - - - - float temp = NAN; - const float mw = 18.01534; - const float r = 8.31447215; - - if (isnan(temperature) || isnan(humidity) ) { - return NAN; - } - - if (tempUnit != 'C') { - temperature = (temperature - 32.0) * (5.0 / 9.0); - } - - temp = POW_FUNC(2.718281828, (17.67 * temperature) / (temperature + 243.5)); - - - return (6.112 * temp * humidity * mw) / ((273.15 + temperature) * r); -} - -#define SAVE_PERIOD 30 - -void Sgp30Update(void) -{ - sgp30_ready = false; - if (!sgp.IAQmeasure()) { - return; - } - if (global_update && (global_humidity > 0) && (global_temperature != 9999)) { - - sgp30_abshum=sgp30_AbsoluteHumidity(global_temperature,global_humidity,TempUnit()); - sgp.setHumidity(sgp30_abshum*1000); - } - sgp30_ready = true; - - - if (!(uptime%SAVE_PERIOD)) { - - uint16_t TVOC_base; - uint16_t eCO2_base; - - if (!sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) return; - - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_SGP30[] PROGMEM = - "{s}SGP30 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" - "{s}SGP30 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; -const char HTTP_SNS_AHUM[] PROGMEM = "{s}SGP30 Abs Humidity{m}%s g/m3{e}"; -#endif - -#define D_JSON_AHUM "aHumidity" - -void Sgp30Show(bool json) -{ - if (sgp30_ready) { - char abs_hum[33]; - - if (json) { - ResponseAppend_P(PSTR(",\"SGP30\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d"), sgp.eCO2, sgp.TVOC); - if (global_update && global_humidity>0 && global_temperature!=9999) { - - dtostrfd(sgp30_abshum,4,abs_hum); - ResponseAppend_P(PSTR(",\"" D_JSON_AHUM "\":%s"),abs_hum); - } - ResponseJsonEnd(); -#ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, sgp.eCO2); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_SGP30, sgp.eCO2, sgp.TVOC); - if (global_update) { - WSContentSend_PD(HTTP_SNS_AHUM, abs_hum); - } -#endif - } - } -} - - - - - -bool Xsns21(uint8_t function) -{ - if (!I2cEnabled(XI2C_18)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - sgp30_Init(); - } - else if (sgp30_type) { - switch (function) { - case FUNC_EVERY_SECOND: - Sgp30Update(); - break; - case FUNC_JSON_APPEND: - Sgp30Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Sgp30Show(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_22_sr04.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_22_sr04.ino" -#ifdef USE_SR04 - -#include -#include -# 32 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_22_sr04.ino" -#define XSNS_22 22 - -uint8_t sr04_type = 1; -int sr04_echo_pin = 0; -int sr04_trig_pin = 0; -real64_t distance; - -NewPing* sonar = nullptr; -TasmotaSerial* sonar_serial = nullptr; - - - -uint8_t Sr04TModeDetect(void) -{ - sr04_type = 0; - if (pin[GPIO_SR04_ECHO]>=99) return sr04_type; - - sr04_echo_pin = pin[GPIO_SR04_ECHO]; - sr04_trig_pin = (pin[GPIO_SR04_TRIG] < 99) ? pin[GPIO_SR04_TRIG] : -1; - sonar_serial = new TasmotaSerial(sr04_echo_pin, sr04_trig_pin, 1); - - if (sonar_serial->begin(9600,1)) { - DEBUG_SENSOR_LOG(PSTR("SR04: Detect mode")); - - if (sr04_trig_pin!=-1) { - sr04_type = (Sr04TMiddleValue(Sr04TMode3Distance(),Sr04TMode3Distance(),Sr04TMode3Distance())!=NO_ECHO)?3:1; - } else { - sr04_type = 2; - } - } else { - sr04_type = 1; - } - - if (sr04_type < 2) { - delete sonar_serial; - sonar_serial = nullptr; - sonar = new NewPing(sr04_trig_pin, sr04_echo_pin, 300); - } else { - if (sonar_serial->hardwareSerial()) { - ClaimSerial(); - } - } - - AddLog_P2(LOG_LEVEL_INFO,PSTR("SR04: Mode %d"), sr04_type); - return sr04_type; -} - -uint16_t Sr04TMiddleValue(uint16_t first, uint16_t second, uint16_t third) -{ - uint16_t ret = first; - if (first > second) { - first = second; - second = ret; - } - - if (third < first) { - return first; - } else if (third > second) { - return second; - } else { - return third; - } -} - -uint16_t Sr04TMode3Distance() { - - sonar_serial->write(0x55); - sonar_serial->flush(); - - return Sr04TMode2Distance(); -} - -uint16_t Sr04TMode2Distance(void) -{ - sonar_serial->setTimeout(300); - const char startByte = 0xff; - - if (!sonar_serial->find(startByte)) { - - return NO_ECHO; - } - - delay(5); - - uint8_t crc = sonar_serial->read(); - - uint16_t distance = ((uint16_t)crc) << 8; - - - distance += sonar_serial->read(); - crc += distance & 0x00ff; - crc += 0x00FF; - - - if (crc != sonar_serial->read()) { - AddLog_P2(LOG_LEVEL_ERROR,PSTR("SR04: Reading CRC error.")); - return NO_ECHO; - } - - return distance; -} - -void Sr04TReading(void) { - - if (sonar_serial==nullptr && sonar==nullptr) { - Sr04TModeDetect(); - } - - switch (sr04_type) { - case 3: - distance = (real64_t)(Sr04TMiddleValue(Sr04TMode3Distance(),Sr04TMode3Distance(),Sr04TMode3Distance()))/ 10; - break; - case 2: - - while(sonar_serial->available()) sonar_serial->read(); - distance = (real64_t)(Sr04TMiddleValue(Sr04TMode2Distance(),Sr04TMode2Distance(),Sr04TMode2Distance()))/10; - break; - case 1: - distance = (real64_t)(sonar->ping_median(5))/ US_ROUNDTRIP_CM; - break; - default: - distance = NO_ECHO; - } - - return; -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_DISTANCE[] PROGMEM = - "{s}SR04 " D_DISTANCE "{m}%s" D_UNIT_CENTIMETER "{e}"; -#endif - -void Sr04Show(bool json) -{ - - if (distance != 0) { - char distance_chr[33]; - dtostrfd(distance, 3, distance_chr); - - if(json) { - ResponseAppend_P(PSTR(",\"SR04\":{\"" D_JSON_DISTANCE "\":%s}"), distance_chr); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_COUNT, distance_chr); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_DISTANCE, distance_chr); -#endif - } - } -} - - - - - -bool Xsns22(uint8_t function) -{ - bool result = false; - - if (sr04_type) { - switch (function) { - case FUNC_INIT: - result = (pin[GPIO_SR04_ECHO]<99); - break; - case FUNC_EVERY_SECOND: - Sr04TReading(); - result = true; - break; - case FUNC_JSON_APPEND: - Sr04Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Sr04Show(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_24_si1145.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_24_si1145.ino" -#ifdef USE_I2C -#ifdef USE_SI1145 -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_24_si1145.ino" -#define XSNS_24 24 -#define XI2C_19 19 - -#define SI114X_ADDR 0X60 - - - -#define SI114X_QUERY 0X80 -#define SI114X_SET 0XA0 -#define SI114X_NOP 0X00 -#define SI114X_RESET 0X01 -#define SI114X_BUSADDR 0X02 -#define SI114X_PS_FORCE 0X05 -#define SI114X_GET_CAL 0X12 -#define SI114X_ALS_FORCE 0X06 -#define SI114X_PSALS_FORCE 0X07 -#define SI114X_PS_PAUSE 0X09 -#define SI114X_ALS_PAUSE 0X0A -#define SI114X_PSALS_PAUSE 0X0B -#define SI114X_PS_AUTO 0X0D -#define SI114X_ALS_AUTO 0X0E -#define SI114X_PSALS_AUTO 0X0F - - - -#define SI114X_PART_ID 0X00 -#define SI114X_REV_ID 0X01 -#define SI114X_SEQ_ID 0X02 -#define SI114X_INT_CFG 0X03 -#define SI114X_IRQ_ENABLE 0X04 -#define SI114X_IRQ_MODE1 0x05 -#define SI114X_IRQ_MODE2 0x06 -#define SI114X_HW_KEY 0X07 -#define SI114X_MEAS_RATE0 0X08 -#define SI114X_MEAS_RATE1 0X09 -#define SI114X_PS_RATE 0X0A -#define SI114X_PS_LED21 0X0F -#define SI114X_PS_LED3 0X10 -#define SI114X_UCOEFF0 0X13 -#define SI114X_UCOEFF1 0X14 -#define SI114X_UCOEFF2 0X15 -#define SI114X_UCOEFF3 0X16 -#define SI114X_WR 0X17 -#define SI114X_COMMAND 0X18 -#define SI114X_RESPONSE 0X20 -#define SI114X_IRQ_STATUS 0X21 -#define SI114X_ALS_VIS_DATA0 0X22 -#define SI114X_ALS_VIS_DATA1 0X23 -#define SI114X_ALS_IR_DATA0 0X24 -#define SI114X_ALS_IR_DATA1 0X25 -#define SI114X_PS1_DATA0 0X26 -#define SI114X_PS1_DATA1 0X27 -#define SI114X_PS2_DATA0 0X28 -#define SI114X_PS2_DATA1 0X29 -#define SI114X_PS3_DATA0 0X2A -#define SI114X_PS3_DATA1 0X2B -#define SI114X_AUX_DATA0_UVINDEX0 0X2C -#define SI114X_AUX_DATA1_UVINDEX1 0X2D -#define SI114X_RD 0X2E -#define SI114X_CHIP_STAT 0X30 - - - -#define SI114X_CHLIST 0X01 -#define SI114X_CHLIST_ENUV 0x80 -#define SI114X_CHLIST_ENAUX 0x40 -#define SI114X_CHLIST_ENALSIR 0x20 -#define SI114X_CHLIST_ENALSVIS 0x10 -#define SI114X_CHLIST_ENPS1 0x01 -#define SI114X_CHLIST_ENPS2 0x02 -#define SI114X_CHLIST_ENPS3 0x04 - -#define SI114X_PSLED12_SELECT 0X02 -#define SI114X_PSLED3_SELECT 0X03 - -#define SI114X_PS_ENCODE 0X05 -#define SI114X_ALS_ENCODE 0X06 - -#define SI114X_PS1_ADCMUX 0X07 -#define SI114X_PS2_ADCMUX 0X08 -#define SI114X_PS3_ADCMUX 0X09 - -#define SI114X_PS_ADC_COUNTER 0X0A -#define SI114X_PS_ADC_GAIN 0X0B -#define SI114X_PS_ADC_MISC 0X0C - -#define SI114X_ALS_IR_ADC_MUX 0X0E -#define SI114X_AUX_ADC_MUX 0X0F - -#define SI114X_ALS_VIS_ADC_COUNTER 0X10 -#define SI114X_ALS_VIS_ADC_GAIN 0X11 -#define SI114X_ALS_VIS_ADC_MISC 0X12 - -#define SI114X_LED_REC 0X1C - -#define SI114X_ALS_IR_ADC_COUNTER 0X1D -#define SI114X_ALS_IR_ADC_GAIN 0X1E -#define SI114X_ALS_IR_ADC_MISC 0X1F - - - - -#define SI114X_ADCMUX_SMALL_IR 0x00 -#define SI114X_ADCMUX_VISIABLE 0x02 -#define SI114X_ADCMUX_LARGE_IR 0x03 -#define SI114X_ADCMUX_NO 0x06 -#define SI114X_ADCMUX_GND 0x25 -#define SI114X_ADCMUX_TEMPERATURE 0x65 -#define SI114X_ADCMUX_VDD 0x75 - -#define SI114X_PSLED12_SELECT_PS1_NONE 0x00 -#define SI114X_PSLED12_SELECT_PS1_LED1 0x01 -#define SI114X_PSLED12_SELECT_PS1_LED2 0x02 -#define SI114X_PSLED12_SELECT_PS1_LED3 0x04 -#define SI114X_PSLED12_SELECT_PS2_NONE 0x00 -#define SI114X_PSLED12_SELECT_PS2_LED1 0x10 -#define SI114X_PSLED12_SELECT_PS2_LED2 0x20 -#define SI114X_PSLED12_SELECT_PS2_LED3 0x40 -#define SI114X_PSLED3_SELECT_PS2_NONE 0x00 -#define SI114X_PSLED3_SELECT_PS2_LED1 0x10 -#define SI114X_PSLED3_SELECT_PS2_LED2 0x20 -#define SI114X_PSLED3_SELECT_PS2_LED3 0x40 - -#define SI114X_ADC_GAIN_DIV1 0X00 -#define SI114X_ADC_GAIN_DIV2 0X01 -#define SI114X_ADC_GAIN_DIV4 0X02 -#define SI114X_ADC_GAIN_DIV8 0X03 -#define SI114X_ADC_GAIN_DIV16 0X04 -#define SI114X_ADC_GAIN_DIV32 0X05 - -#define SI114X_LED_CURRENT_5MA 0X01 -#define SI114X_LED_CURRENT_11MA 0X02 -#define SI114X_LED_CURRENT_22MA 0X03 -#define SI114X_LED_CURRENT_45MA 0X04 - -#define SI114X_ADC_COUNTER_1ADCCLK 0X00 -#define SI114X_ADC_COUNTER_7ADCCLK 0X01 -#define SI114X_ADC_COUNTER_15ADCCLK 0X02 -#define SI114X_ADC_COUNTER_31ADCCLK 0X03 -#define SI114X_ADC_COUNTER_63ADCCLK 0X04 -#define SI114X_ADC_COUNTER_127ADCCLK 0X05 -#define SI114X_ADC_COUNTER_255ADCCLK 0X06 -#define SI114X_ADC_COUNTER_511ADCCLK 0X07 - -#define SI114X_ADC_MISC_LOWRANGE 0X00 -#define SI114X_ADC_MISC_HIGHRANGE 0X20 -#define SI114X_ADC_MISC_ADC_NORMALPROXIMITY 0X00 -#define SI114X_ADC_MISC_ADC_RAWADC 0X04 - -#define SI114X_INT_CFG_INTOE 0X01 - -#define SI114X_IRQEN_ALS 0x01 -#define SI114X_IRQEN_PS1 0x04 -#define SI114X_IRQEN_PS2 0x08 -#define SI114X_IRQEN_PS3 0x10 - -uint16_t si1145_visible; -uint16_t si1145_infrared; -uint16_t si1145_uvindex; - -bool si1145_type = false; -uint8_t si1145_valid = 0; - - - -uint8_t Si1145ReadByte(uint8_t reg) -{ - return I2cRead8(SI114X_ADDR, reg); -} - -uint16_t Si1145ReadHalfWord(uint8_t reg) -{ - return I2cRead16LE(SI114X_ADDR, reg); -} - -bool Si1145WriteByte(uint8_t reg, uint16_t val) -{ - I2cWrite8(SI114X_ADDR, reg, val); -} - -uint8_t Si1145WriteParamData(uint8_t p, uint8_t v) -{ - Si1145WriteByte(SI114X_WR, v); - Si1145WriteByte(SI114X_COMMAND, p | SI114X_SET); - return Si1145ReadByte(SI114X_RD); -} - - - -bool Si1145Present(void) -{ - return (Si1145ReadByte(SI114X_PART_ID) == 0X45); -} - -void Si1145Reset(void) -{ - Si1145WriteByte(SI114X_MEAS_RATE0, 0); - Si1145WriteByte(SI114X_MEAS_RATE1, 0); - Si1145WriteByte(SI114X_IRQ_ENABLE, 0); - Si1145WriteByte(SI114X_IRQ_MODE1, 0); - Si1145WriteByte(SI114X_IRQ_MODE2, 0); - Si1145WriteByte(SI114X_INT_CFG, 0); - Si1145WriteByte(SI114X_IRQ_STATUS, 0xFF); - - Si1145WriteByte(SI114X_COMMAND, SI114X_RESET); - delay(10); - Si1145WriteByte(SI114X_HW_KEY, 0x17); - delay(10); -} - -void Si1145DeInit(void) -{ - - - Si1145WriteByte(SI114X_UCOEFF0, 0x29); - Si1145WriteByte(SI114X_UCOEFF1, 0x89); - Si1145WriteByte(SI114X_UCOEFF2, 0x02); - Si1145WriteByte(SI114X_UCOEFF3, 0x00); - Si1145WriteParamData(SI114X_CHLIST, SI114X_CHLIST_ENUV | SI114X_CHLIST_ENALSIR | SI114X_CHLIST_ENALSVIS | SI114X_CHLIST_ENPS1); - - - - Si1145WriteParamData(SI114X_PS1_ADCMUX, SI114X_ADCMUX_LARGE_IR); - Si1145WriteByte(SI114X_PS_LED21, SI114X_LED_CURRENT_22MA); - Si1145WriteParamData(SI114X_PSLED12_SELECT, SI114X_PSLED12_SELECT_PS1_LED1); - - - - Si1145WriteParamData(SI114X_PS_ADC_GAIN, SI114X_ADC_GAIN_DIV1); - Si1145WriteParamData(SI114X_PS_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); - Si1145WriteParamData(SI114X_PS_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE | SI114X_ADC_MISC_ADC_RAWADC); - - - - Si1145WriteParamData(SI114X_ALS_VIS_ADC_GAIN, SI114X_ADC_GAIN_DIV1); - Si1145WriteParamData(SI114X_ALS_VIS_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); - Si1145WriteParamData(SI114X_ALS_VIS_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE); - - - - Si1145WriteParamData(SI114X_ALS_IR_ADC_GAIN, SI114X_ADC_GAIN_DIV1); - Si1145WriteParamData(SI114X_ALS_IR_ADC_COUNTER, SI114X_ADC_COUNTER_511ADCCLK); - Si1145WriteParamData(SI114X_ALS_IR_ADC_MISC, SI114X_ADC_MISC_HIGHRANGE); - - - - Si1145WriteByte(SI114X_INT_CFG, SI114X_INT_CFG_INTOE); - Si1145WriteByte(SI114X_IRQ_ENABLE, SI114X_IRQEN_ALS); - - - - Si1145WriteByte(SI114X_MEAS_RATE0, 0xFF); - Si1145WriteByte(SI114X_COMMAND, SI114X_PSALS_AUTO); -} - -bool Si1145Begin(void) -{ - if (!Si1145Present()) { return false; } - - Si1145Reset(); - Si1145DeInit(); - return true; -} - - -uint16_t Si1145ReadUV(void) -{ - return Si1145ReadHalfWord(SI114X_AUX_DATA0_UVINDEX0); -} - - -uint16_t Si1145ReadVisible(void) -{ - return Si1145ReadHalfWord(SI114X_ALS_VIS_DATA0); -} - - -uint16_t Si1145ReadIR(void) -{ - return Si1145ReadHalfWord(SI114X_ALS_IR_DATA0); -} - - - -bool Si1145Read(void) -{ - if (si1145_valid) { si1145_valid--; } - - if (!Si1145Present()) { return false; } - - si1145_visible = Si1145ReadVisible(); - si1145_infrared = Si1145ReadIR(); - si1145_uvindex = Si1145ReadUV(); - si1145_valid = SENSOR_MAX_MISS; - return true; -} - -void Si1145Detect(void) -{ - if (I2cActive(SI114X_ADDR)) { return; } - - if (Si1145Begin()) { - si1145_type = true; - I2cSetActiveFound(SI114X_ADDR, "SI1145"); - } -} - -void Si1145Update(void) -{ - if (!Si1145Read()) { - AddLogMissed("SI1145", si1145_valid); - } -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_SI1145[] PROGMEM = - "{s}SI1145 " D_ILLUMINANCE "{m}%d " D_UNIT_LUX "{e}" - "{s}SI1145 " D_INFRARED "{m}%d " D_UNIT_LUX "{e}" - "{s}SI1145 " D_UV_INDEX "{m}%d.%d{e}"; -#endif - -void Si1145Show(bool json) -{ - if (si1145_valid) { - if (json) { - ResponseAppend_P(PSTR(",\"SI1145\":{\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_INFRARED "\":%d,\"" D_JSON_UV_INDEX "\":%d.%d}"), - si1145_visible, si1145_infrared, si1145_uvindex /100, si1145_uvindex %100); -#ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, si1145_visible); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_SI1145, si1145_visible, si1145_infrared, si1145_uvindex /100, si1145_uvindex %100); -#endif - } - } -} - - - - - -bool Xsns24(uint8_t function) -{ - if (!I2cEnabled(XI2C_19)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Si1145Detect(); - } - else if (si1145_type) { - switch (function) { - case FUNC_EVERY_SECOND: - Si1145Update(); - break; - case FUNC_JSON_APPEND: - Si1145Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Si1145Show(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_26_lm75ad.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_26_lm75ad.ino" -#ifdef USE_I2C -#ifdef USE_LM75AD -# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_26_lm75ad.ino" -#define XSNS_26 26 -#define XI2C_20 20 - -#define LM75AD_ADDRESS1 0x48 -#define LM75AD_ADDRESS2 0x49 -#define LM75AD_ADDRESS3 0x4A -#define LM75AD_ADDRESS4 0x4B -#define LM75AD_ADDRESS5 0x4C -#define LM75AD_ADDRESS6 0x4D -#define LM75AD_ADDRESS7 0x4E -#define LM75AD_ADDRESS8 0x4F - -#define LM75_TEMP_REGISTER 0x00 -#define LM75_CONF_REGISTER 0x01 -#define LM75_THYST_REGISTER 0x02 -#define LM75_TOS_REGISTER 0x03 - -bool lm75ad_type = false; -uint8_t lm75ad_address; -uint8_t lm75ad_addresses[] = { LM75AD_ADDRESS1, LM75AD_ADDRESS2, LM75AD_ADDRESS3, LM75AD_ADDRESS4, LM75AD_ADDRESS5, LM75AD_ADDRESS6, LM75AD_ADDRESS7, LM75AD_ADDRESS8 }; - -void LM75ADDetect(void) -{ - for (uint32_t i = 0; i < sizeof(lm75ad_addresses); i++) { - lm75ad_address = lm75ad_addresses[i]; - if (I2cActive(lm75ad_address)) { - continue; } - if (!I2cSetDevice(lm75ad_address)) { - break; - } - uint16_t buffer; - if (I2cValidRead16(&buffer, lm75ad_address, LM75_THYST_REGISTER)) { - if (buffer == 0x4B00) { - lm75ad_type = true; - I2cSetActiveFound(lm75ad_address, "LM75AD"); - break; - } - } - } -} - -float LM75ADGetTemp(void) -{ - int16_t sign = 1; - - uint16_t t = I2cRead16(lm75ad_address, LM75_TEMP_REGISTER); - if (t & 0x8000) { - t = (~t) +0x20; - sign = -1; - } - t = t >> 5; - return ConvertTemp(sign * t * 0.125); -} - -void LM75ADShow(bool json) -{ - float t = LM75ADGetTemp(); - char temperature[33]; - dtostrfd(t, Settings.flag2.temperature_resolution, temperature); - - if (json) { - ResponseAppend_P(PSTR(",\"LM75AD\":{\"" D_JSON_TEMPERATURE "\":%s}"), temperature); -#ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_TEMP, temperature); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, "LM75AD", temperature, TempUnit()); -#endif - } -} - - - - - -bool Xsns26(uint8_t function) -{ - if (!I2cEnabled(XI2C_20)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - LM75ADDetect(); - } - else if (lm75ad_type) { - switch (function) { - case FUNC_JSON_APPEND: - LM75ADShow(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - LM75ADShow(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" -# 28 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" -#ifdef USE_I2C -#ifdef USE_APDS9960 -# 39 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" -#define XSNS_27 27 -#define XI2C_21 21 - -#if defined(USE_SHT) || defined(USE_VEML6070) || defined(USE_TSL2561) - #warning **** Turned off conflicting drivers SHT and VEML6070 **** - #ifdef USE_SHT - #undef USE_SHT - #endif - #ifdef USE_VEML6070 - #undef USE_VEML6070 - #endif - #ifdef USE_TSL2561 - #undef USE_TSL2561 - #endif -#endif - -#define APDS9960_I2C_ADDR 0x39 - -#define APDS9960_CHIPID_1 0xAB -#define APDS9960_CHIPID_2 0x9C - -#define APDS9930_CHIPID_1 0x12 -#define APDS9930_CHIPID_2 0x39 - - -#define GESTURE_THRESHOLD_OUT 10 -#define GESTURE_SENSITIVITY_1 50 -#define GESTURE_SENSITIVITY_2 20 - -uint8_t APDS9960addr; -uint8_t APDS9960type = 0; -char APDS9960stype[] = "APDS9960"; -char currentGesture[6]; -uint8_t gesture_mode = 1; - - -volatile uint8_t recovery_loop_counter = 0; -#define APDS9960_LONG_RECOVERY 50 -#define APDS9960_MAX_GESTURE_CYCLES 50 -bool APDS9960_overload = false; - -#ifdef USE_WEBSERVER -const char HTTP_APDS_9960_SNS[] PROGMEM = - "{s}" "Red" "{m}%s{e}" - "{s}" "Green" "{m}%s{e}" - "{s}" "Blue" "{m}%s{e}" - "{s}" "Ambient" "{m}%s " D_UNIT_LUX "{e}" - "{s}" "CCT" "{m}%s " "K" "{e}" - "{s}" "Proximity" "{m}%s{e}"; -#endif -# 97 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" -#define FIFO_PAUSE_TIME 30 - - -#define APDS9960_ENABLE 0x80 -#define APDS9960_ATIME 0x81 -#define APDS9960_WTIME 0x83 -#define APDS9960_AILTL 0x84 -#define APDS9960_AILTH 0x85 -#define APDS9960_AIHTL 0x86 -#define APDS9960_AIHTH 0x87 -#define APDS9960_PILT 0x89 -#define APDS9960_PIHT 0x8B -#define APDS9960_PERS 0x8C -#define APDS9960_CONFIG1 0x8D -#define APDS9960_PPULSE 0x8E -#define APDS9960_CONTROL 0x8F -#define APDS9960_CONFIG2 0x90 -#define APDS9960_ID 0x92 -#define APDS9960_STATUS 0x93 -#define APDS9960_CDATAL 0x94 -#define APDS9960_CDATAH 0x95 -#define APDS9960_RDATAL 0x96 -#define APDS9960_RDATAH 0x97 -#define APDS9960_GDATAL 0x98 -#define APDS9960_GDATAH 0x99 -#define APDS9960_BDATAL 0x9A -#define APDS9960_BDATAH 0x9B -#define APDS9960_PDATA 0x9C -#define APDS9960_POFFSET_UR 0x9D -#define APDS9960_POFFSET_DL 0x9E -#define APDS9960_CONFIG3 0x9F -#define APDS9960_GPENTH 0xA0 -#define APDS9960_GEXTH 0xA1 -#define APDS9960_GCONF1 0xA2 -#define APDS9960_GCONF2 0xA3 -#define APDS9960_GOFFSET_U 0xA4 -#define APDS9960_GOFFSET_D 0xA5 -#define APDS9960_GOFFSET_L 0xA7 -#define APDS9960_GOFFSET_R 0xA9 -#define APDS9960_GPULSE 0xA6 -#define APDS9960_GCONF3 0xAA -#define APDS9960_GCONF4 0xAB -#define APDS9960_GFLVL 0xAE -#define APDS9960_GSTATUS 0xAF -#define APDS9960_IFORCE 0xE4 -#define APDS9960_PICLEAR 0xE5 -#define APDS9960_CICLEAR 0xE6 -#define APDS9960_AICLEAR 0xE7 -#define APDS9960_GFIFO_U 0xFC -#define APDS9960_GFIFO_D 0xFD -#define APDS9960_GFIFO_L 0xFE -#define APDS9960_GFIFO_R 0xFF - - -#define APDS9960_PON 0b00000001 -#define APDS9960_AEN 0b00000010 -#define APDS9960_PEN 0b00000100 -#define APDS9960_WEN 0b00001000 -#define APSD9960_AIEN 0b00010000 -#define APDS9960_PIEN 0b00100000 -#define APDS9960_GEN 0b01000000 -#define APDS9960_GVALID 0b00000001 - - -#define OFF 0 -#define ON 1 - - -#define POWER 0 -#define AMBIENT_LIGHT 1 -#define PROXIMITY 2 -#define WAIT 3 -#define AMBIENT_LIGHT_INT 4 -#define PROXIMITY_INT 5 -#define GESTURE 6 -#define ALL 7 - - -#define LED_DRIVE_100MA 0 -#define LED_DRIVE_50MA 1 -#define LED_DRIVE_25MA 2 -#define LED_DRIVE_12_5MA 3 - - -#define PGAIN_1X 0 -#define PGAIN_2X 1 -#define PGAIN_4X 2 -#define PGAIN_8X 3 - - -#define AGAIN_1X 0 -#define AGAIN_4X 1 -#define AGAIN_16X 2 -#define AGAIN_64X 3 - - -#define GGAIN_1X 0 -#define GGAIN_2X 1 -#define GGAIN_4X 2 -#define GGAIN_8X 3 - - -#define LED_BOOST_100 0 -#define LED_BOOST_150 1 -#define LED_BOOST_200 2 -#define LED_BOOST_300 3 - - -#define GWTIME_0MS 0 -#define GWTIME_2_8MS 1 -#define GWTIME_5_6MS 2 -#define GWTIME_8_4MS 3 -#define GWTIME_14_0MS 4 -#define GWTIME_22_4MS 5 -#define GWTIME_30_8MS 6 -#define GWTIME_39_2MS 7 - - -#define DEFAULT_ATIME 0xdb -#define DEFAULT_WTIME 246 -#define DEFAULT_PROX_PPULSE 0x87 -#define DEFAULT_GESTURE_PPULSE 0x89 -#define DEFAULT_POFFSET_UR 0 -#define DEFAULT_POFFSET_DL 0 -#define DEFAULT_CONFIG1 0x60 -#define DEFAULT_LDRIVE LED_DRIVE_100MA -#define DEFAULT_PGAIN PGAIN_4X -#define DEFAULT_AGAIN AGAIN_4X -#define DEFAULT_PILT 0 -#define DEFAULT_PIHT 50 -#define DEFAULT_AILT 0xFFFF -#define DEFAULT_AIHT 0 -#define DEFAULT_PERS 0x11 -#define DEFAULT_CONFIG2 0x01 -#define DEFAULT_CONFIG3 0 -#define DEFAULT_GPENTH 40 -#define DEFAULT_GEXTH 30 -#define DEFAULT_GCONF1 0x40 -#define DEFAULT_GGAIN GGAIN_4X -#define DEFAULT_GLDRIVE LED_DRIVE_100MA -#define DEFAULT_GWTIME GWTIME_2_8MS -#define DEFAULT_GOFFSET 0 -#define DEFAULT_GPULSE 0xC9 -#define DEFAULT_GCONF3 0 -#define DEFAULT_GIEN 0 - -#define ERROR 0xFF - - -enum { - DIR_NONE, - DIR_LEFT, - DIR_RIGHT, - DIR_UP, - DIR_DOWN, - DIR_ALL -}; - - -enum { - APDS9960_NA_STATE, - APDS9960_ALL_STATE -}; - - -typedef struct gesture_data_type { - uint8_t u_data[32]; - uint8_t d_data[32]; - uint8_t l_data[32]; - uint8_t r_data[32]; - uint8_t index; - uint8_t total_gestures; - uint8_t in_threshold; - uint8_t out_threshold; -} gesture_data_type; - - - gesture_data_type gesture_data_; - int16_t gesture_ud_delta_ = 0; - int16_t gesture_lr_delta_ = 0; - int16_t gesture_ud_count_ = 0; - int16_t gesture_lr_count_ = 0; - int16_t gesture_state_ = 0; - int16_t gesture_motion_ = DIR_NONE; - - typedef struct color_data_type { - uint16_t a; - uint16_t r; - uint16_t g; - uint16_t b; - uint8_t p; - uint16_t cct; - uint16_t lux; - } color_data_type; - - color_data_type color_data; - uint8_t APDS9960_aTime = DEFAULT_ATIME; -# 306 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - bool wireWriteByte(uint8_t val) - { - Wire.beginTransmission(APDS9960_I2C_ADDR); - Wire.write(val); - if( Wire.endTransmission() != 0 ) { - return false; - } - - return true; - } -# 325 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" -int8_t wireReadDataBlock( uint8_t reg, - uint8_t *val, - uint16_t len) -{ - unsigned char i = 0; - - - if (!wireWriteByte(reg)) { - return -1; - } - - - Wire.requestFrom(APDS9960_I2C_ADDR, len); - while (Wire.available()) { - if (i >= len) { - return -1; - } - val[i] = Wire.read(); - i++; - } - - return i; -} - - - - - - - -void calculateColorTemperature(void) -{ - float X, Y, Z; - float xc, yc; - float n; - float cct; - - - - - - X = (-0.14282F * color_data.r) + (1.54924F * color_data.g) + (-0.95641F * color_data.b); - Y = (-0.32466F * color_data.r) + (1.57837F * color_data.g) + (-0.73191F * color_data.b); - Z = (-0.68202F * color_data.r) + (0.77073F * color_data.g) + ( 0.56332F * color_data.b); - - - xc = (X) / (X + Y + Z); - yc = (Y) / (X + Y + Z); - - - n = (xc - 0.3320F) / (0.1858F - yc); - - - color_data.cct = (449.0F * FastPrecisePowf(n, 3)) + (3525.0F * FastPrecisePowf(n, 2)) + (6823.3F * n) + 5520.33F; - - return; -} -# 392 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getProxIntLowThresh(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT) ; - return val; - } - - - - - - - void setProxIntLowThresh(uint8_t threshold) - { - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold); - } - - - - - - - uint8_t getProxIntHighThresh(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT) ; - return val; - } - - - - - - - - void setProxIntHighThresh(uint8_t threshold) - { - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold); - } -# 448 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getLEDDrive(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL) ; - - val = (val >> 6) & 0b00000011; - - return val; - } -# 471 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - void setLEDDrive(uint8_t drive) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); - - - drive &= 0b00000011; - drive = drive << 6; - val &= 0b00111111; - val |= drive; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); - } -# 500 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getProximityGain(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL) ; - - val = (val >> 2) & 0b00000011; - - return val; - } -# 523 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - void setProximityGain(uint8_t drive) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); - - - drive &= 0b00000011; - drive = drive << 2; - val &= 0b11110011; - val |= drive; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); - } -# 564 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - void setAmbientLightGain(uint8_t drive) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONTROL); - - - drive &= 0b00000011; - val &= 0b11111100; - val |= drive; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONTROL, val); - } -# 591 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getLEDBoost(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2) ; - - - val = (val >> 4) & 0b00000011; - - return val; - } -# 615 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - void setLEDBoost(uint8_t boost) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG2) ; - - boost &= 0b00000011; - boost = boost << 4; - val &= 0b11001111; - val |= boost; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, val) ; - } - - - - - - - uint8_t getProxGainCompEnable(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; - - - val = (val >> 5) & 0b00000001; - - return val; - } - - - - - - - void setProxGainCompEnable(uint8_t enable) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; - - - enable &= 0b00000001; - enable = enable << 5; - val &= 0b11011111; - val |= enable; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val) ; - } -# 683 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getProxPhotoMask(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; - - - val &= 0b00001111; - - return val; - } -# 708 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - void setProxPhotoMask(uint8_t mask) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_CONFIG3) ; - - - mask &= 0b00001111; - val &= 0b11110000; - val |= mask; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, val) ; - } - - - - - - - uint8_t getGestureEnterThresh(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GPENTH) ; - - return val; - } - - - - - - - void setGestureEnterThresh(uint8_t threshold) - { - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPENTH, threshold) ; - - } - - - - - - - uint8_t getGestureExitThresh(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GEXTH) ; - - return val; - } - - - - - - - void setGestureExitThresh(uint8_t threshold) - { - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GEXTH, threshold) ; - } -# 786 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getGestureGain(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - - val = (val >> 5) & 0b00000011; - - return val; - } -# 810 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - void setGestureGain(uint8_t gain) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - - gain &= 0b00000011; - gain = gain << 5; - val &= 0b10011111; - val |= gain; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; - } -# 838 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getGestureLEDDrive(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - - val = (val >> 3) & 0b00000011; - - return val; - } -# 862 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - void setGestureLEDDrive(uint8_t drive) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - - drive &= 0b00000011; - drive = drive << 3; - val &= 0b11100111; - val |= drive; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; - } -# 894 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - uint8_t getGestureWaitTime(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - - val &= 0b00000111; - - return val; - } -# 922 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - void setGestureWaitTime(uint8_t time) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF2) ; - - - time &= 0b00000111; - val &= 0b11111000; - val |= time; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF2, val) ; - } - - - - - - - void getLightIntLowThreshold(uint16_t &threshold) - { - uint8_t val_byte; - threshold = 0; - - - val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AILTL) ; - threshold = val_byte; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_byte) ; - threshold = threshold + ((uint16_t)val_byte << 8); - } - - - - - - - - void setLightIntLowThreshold(uint16_t threshold) - { - uint8_t val_low; - uint8_t val_high; - - - val_low = threshold & 0x00FF; - val_high = (threshold & 0xFF00) >> 8; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTL, val_low) ; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AILTH, val_high) ; - - } - - - - - - - - void getLightIntHighThreshold(uint16_t &threshold) - { - uint8_t val_byte; - threshold = 0; - - - val_byte = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AIHTL); - threshold = val_byte; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_byte) ; - threshold = threshold + ((uint16_t)val_byte << 8); - } - - - - - - - void setLightIntHighThreshold(uint16_t threshold) - { - uint8_t val_low; - uint8_t val_high; - - - val_low = threshold & 0x00FF; - val_high = (threshold & 0xFF00) >> 8; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTL, val_low); - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_AIHTH, val_high) ; - } - - - - - - - - void getProximityIntLowThreshold(uint8_t &threshold) - { - threshold = 0; - - - threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PILT); - - } - - - - - - - void setProximityIntLowThreshold(uint8_t threshold) - { - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PILT, threshold) ; - } -# 1055 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" - void getProximityIntHighThreshold(uint8_t &threshold) - { - threshold = 0; - - - threshold = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PIHT) ; - - } - - - - - - - void setProximityIntHighThreshold(uint8_t threshold) - { - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PIHT, threshold) ; - } - - - - - - - uint8_t getAmbientLightIntEnable(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; - - - val = (val >> 4) & 0b00000001; - - return val; - } - - - - - - - void setAmbientLightIntEnable(uint8_t enable) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE); - - - enable &= 0b00000001; - enable = enable << 4; - val &= 0b11101111; - val |= enable; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val) ; - } - - - - - - - uint8_t getProximityIntEnable(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; - - - val = (val >> 5) & 0b00000001; - - return val; - } - - - - - - - void setProximityIntEnable(uint8_t enable) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; - - - enable &= 0b00000001; - enable = enable << 5; - val &= 0b11011111; - val |= enable; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, val) ; - } - - - - - - - uint8_t getGestureIntEnable(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; - - - val = (val >> 1) & 0b00000001; - - return val; - } - - - - - - - void setGestureIntEnable(uint8_t enable) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; - - - enable &= 0b00000001; - enable = enable << 1; - val &= 0b11111101; - val |= enable; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val) ; - } - - - - - - void clearAmbientLightInt(void) - { - uint8_t throwaway; - throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_AICLEAR); - } - - - - - - void clearProximityInt(void) - { - uint8_t throwaway; - throwaway = I2cRead8(APDS9960_I2C_ADDR, APDS9960_PICLEAR) ; - - } - - - - - - - uint8_t getGestureMode(void) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; - - - val &= 0b00000001; - - return val; - } - - - - - - - void setGestureMode(uint8_t mode) - { - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GCONF4) ; - - - mode &= 0b00000001; - val &= 0b11111110; - val |= mode; - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF4, val) ; - } - - -bool APDS9960_init(void) -{ - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, DEFAULT_ATIME) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, DEFAULT_WTIME) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_PROX_PPULSE) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_UR, DEFAULT_POFFSET_UR) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_POFFSET_DL, DEFAULT_POFFSET_DL) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG1, DEFAULT_CONFIG1) ; - - setLEDDrive(DEFAULT_LDRIVE); - - setProximityGain(DEFAULT_PGAIN); - - setAmbientLightGain(DEFAULT_AGAIN); - - setProxIntLowThresh(DEFAULT_PILT) ; - - setProxIntHighThresh(DEFAULT_PIHT); - - setLightIntLowThreshold(DEFAULT_AILT) ; - - setLightIntHighThreshold(DEFAULT_AIHT) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PERS, DEFAULT_PERS) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG2, DEFAULT_CONFIG2) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_CONFIG3, DEFAULT_CONFIG3) ; - - - setGestureEnterThresh(DEFAULT_GPENTH); - - setGestureExitThresh(DEFAULT_GEXTH) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF1, DEFAULT_GCONF1) ; - - setGestureGain(DEFAULT_GGAIN) ; - - setGestureLEDDrive(DEFAULT_GLDRIVE) ; - - setGestureWaitTime(DEFAULT_GWTIME) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_U, DEFAULT_GOFFSET) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_D, DEFAULT_GOFFSET) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_L, DEFAULT_GOFFSET) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GOFFSET_R, DEFAULT_GOFFSET) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GPULSE, DEFAULT_GPULSE) ; - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_GCONF3, DEFAULT_GCONF3) ; - - setGestureIntEnable(DEFAULT_GIEN); - - disablePower(); - - return true; -} -# 1333 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" -uint8_t getMode(void) -{ - uint8_t enable_value; - - - enable_value = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ENABLE) ; - - return enable_value; -} - - - - - - - -void setMode(uint8_t mode, uint8_t enable) -{ - uint8_t reg_val; - - - reg_val = getMode(); - - - - enable = enable & 0x01; - if( mode >= 0 && mode <= 6 ) { - if (enable) { - reg_val |= (1 << mode); - } else { - reg_val &= ~(1 << mode); - } - } else if( mode == ALL ) { - if (enable) { - reg_val = 0x7F; - } else { - reg_val = 0x00; - } - } - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ENABLE, reg_val) ; -} - - - - - - -void enableLightSensor(void) -{ - - setAmbientLightGain(DEFAULT_AGAIN); - setAmbientLightIntEnable(0); - enablePower() ; - setMode(AMBIENT_LIGHT, 1) ; -} - - - - - -void disableLightSensor(void) -{ - setAmbientLightIntEnable(0) ; - setMode(AMBIENT_LIGHT, 0) ; -} - - - - - - -void enableProximitySensor(void) -{ - - setProximityGain(DEFAULT_PGAIN); - setLEDDrive(DEFAULT_LDRIVE) ; - setProximityIntEnable(0) ; - enablePower(); - setMode(PROXIMITY, 1) ; -} - - - - - -void disableProximitySensor(void) -{ - setProximityIntEnable(0) ; - setMode(PROXIMITY, 0) ; -} - - - - - - -void enableGestureSensor(void) -{ - - - - - - - - resetGestureParameters(); - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_WTIME, 0xFF) ; - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_PPULSE, DEFAULT_GESTURE_PPULSE) ; - setLEDBoost(LED_BOOST_100); - setGestureIntEnable(0) ; - setGestureMode(1); - enablePower() ; - setMode(WAIT, 1) ; - setMode(PROXIMITY, 1) ; - setMode(GESTURE, 1); -} - - - - - -void disableGestureSensor(void) -{ - resetGestureParameters(); - setGestureIntEnable(0) ; - setGestureMode(0) ; - setMode(GESTURE, 0) ; -} - - - - - - -bool isGestureAvailable(void) -{ - uint8_t val; - - - val = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS) ; - - - val &= APDS9960_GVALID; - - - if( val == 1) { - return true; - } else { - return false; - } -} - - - - - - -int16_t readGesture(void) -{ - uint8_t fifo_level = 0; - uint8_t bytes_read = 0; - uint8_t fifo_data[128]; - uint8_t gstatus; - uint16_t motion; - uint16_t i; - uint8_t gesture_loop_counter = 0; - - - if( !isGestureAvailable() || !(getMode() & 0b01000001) ) { - return DIR_NONE; - } - - - while(1) { - if (gesture_loop_counter == APDS9960_MAX_GESTURE_CYCLES){ - disableGestureSensor(); - APDS9960_overload = true; - AddLog_P(LOG_LEVEL_DEBUG, PSTR("Sensor overload")); - } - gesture_loop_counter += 1; - - delay(FIFO_PAUSE_TIME); - - - gstatus = I2cRead8(APDS9960_I2C_ADDR, APDS9960_GSTATUS); - - - if( (gstatus & APDS9960_GVALID) == APDS9960_GVALID ) { - - - fifo_level = I2cRead8(APDS9960_I2C_ADDR,APDS9960_GFLVL) ; - - - if( fifo_level > 0) { - bytes_read = wireReadDataBlock( APDS9960_GFIFO_U, - (uint8_t*)fifo_data, - (fifo_level * 4) ); - if( bytes_read == -1 ) { - return ERROR; - } - - - if( bytes_read >= 4 ) { - for( i = 0; i < bytes_read; i += 4 ) { - gesture_data_.u_data[gesture_data_.index] = \ - fifo_data[i + 0]; - gesture_data_.d_data[gesture_data_.index] = \ - fifo_data[i + 1]; - gesture_data_.l_data[gesture_data_.index] = \ - fifo_data[i + 2]; - gesture_data_.r_data[gesture_data_.index] = \ - fifo_data[i + 3]; - gesture_data_.index++; - gesture_data_.total_gestures++; - } - - if( processGestureData() ) { - if( decodeGesture() ) { - - } - } - - gesture_data_.index = 0; - gesture_data_.total_gestures = 0; - } - } - } else { - - - delay(FIFO_PAUSE_TIME); - decodeGesture(); - motion = gesture_motion_; - resetGestureParameters(); - return motion; - } - } -} - - - - - -void enablePower(void) -{ - setMode(POWER, 1) ; -} - - - - - -void disablePower(void) -{ - setMode(POWER, 0) ; -} -# 1600 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" -void readAllColorAndProximityData(void) -{ - if (I2cReadBuffer(APDS9960_I2C_ADDR, APDS9960_CDATAL, (uint8_t *) &color_data, (uint16_t)9)) - { - - - } -} -# 1616 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" -void resetGestureParameters(void) -{ - gesture_data_.index = 0; - gesture_data_.total_gestures = 0; - - gesture_ud_delta_ = 0; - gesture_lr_delta_ = 0; - - gesture_ud_count_ = 0; - gesture_lr_count_ = 0; - - gesture_state_ = 0; - gesture_motion_ = DIR_NONE; -} - - - - - - -bool processGestureData(void) -{ - uint8_t u_first = 0; - uint8_t d_first = 0; - uint8_t l_first = 0; - uint8_t r_first = 0; - uint8_t u_last = 0; - uint8_t d_last = 0; - uint8_t l_last = 0; - uint8_t r_last = 0; - uint16_t ud_ratio_first; - uint16_t lr_ratio_first; - uint16_t ud_ratio_last; - uint16_t lr_ratio_last; - uint16_t ud_delta; - uint16_t lr_delta; - uint16_t i; - - - if( gesture_data_.total_gestures <= 4 ) { - return false; - } - - - if( (gesture_data_.total_gestures <= 32) && \ - (gesture_data_.total_gestures > 0) ) { - - - for( i = 0; i < gesture_data_.total_gestures; i++ ) { - if( (gesture_data_.u_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.d_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.l_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.r_data[i] > GESTURE_THRESHOLD_OUT) ) { - - u_first = gesture_data_.u_data[i]; - d_first = gesture_data_.d_data[i]; - l_first = gesture_data_.l_data[i]; - r_first = gesture_data_.r_data[i]; - break; - } - } - - - if( (u_first == 0) || (d_first == 0) || \ - (l_first == 0) || (r_first == 0) ) { - - return false; - } - - for( i = gesture_data_.total_gestures - 1; i >= 0; i-- ) { - - if( (gesture_data_.u_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.d_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.l_data[i] > GESTURE_THRESHOLD_OUT) && - (gesture_data_.r_data[i] > GESTURE_THRESHOLD_OUT) ) { - - u_last = gesture_data_.u_data[i]; - d_last = gesture_data_.d_data[i]; - l_last = gesture_data_.l_data[i]; - r_last = gesture_data_.r_data[i]; - break; - } - } - } - - - ud_ratio_first = ((u_first - d_first) * 100) / (u_first + d_first); - lr_ratio_first = ((l_first - r_first) * 100) / (l_first + r_first); - ud_ratio_last = ((u_last - d_last) * 100) / (u_last + d_last); - lr_ratio_last = ((l_last - r_last) * 100) / (l_last + r_last); - - - ud_delta = ud_ratio_last - ud_ratio_first; - lr_delta = lr_ratio_last - lr_ratio_first; - - - gesture_ud_delta_ += ud_delta; - gesture_lr_delta_ += lr_delta; - - - if( gesture_ud_delta_ >= GESTURE_SENSITIVITY_1 ) { - gesture_ud_count_ = 1; - } else if( gesture_ud_delta_ <= -GESTURE_SENSITIVITY_1 ) { - gesture_ud_count_ = -1; - } else { - gesture_ud_count_ = 0; - } - - - if( gesture_lr_delta_ >= GESTURE_SENSITIVITY_1 ) { - gesture_lr_count_ = 1; - } else if( gesture_lr_delta_ <= -GESTURE_SENSITIVITY_1 ) { - gesture_lr_count_ = -1; - } else { - gesture_lr_count_ = 0; - } - return false; -} - - - - - - -bool decodeGesture(void) -{ - - - if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == 0) ) { - gesture_motion_ = DIR_UP; - } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == 0) ) { - gesture_motion_ = DIR_DOWN; - } else if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == 1) ) { - gesture_motion_ = DIR_RIGHT; - } else if( (gesture_ud_count_ == 0) && (gesture_lr_count_ == -1) ) { - gesture_motion_ = DIR_LEFT; - } else if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == 1) ) { - if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { - gesture_motion_ = DIR_UP; - } else { - gesture_motion_ = DIR_RIGHT; - } - } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == -1) ) { - if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { - gesture_motion_ = DIR_DOWN; - } else { - gesture_motion_ = DIR_LEFT; - } - } else if( (gesture_ud_count_ == -1) && (gesture_lr_count_ == -1) ) { - if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { - gesture_motion_ = DIR_UP; - } else { - gesture_motion_ = DIR_LEFT; - } - } else if( (gesture_ud_count_ == 1) && (gesture_lr_count_ == 1) ) { - if( abs(gesture_ud_delta_) > abs(gesture_lr_delta_) ) { - gesture_motion_ = DIR_DOWN; - } else { - gesture_motion_ = DIR_RIGHT; - } - } else { - return false; - } - - return true; -} - -void handleGesture(void) { - if (isGestureAvailable() ) { - switch (readGesture()) { - case DIR_UP: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("UP")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Up")); - break; - case DIR_DOWN: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("DOWN")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Down")); - break; - case DIR_LEFT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("LEFT")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Left")); - break; - case DIR_RIGHT: - AddLog_P(LOG_LEVEL_DEBUG, PSTR("RIGHT")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Right")); - break; - default: - if(APDS9960_overload) - { - AddLog_P(LOG_LEVEL_DEBUG, PSTR("LONG")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("Long")); - } - else{ - AddLog_P(LOG_LEVEL_DEBUG, PSTR("NONE")); - snprintf_P(currentGesture, sizeof(currentGesture), PSTR("None")); - } - } - MqttPublishSensor(); - } -} - -void APDS9960_adjustATime(void) -{ - - I2cValidRead16LE(&color_data.a, APDS9960_I2C_ADDR, APDS9960_CDATAL); - - - if (color_data.a < (uint16_t)20){ - APDS9960_aTime = 0x40; - } - else if (color_data.a < (uint16_t)40){ - APDS9960_aTime = 0x80; - } - else if (color_data.a < (uint16_t)50){ - APDS9960_aTime = DEFAULT_ATIME; - } - else if (color_data.a < (uint16_t)70){ - APDS9960_aTime = 0xc0; - } - if (color_data.a < 200){ - APDS9960_aTime = 0xe9; - } - - - - else{ - APDS9960_aTime = 0xff; - } - - - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, APDS9960_aTime); - enablePower(); - enableLightSensor(); - delay(20); -} - - -void APDS9960_loop(void) -{ - if (recovery_loop_counter > 0){ - recovery_loop_counter -= 1; - } - if (recovery_loop_counter == 1 && APDS9960_overload){ - enableGestureSensor(); - APDS9960_overload = false; - Response_P(PSTR("{\"Gesture\":\"On\"}")); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); - gesture_mode = 1; - } - - if (gesture_mode) { - if (recovery_loop_counter == 0){ - handleGesture(); - - if (APDS9960_overload) - { - disableGestureSensor(); - recovery_loop_counter = APDS9960_LONG_RECOVERY; - Response_P(PSTR("{\"Gesture\":\"Off\"}")); - MqttPublishPrefixTopic_P(RESULT_OR_TELE, mqtt_data); - gesture_mode = 0; - } - } - } -} - -void APDS9960_detect(void) -{ - if (APDS9960type || I2cActive(APDS9960_I2C_ADDR)) { return; } - - APDS9960type = I2cRead8(APDS9960_I2C_ADDR, APDS9960_ID); - if (APDS9960type == APDS9960_CHIPID_1 || APDS9960type == APDS9960_CHIPID_2) { - if (APDS9960_init()) { - I2cSetActiveFound(APDS9960_I2C_ADDR, APDS9960stype); - - enableProximitySensor(); - enableGestureSensor(); - } else { - APDS9960type = 0; - } - } else { - APDS9960type = 0; - } - currentGesture[0] = '\0'; -} - - - - - -void APDS9960_show(bool json) -{ - if (!APDS9960type) { return; } - - if (!gesture_mode && !APDS9960_overload) { - char red_chr[10]; - char green_chr[10]; - char blue_chr[10]; - char ambient_chr[10]; - char cct_chr[10]; - char prox_chr[10]; - - readAllColorAndProximityData(); - - sprintf (ambient_chr, "%u", color_data.a/4); - sprintf (red_chr, "%u", color_data.r); - sprintf (green_chr, "%u", color_data.g); - sprintf (blue_chr, "%u", color_data.b ); - sprintf (prox_chr, "%u", color_data.p ); - - - - - - calculateColorTemperature(); - sprintf (cct_chr, "%u", color_data.cct); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"Red\":%s,\"Green\":%s,\"Blue\":%s,\"Ambient\":%s,\"CCT\":%s,\"Proximity\":%s}"), - APDS9960stype, red_chr, green_chr, blue_chr, ambient_chr, cct_chr, prox_chr); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_APDS_9960_SNS, red_chr, green_chr, blue_chr, ambient_chr, cct_chr, prox_chr ); -#endif - } - } - else { - if (json && (currentGesture[0] != '\0' )) { - ResponseAppend_P(PSTR(",\"%s\":{\"%s\":1}"), APDS9960stype, currentGesture); - currentGesture[0] = '\0'; - } - } -} -# 1961 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_27_apds9960.ino" -bool APDS9960CommandSensor(void) -{ - bool serviced = true; - - switch (XdrvMailbox.payload) { - case 0: - disableGestureSensor(); - gesture_mode = 0; - enableLightSensor(); - APDS9960_overload = false; - break; - case 1: - if (APDS9960type) { - setGestureGain(DEFAULT_GGAIN); - setProximityGain(DEFAULT_PGAIN); - disableLightSensor(); - enableGestureSensor(); - gesture_mode = 1; - } - break; - case 2: - if (APDS9960type) { - setGestureGain(GGAIN_2X); - setProximityGain(PGAIN_2X); - disableLightSensor(); - enableGestureSensor(); - gesture_mode = 1; - } - break; - default: - int temp_aTime = (uint8_t)XdrvMailbox.payload; - if (temp_aTime > 2 && temp_aTime < 256){ - disablePower(); - I2cWrite8(APDS9960_I2C_ADDR, APDS9960_ATIME, temp_aTime); - enablePower(); - enableLightSensor(); - } - break; - } - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_27, GetStateText(gesture_mode)); - - return serviced; -} - - - - - -bool Xsns27(uint8_t function) -{ - if (!I2cEnabled(XI2C_21)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - APDS9960_detect(); - } - else if (APDS9960type) { - switch (function) { - case FUNC_EVERY_50_MSECOND: - APDS9960_loop(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_27 == XdrvMailbox.index) { - result = APDS9960CommandSensor(); - } - break; - case FUNC_JSON_APPEND: - APDS9960_show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - APDS9960_show(0); - break; -#endif - } - } - return result; -} -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_28_tm1638.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_28_tm1638.ino" -#ifdef USE_TM1638 - - - - - - -#define XSNS_28 28 - -#define TM1638_COLOR_NONE 0 -#define TM1638_COLOR_RED 1 -#define TM1638_COLOR_GREEN 2 - -#define TM1638_CLOCK_DELAY 1 - -uint8_t tm1638_type = 1; -uint8_t tm1638_clock_pin = 0; -uint8_t tm1638_data_pin = 0; -uint8_t tm1638_strobe_pin = 0; -uint8_t tm1638_displays = 8; -uint8_t tm1638_active_display = 1; -uint8_t tm1638_intensity = 0; -uint8_t tm1638_state = 0; - - - - - - -void Tm16XXSend(uint8_t data) -{ - for (uint32_t i = 0; i < 8; i++) { - digitalWrite(tm1638_data_pin, !!(data & (1 << i))); - digitalWrite(tm1638_clock_pin, LOW); - delayMicroseconds(TM1638_CLOCK_DELAY); - digitalWrite(tm1638_clock_pin, HIGH); - } -} - -void Tm16XXSendCommand(uint8_t cmd) -{ - digitalWrite(tm1638_strobe_pin, LOW); - Tm16XXSend(cmd); - digitalWrite(tm1638_strobe_pin, HIGH); -} - -void TM16XXSendData(uint8_t address, uint8_t data) -{ - Tm16XXSendCommand(0x44); - digitalWrite(tm1638_strobe_pin, LOW); - Tm16XXSend(0xC0 | address); - Tm16XXSend(data); - digitalWrite(tm1638_strobe_pin, HIGH); -} - -uint8_t Tm16XXReceive(void) -{ - uint8_t temp = 0; - - - pinMode(tm1638_data_pin, INPUT); - digitalWrite(tm1638_data_pin, HIGH); - - for (uint32_t i = 0; i < 8; ++i) { - digitalWrite(tm1638_clock_pin, LOW); - delayMicroseconds(TM1638_CLOCK_DELAY); - temp |= digitalRead(tm1638_data_pin) << i; - digitalWrite(tm1638_clock_pin, HIGH); - } - - - pinMode(tm1638_data_pin, OUTPUT); - digitalWrite(tm1638_data_pin, LOW); - - return temp; -} - - - -void Tm16XXClearDisplay(void) -{ - for (uint32_t i = 0; i < tm1638_displays; i++) { - TM16XXSendData(i << 1, 0); - } -} - -void Tm1638SetLED(uint8_t color, uint8_t pos) -{ - TM16XXSendData((pos << 1) + 1, color); -} - -void Tm1638SetLEDs(word leds) -{ - for (uint32_t i = 0; i < tm1638_displays; i++) { - uint8_t color = 0; - - if ((leds & (1 << i)) != 0) { - color |= TM1638_COLOR_RED; - } - - if ((leds & (1 << (i + 8))) != 0) { - color |= TM1638_COLOR_GREEN; - } - - Tm1638SetLED(color, i); - } -} - -uint8_t Tm1638GetButtons(void) -{ - uint8_t keys = 0; - - digitalWrite(tm1638_strobe_pin, LOW); - Tm16XXSend(0x42); - for (uint32_t i = 0; i < 4; i++) { - keys |= Tm16XXReceive() << i; - } - digitalWrite(tm1638_strobe_pin, HIGH); - - return keys; -} - - - -void TmInit(void) -{ - tm1638_type = 0; - if ((pin[GPIO_TM16CLK] < 99) && (pin[GPIO_TM16DIO] < 99) && (pin[GPIO_TM16STB] < 99)) { - tm1638_clock_pin = pin[GPIO_TM16CLK]; - tm1638_data_pin = pin[GPIO_TM16DIO]; - tm1638_strobe_pin = pin[GPIO_TM16STB]; - - pinMode(tm1638_data_pin, OUTPUT); - pinMode(tm1638_clock_pin, OUTPUT); - pinMode(tm1638_strobe_pin, OUTPUT); - - digitalWrite(tm1638_strobe_pin, HIGH); - digitalWrite(tm1638_clock_pin, HIGH); - - Tm16XXSendCommand(0x40); - Tm16XXSendCommand(0x80 | (tm1638_active_display ? 8 : 0) | tmin(7, tm1638_intensity)); - - digitalWrite(tm1638_strobe_pin, LOW); - Tm16XXSend(0xC0); - for (uint32_t i = 0; i < 16; i++) { - Tm16XXSend(0x00); - } - digitalWrite(tm1638_strobe_pin, HIGH); - - tm1638_type = 1; - tm1638_state = 1; - } -} - -void TmLoop(void) -{ - if (tm1638_state) { - uint8_t buttons = Tm1638GetButtons(); - for (uint32_t i = 0; i < MAX_SWITCHES; i++) { - SwitchSetVirtual(i, (buttons &1) ^1); - uint8_t color = (SwitchGetVirtual(i)) ? TM1638_COLOR_NONE : TM1638_COLOR_RED; - Tm1638SetLED(color, i); - buttons >>= 1; - } - SwitchHandler(1); - } -} -# 201 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_28_tm1638.ino" -bool Xsns28(uint8_t function) -{ - bool result = false; - - if (tm1638_type) { - switch (function) { - case FUNC_INIT: - TmInit(); - break; - case FUNC_EVERY_50_MSECOND: - TmLoop(); - break; -# 223 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_28_tm1638.ino" - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_29_mcp230xx.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_29_mcp230xx.ino" -#ifdef USE_I2C -#ifdef USE_MCP230xx -# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_29_mcp230xx.ino" -#define XSNS_29 29 -#define XI2C_22 22 - - - - - -uint8_t MCP230xx_IODIR = 0x00; -uint8_t MCP230xx_GPINTEN = 0x02; -uint8_t MCP230xx_IOCON = 0x05; -uint8_t MCP230xx_GPPU = 0x06; -uint8_t MCP230xx_INTF = 0x07; -uint8_t MCP230xx_INTCAP = 0x08; -uint8_t MCP230xx_GPIO = 0x09; - -uint8_t mcp230xx_type = 0; -uint8_t mcp230xx_pincount = 0; -uint8_t mcp230xx_int_en = 0; -uint8_t mcp230xx_int_prio_counter = 0; -uint8_t mcp230xx_int_counter_en = 0; -uint8_t mcp230xx_int_retainer_en = 0; -uint8_t mcp230xx_int_sec_counter = 0; - -uint8_t mcp230xx_int_report_defer_counter[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - -uint16_t mcp230xx_int_counter[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - -uint8_t mcp230xx_int_retainer[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - -unsigned long int_millis[16]; - -const char MCP230XX_SENSOR_RESPONSE[] PROGMEM = "{\"Sensor29_D%i\":{\"MODE\":%i,\"PULL_UP\":\"%s\",\"INT_MODE\":\"%s\",\"STATE\":\"%s\"}}"; - -const char MCP230XX_INTCFG_RESPONSE[] PROGMEM = "{\"MCP230xx_INT%s\":{\"D_%i\":%i}}"; - -#ifdef USE_MCP230xx_OUTPUT -const char MCP230XX_CMND_RESPONSE[] PROGMEM = "{\"S29cmnd_D%i\":{\"COMMAND\":\"%s\",\"STATE\":\"%s\"}}"; -#endif - -void MCP230xx_CheckForIntCounter(void) { - uint8_t en = 0; - for (uint32_t ca=0;ca<16;ca++) { - if (Settings.mcp230xx_config[ca].int_count_en) { - en=1; - } - } - if (!Settings.mcp230xx_int_timer) en=0; - mcp230xx_int_counter_en=en; - if (!mcp230xx_int_counter_en) { - for (uint32_t ca=0;ca<16;ca++) { - mcp230xx_int_counter[ca] = 0; - } - } -} - -void MCP230xx_CheckForIntRetainer(void) { - uint8_t en = 0; - for (uint32_t ca=0;ca<16;ca++) { - if (Settings.mcp230xx_config[ca].int_retain_flag) { - en=1; - } - } - mcp230xx_int_retainer_en=en; - if (!mcp230xx_int_retainer_en) { - for (uint32_t ca=0;ca<16;ca++) { - mcp230xx_int_retainer[ca] = 0; - } - } -} - -const char* ConvertNumTxt(uint8_t statu, uint8_t pinmod=0) { -#ifdef USE_MCP230xx_OUTPUT -if ((6 == pinmod) && (statu < 2)) { statu = abs(statu-1); } -#endif - switch (statu) { - case 0: - return "OFF"; - break; - case 1: - return "ON"; - break; -#ifdef USE_MCP230xx_OUTPUT - case 2: - return "TOGGLE"; - break; -#endif - } - return ""; -} - -const char* IntModeTxt(uint8_t intmo) { - switch (intmo) { - case 0: - return "ALL"; - break; - case 1: - return "EVENT"; - break; - case 2: - return "TELE"; - break; - case 3: - return "DISABLED"; - break; - } - return ""; -} - -uint8_t MCP230xx_readGPIO(uint8_t port) { - return I2cRead8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port); -} - -void MCP230xx_ApplySettings(void) -{ - uint8_t int_en = 0; - for (uint32_t mcp230xx_port = 0; mcp230xx_port < mcp230xx_type; mcp230xx_port++) { - uint8_t reg_gppu = 0; - uint8_t reg_gpinten = 0; - uint8_t reg_iodir = 0xFF; -#ifdef USE_MCP230xx_OUTPUT - uint8_t reg_portpins = 0x00; -#endif - for (uint32_t idx = 0; idx < 8; idx++) { - switch (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pinmode) { - case 0 ... 1: - reg_iodir |= (1 << idx); - break; - case 2 ... 4: - reg_iodir |= (1 << idx); - reg_gpinten |= (1 << idx); - int_en = 1; - break; -#ifdef USE_MCP230xx_OUTPUT - case 5 ... 6: - reg_iodir &= ~(1 << idx); - if (Settings.flag.save_state) { - reg_portpins |= (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].saved_state << idx); - } else { - if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) { - reg_portpins |= (1 << idx); - } - } - break; -#endif - default: - break; - } -#ifdef USE_MCP230xx_OUTPUT - if ((Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) && (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pinmode < 5)) { - reg_gppu |= (1 << idx); - } -#else - if (Settings.mcp230xx_config[idx+(mcp230xx_port*8)].pullup) { - reg_gppu |= (1 << idx); - } -#endif - } - I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPPU+mcp230xx_port, reg_gppu); - I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten); - I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IODIR+mcp230xx_port, reg_iodir); -#ifdef USE_MCP230xx_OUTPUT - I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins); -#endif - } - for (uint32_t idx=0;idx 0) { - if (I2cValidRead8(&mcp230xx_intcap, USE_MCP230xx_ADDR, MCP230xx_INTCAP+mcp230xx_port)) { - for (uint32_t intp = 0; intp < 8; intp++) { - if ((intf >> intp) & 0x01) { - report_int = 0; - if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].pinmode > 1) { - switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].pinmode) { - case 2: - report_int = 1; - break; - case 3: - if (((mcp230xx_intcap >> intp) & 0x01) == 0) report_int = 1; - break; - case 4: - if (((mcp230xx_intcap >> intp) & 0x01) == 1) report_int = 1; - break; - default: - break; - } - - if ((mcp230xx_int_counter_en) && (report_int)) { - if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_count_en) { - mcp230xx_int_counter[intp+(mcp230xx_port*8)]++; - } - } - - if (report_int) { - if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_defer) { - mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)]++; - if (mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)] >= Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_defer) { - mcp230xx_int_report_defer_counter[intp+(mcp230xx_port*8)]=0; - } else { - report_int = 0; - } - } - } - - if (report_int) { - if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_retain_flag) { - mcp230xx_int_retainer[intp+(mcp230xx_port*8)] = 1; - report_int = 0; - } - } - if (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_count_en) { - report_int = 0; - } - if (report_int) { - bool int_tele = false; - bool int_event = false; - unsigned long millis_now = millis(); - unsigned long millis_since_last_int = millis_now - int_millis[intp+(mcp230xx_port*8)]; - int_millis[intp+(mcp230xx_port*8)]=millis_now; - switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_mode) { - case 0: - int_tele=true; - int_event=true; - break; - case 1: - int_event=true; - break; - case 2: - int_tele=true; - break; - } - if (int_tele) { - ResponseTime_P(PSTR(",\"MCP230XX_INT\":{\"D%i\":%i,\"MS\":%lu}}"), - intp+(mcp230xx_port*8), ((mcp230xx_intcap >> intp) & 0x01),millis_since_last_int); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("MCP230XX_INT")); - } - if (int_event) { - char command[19]; - sprintf(command,"event MCPINT_D%i=%i",intp+(mcp230xx_port*8),((mcp230xx_intcap >> intp) & 0x01)); - ExecuteCommand(command, SRC_RULE); - } - } - } - } - } - } - } - } - } -} - -void MCP230xx_Show(bool json) -{ - if (json) { - uint8_t gpio = MCP230xx_readGPIO(0); - ResponseAppend_P(PSTR(",\"MCP230XX\":{\"D0\":%i,\"D1\":%i,\"D2\":%i,\"D3\":%i,\"D4\":%i,\"D5\":%i,\"D6\":%i,\"D7\":%i"), - (gpio>>0)&1,(gpio>>1)&1,(gpio>>2)&1,(gpio>>3)&1,(gpio>>4)&1,(gpio>>5)&1,(gpio>>6)&1,(gpio>>7)&1); - if (2 == mcp230xx_type) { - gpio = MCP230xx_readGPIO(1); - ResponseAppend_P(PSTR(",\"D8\":%i,\"D9\":%i,\"D10\":%i,\"D11\":%i,\"D12\":%i,\"D13\":%i,\"D14\":%i,\"D15\":%i"), - (gpio>>0)&1,(gpio>>1)&1,(gpio>>2)&1,(gpio>>3)&1,(gpio>>4)&1,(gpio>>5)&1,(gpio>>6)&1,(gpio>>7)&1); - } - ResponseJsonEnd(); - } -} - -#ifdef USE_MCP230xx_OUTPUT - -void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate) { - uint8_t portpins; - uint8_t port = 0; - uint8_t pinmo = Settings.mcp230xx_config[pin].pinmode; - uint8_t interlock = Settings.flag.interlock; - int pinadd = (pin % 2)+1-(3*(pin % 2)); - char cmnd[7], stt[4]; - if (pin > 7) { port = 1; } - portpins = MCP230xx_readGPIO(port); - if (interlock && (pinmo == Settings.mcp230xx_config[pin+pinadd].pinmode)) { - if (pinstate < 2) { - if (6 == pinmo) { - if (pinstate) portpins |= (1 << (pin-(port*8))); else portpins |= (1 << (pin+pinadd-(port*8))),portpins &= ~(1 << (pin-(port*8))); - } else { - if (pinstate) portpins &= ~(1 << (pin+pinadd-(port*8))),portpins |= (1 << (pin-(port*8))); else portpins &= ~(1 << (pin-(port*8))); - } - } else { - if (6 == pinmo) { - portpins |= (1 << (pin+pinadd-(port*8))),portpins ^= (1 << (pin-(port*8))); - } else { - portpins &= ~(1 << (pin+pinadd-(port*8))),portpins ^= (1 << (pin-(port*8))); - } - } - } else { - if (pinstate < 2) { - if (pinstate) portpins |= (1 << (pin-(port*8))); else portpins &= ~(1 << (pin-(port*8))); - } else { - portpins ^= (1 << (pin-(port*8))); - } - } - I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port, portpins); - if (Settings.flag.save_state) { - Settings.mcp230xx_config[pin].saved_state=portpins>>(pin-(port*8))&1; - Settings.mcp230xx_config[pin+pinadd].saved_state=portpins>>(pin+pinadd-(port*8))&1; - } - sprintf(cmnd,ConvertNumTxt(pinstate, pinmo)); - sprintf(stt,ConvertNumTxt((portpins >> (pin-(port*8))&1), pinmo)); - if (interlock && (pinmo == Settings.mcp230xx_config[pin+pinadd].pinmode)) { - char stt1[4]; - sprintf(stt1,ConvertNumTxt((portpins >> (pin+pinadd-(port*8))&1), pinmo)); - Response_P(PSTR("{\"S29cmnd_D%i\":{\"COMMAND\":\"%s\",\"STATE\":\"%s\"},\"S29cmnd_D%i\":{\"STATE\":\"%s\"}}"),pin, cmnd, stt, pin+pinadd, stt1); - } else { - Response_P(MCP230XX_CMND_RESPONSE, pin, cmnd, stt); - } -} - -#endif - -void MCP230xx_Reset(uint8_t pinmode) { - uint8_t pullup = 0; - if ((pinmode > 1) && (pinmode < 5)) { pullup=1; } - for (uint32_t pinx=0;pinx<16;pinx++) { - Settings.mcp230xx_config[pinx].pinmode=pinmode; - Settings.mcp230xx_config[pinx].pullup=pullup; - Settings.mcp230xx_config[pinx].saved_state=0; - if ((pinmode > 1) && (pinmode < 5)) { - Settings.mcp230xx_config[pinx].int_report_mode=0; - } else { - Settings.mcp230xx_config[pinx].int_report_mode=3; - } - Settings.mcp230xx_config[pinx].int_report_defer=0; - Settings.mcp230xx_config[pinx].int_count_en=0; - Settings.mcp230xx_config[pinx].int_retain_flag=0; - Settings.mcp230xx_config[pinx].spare13=0; - Settings.mcp230xx_config[pinx].spare14=0; - Settings.mcp230xx_config[pinx].spare15=0; - } - Settings.mcp230xx_int_prio = 0; - Settings.mcp230xx_int_timer = 0; - MCP230xx_ApplySettings(); - char pulluptxt[7]; - char intmodetxt[9]; - sprintf(pulluptxt,ConvertNumTxt(pullup)); - uint8_t intmode = 3; - if ((pinmode > 1) && (pinmode < 5)) { intmode = 0; } - sprintf(intmodetxt,IntModeTxt(intmode)); - Response_P(MCP230XX_SENSOR_RESPONSE,99,pinmode,pulluptxt,intmodetxt,""); -} - -bool MCP230xx_Command(void) -{ - bool serviced = true; - bool validpin = false; - uint8_t paramcount = 0; - if (XdrvMailbox.data_len > 0) { - paramcount=1; - } else { - serviced = false; - return serviced; - } - char sub_string[XdrvMailbox.data_len]; - for (uint32_t ca=0;ca 1) { - uint8_t intpri = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if ((intpri >= 0) && (intpri <= 20)) { - Settings.mcp230xx_int_prio = intpri; - Response_P(MCP230XX_INTCFG_RESPONSE,"PRI",99,Settings.mcp230xx_int_prio); - return serviced; - } - } else { - Response_P(MCP230XX_INTCFG_RESPONSE,"PRI",99,Settings.mcp230xx_int_prio); - return serviced; - } - } - - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTTIMER")) { - if (paramcount > 1) { - uint8_t inttim = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if ((inttim >= 0) && (inttim <= 3600)) { - Settings.mcp230xx_int_timer = inttim; - MCP230xx_CheckForIntCounter(); - Response_P(MCP230XX_INTCFG_RESPONSE,"TIMER",99,Settings.mcp230xx_int_timer); - return serviced; - } - } else { - Response_P(MCP230XX_INTCFG_RESPONSE,"TIMER",99,Settings.mcp230xx_int_timer); - return serviced; - } - } - - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTDEF")) { - if (paramcount > 1) { - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if (pin < mcp230xx_pincount) { - if (pin == 0) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; - } else { - validpin = true; - } - } - if (validpin) { - if (paramcount > 2) { - uint8_t intdef = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - if ((intdef >= 0) && (intdef <= 15)) { - Settings.mcp230xx_config[pin].int_report_defer=intdef; - if (Settings.mcp230xx_config[pin].int_count_en) { - Settings.mcp230xx_config[pin].int_count_en=0; - MCP230xx_CheckForIntCounter(); - AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTCNT for pin D%i"),pin); - } - Response_P(MCP230XX_INTCFG_RESPONSE,"DEF",pin,Settings.mcp230xx_config[pin].int_report_defer); - return serviced; - } else { - serviced=false; - return serviced; - } - } else { - Response_P(MCP230XX_INTCFG_RESPONSE,"DEF",pin,Settings.mcp230xx_config[pin].int_report_defer); - return serviced; - } - } - serviced = false; - return serviced; - } else { - serviced = false; - return serviced; - } - } - - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTCNT")) { - if (paramcount > 1) { - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if (pin < mcp230xx_pincount) { - if (pin == 0) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; - } else { - validpin = true; - } - } - if (validpin) { - if (paramcount > 2) { - uint8_t intcnt = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - if ((intcnt >= 0) && (intcnt <= 1)) { - Settings.mcp230xx_config[pin].int_count_en=intcnt; - if (Settings.mcp230xx_config[pin].int_report_defer) { - Settings.mcp230xx_config[pin].int_report_defer=0; - AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled INTDEF for pin D%i"),pin); - } - if (Settings.mcp230xx_config[pin].int_report_mode < 3) { - Settings.mcp230xx_config[pin].int_report_mode=3; - AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - Disabled immediate interrupt/telemetry reporting for pin D%i"),pin); - } - if ((Settings.mcp230xx_config[pin].int_count_en) && (!Settings.mcp230xx_int_timer)) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("*** WARNING *** - INTCNT enabled for pin D%i but global INTTIMER is disabled!"),pin); - } - MCP230xx_CheckForIntCounter(); - Response_P(MCP230XX_INTCFG_RESPONSE,"CNT",pin,Settings.mcp230xx_config[pin].int_count_en); - return serviced; - } else { - serviced=false; - return serviced; - } - } else { - Response_P(MCP230XX_INTCFG_RESPONSE,"CNT",pin,Settings.mcp230xx_config[pin].int_count_en); - return serviced; - } - } - serviced = false; - return serviced; - } else { - serviced = false; - return serviced; - } - } - - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"INTRETAIN")) { - if (paramcount > 1) { - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - if (pin < mcp230xx_pincount) { - if (pin == 0) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0")) validpin=true; - } else { - validpin = true; - } - } - if (validpin) { - if (paramcount > 2) { - uint8_t int_retain = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - if ((int_retain >= 0) && (int_retain <= 1)) { - Settings.mcp230xx_config[pin].int_retain_flag=int_retain; - Response_P(MCP230XX_INTCFG_RESPONSE,"INT_RETAIN",pin,Settings.mcp230xx_config[pin].int_retain_flag); - MCP230xx_CheckForIntRetainer(); - return serviced; - } else { - serviced=false; - return serviced; - } - } else { - Response_P(MCP230XX_INTCFG_RESPONSE,"INT_RETAIN",pin,Settings.mcp230xx_config[pin].int_retain_flag); - return serviced; - } - } - serviced = false; - return serviced; - } else { - serviced = false; - return serviced; - } - } - - uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 1)); - - if (pin < mcp230xx_pincount) { - if (0 == pin) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1), "0")) validpin=true; - } else { - validpin=true; - } - } - if (validpin && (paramcount > 1)) { - if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "?")) { - uint8_t port = 0; - if (pin > 7) { port = 1; } - uint8_t portdata = MCP230xx_readGPIO(port); - char pulluptxtr[7],pinstatustxtr[7]; - char intmodetxt[9]; - sprintf(intmodetxt,IntModeTxt(Settings.mcp230xx_config[pin].int_report_mode)); - sprintf(pulluptxtr,ConvertNumTxt(Settings.mcp230xx_config[pin].pullup)); -#ifdef USE_MCP230xx_OUTPUT - uint8_t pinmod = Settings.mcp230xx_config[pin].pinmode; - sprintf(pinstatustxtr,ConvertNumTxt(portdata>>(pin-(port*8))&1,pinmod)); - Response_P(MCP230XX_SENSOR_RESPONSE,pin,pinmod,pulluptxtr,intmodetxt,pinstatustxtr); -#else - sprintf(pinstatustxtr,ConvertNumTxt(portdata>>(pin-(port*8))&1)); - Response_P(MCP230XX_SENSOR_RESPONSE,pin,Settings.mcp230xx_config[pin].pinmode,pulluptxtr,intmodetxt,pinstatustxtr); -#endif - return serviced; - } -#ifdef USE_MCP230xx_OUTPUT - if (Settings.mcp230xx_config[pin].pinmode >= 5) { - uint8_t pincmd = Settings.mcp230xx_config[pin].pinmode - 5; - if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "ON")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "1"))) { - MCP230xx_SetOutPin(pin,abs(pincmd-1)); - return serviced; - } - if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "OFF")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "0"))) { - MCP230xx_SetOutPin(pin,pincmd); - return serviced; - } - if ((!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "T")) || (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 2), "2"))) { - MCP230xx_SetOutPin(pin,2); - return serviced; - } - } -#endif - uint8_t pinmode = 0; - uint8_t pullup = 0; - uint8_t intmode = 0; - if (paramcount > 1) { - pinmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); - } - if (paramcount > 2) { - pullup = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3)); - } - if (paramcount > 3) { - intmode = atoi(subStr(sub_string, XdrvMailbox.data, ",", 4)); - } -#ifdef USE_MCP230xx_OUTPUT - if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 7) && (pullup < 2) && (paramcount > 2)) { -#else - if ((pin < mcp230xx_pincount) && (pinmode > 0) && (pinmode < 5) && (pullup < 2) && (paramcount > 2)) { -#endif - Settings.mcp230xx_config[pin].pinmode=pinmode; - Settings.mcp230xx_config[pin].pullup=pullup; - if ((pinmode > 1) && (pinmode < 5)) { - if ((intmode >= 0) && (intmode <= 3)) { - Settings.mcp230xx_config[pin].int_report_mode=intmode; - } - } else { - Settings.mcp230xx_config[pin].int_report_mode=3; - } - MCP230xx_ApplySettings(); - uint8_t port = 0; - if (pin > 7) { port = 1; } - uint8_t portdata = MCP230xx_readGPIO(port); - char pulluptxtc[7], pinstatustxtc[7]; - char intmodetxt[9]; - sprintf(pulluptxtc,ConvertNumTxt(pullup)); - sprintf(intmodetxt,IntModeTxt(Settings.mcp230xx_config[pin].int_report_mode)); -#ifdef USE_MCP230xx_OUTPUT - sprintf(pinstatustxtc,ConvertNumTxt(portdata>>(pin-(port*8))&1,Settings.mcp230xx_config[pin].pinmode)); -#else - sprintf(pinstatustxtc,ConvertNumTxt(portdata>>(pin-(port*8))&1)); -#endif - Response_P(MCP230XX_SENSOR_RESPONSE,pin,pinmode,pulluptxtc,intmodetxt,pinstatustxtc); - return serviced; - } - } else { - serviced=false; - return serviced; - } - return serviced; -} - -#ifdef USE_MCP230xx_DISPLAYOUTPUT - -const char HTTP_SNS_MCP230xx_OUTPUT[] PROGMEM = "{s}MCP230XX D%d{m}%s{e}"; - -void MCP230xx_UpdateWebData(void) -{ - uint8_t gpio1 = MCP230xx_readGPIO(0); - uint8_t gpio2 = 0; - if (2 == mcp230xx_type) { - gpio2 = MCP230xx_readGPIO(1); - } - uint16_t gpio = (gpio2 << 8) + gpio1; - for (uint32_t pin = 0; pin < mcp230xx_pincount; pin++) { - if (Settings.mcp230xx_config[pin].pinmode >= 5) { - char stt[7]; - sprintf(stt,ConvertNumTxt((gpio>>pin)&1,Settings.mcp230xx_config[pin].pinmode)); - WSContentSend_PD(HTTP_SNS_MCP230xx_OUTPUT, pin, stt); - } - } -} - -#endif - -#ifdef USE_MCP230xx_OUTPUT - -void MCP230xx_OutputTelemetry(void) -{ - uint8_t outputcount = 0; - uint16_t gpiototal = 0; - uint8_t gpioa = 0; - uint8_t gpiob = 0; - gpioa=MCP230xx_readGPIO(0); - if (2 == mcp230xx_type) { gpiob=MCP230xx_readGPIO(1); } - gpiototal=((uint16_t)gpiob << 8) | gpioa; - for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { - if (Settings.mcp230xx_config[pinx].pinmode >= 5) outputcount++; - } - if (outputcount) { - char stt[7]; - ResponseTime_P(PSTR(",\"MCP230_OUT\":{")); - for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { - if (Settings.mcp230xx_config[pinx].pinmode >= 5) { - sprintf(stt,ConvertNumTxt(((gpiototal>>pinx)&1),Settings.mcp230xx_config[pinx].pinmode)); - ResponseAppend_P(PSTR("\"OUT_D%i\":\"%s\","),pinx,stt); - } - } - ResponseAppend_P(PSTR("\"END\":1}}")); - MqttPublishTeleSensor(); - } -} - -#endif - -void MCP230xx_Interrupt_Counter_Report(void) { - ResponseTime_P(PSTR(",\"MCP230_INTTIMER\":{")); - for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { - if (Settings.mcp230xx_config[pinx].int_count_en) { - ResponseAppend_P(PSTR("\"INTCNT_D%i\":%i,"),pinx,mcp230xx_int_counter[pinx]); - mcp230xx_int_counter[pinx]=0; - } - } - ResponseAppend_P(PSTR("\"END\":1}}")); - MqttPublishTeleSensor(); - mcp230xx_int_sec_counter = 0; -} - -void MCP230xx_Interrupt_Retain_Report(void) { - uint16_t retainresult = 0; - ResponseTime_P(PSTR(",\"MCP_INTRETAIN\":{")); - for (uint32_t pinx = 0;pinx < mcp230xx_pincount;pinx++) { - if (Settings.mcp230xx_config[pinx].int_retain_flag) { - ResponseAppend_P(PSTR("\"D%i\":%i,"),pinx,mcp230xx_int_retainer[pinx]); - retainresult |= (((mcp230xx_int_retainer[pinx])&1) << pinx); - mcp230xx_int_retainer[pinx]=0; - } - } - ResponseAppend_P(PSTR("\"Value\":%u}}"),retainresult); - MqttPublishTeleSensor(); -} - - - - - -bool Xsns29(uint8_t function) -{ - if (!I2cEnabled(XI2C_22)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - MCP230xx_Detect(); - } - else if (mcp230xx_type) { - switch (function) { - case FUNC_EVERY_50_MSECOND: - if (mcp230xx_int_en) { - mcp230xx_int_prio_counter++; - if ((mcp230xx_int_prio_counter) >= (Settings.mcp230xx_int_prio)) { - MCP230xx_CheckForInterrupt(); - mcp230xx_int_prio_counter=0; - } - } - break; - case FUNC_EVERY_SECOND: - if (mcp230xx_int_counter_en) { - mcp230xx_int_sec_counter++; - if (mcp230xx_int_sec_counter >= Settings.mcp230xx_int_timer) { - MCP230xx_Interrupt_Counter_Report(); - } - } - if (tele_period == 0) { - if (mcp230xx_int_retainer_en) { - MCP230xx_Interrupt_Retain_Report(); - } -#ifdef USE_MCP230xx_OUTPUT - MCP230xx_OutputTelemetry(); -#endif - } - break; - case FUNC_JSON_APPEND: - MCP230xx_Show(1); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_29 == XdrvMailbox.index) { - result = MCP230xx_Command(); - } - break; -#ifdef USE_WEBSERVER -#ifdef USE_MCP230xx_OUTPUT -#ifdef USE_MCP230xx_DISPLAYOUTPUT - case FUNC_WEB_SENSOR: - MCP230xx_UpdateWebData(); - break; -#endif -#endif -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_30_mpr121.ino" -# 46 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_30_mpr121.ino" -#ifdef USE_I2C -#ifdef USE_MPR121 - - - - - -#define XSNS_30 30 -#define XI2C_23 23 - - - - - - - -#define MPR121_ELEX_REG 0x00 - - -#define MPR121_MHDR_REG 0x2B - - -#define MPR121_MHDR_VAL 0x01 - - -#define MPR121_NHDR_REG 0x2C - - -#define MPR121_NHDR_VAL 0x01 - - -#define MPR121_NCLR_REG 0x2D - - -#define MPR121_NCLR_VAL 0x0E - - -#define MPR121_MHDF_REG 0x2F - - -#define MPR121_MHDF_VAL 0x01 - - -#define MPR121_NHDF_REG 0x30 - - -#define MPR121_NHDF_VAL 0x05 - - -#define MPR121_NCLF_REG 0x31 - - -#define MPR121_NCLF_VAL 0x01 - - -#define MPR121_MHDPROXR_REG 0x36 - - -#define MPR121_MHDPROXR_VAL 0x3F - - -#define MPR121_NHDPROXR_REG 0x37 - - -#define MPR121_NHDPROXR_VAL 0x5F - - -#define MPR121_NCLPROXR_REG 0x38 - - -#define MPR121_NCLPROXR_VAL 0x04 - - -#define MPR121_FDLPROXR_REG 0x39 - - -#define MPR121_FDLPROXR_VAL 0x00 - - -#define MPR121_MHDPROXF_REG 0x3A - - -#define MPR121_MHDPROXF_VAL 0x01 - - -#define MPR121_NHDPROXF_REG 0x3B - - -#define MPR121_NHDPROXF_VAL 0x01 - - -#define MPR121_NCLPROXF_REG 0x3C - - -#define MPR121_NCLPROXF_VAL 0x1F - - -#define MPR121_FDLPROXF_REG 0x3D - - -#define MPR121_FDLPROXF_VAL 0x04 - - -#define MPR121_E0TTH_REG 0x41 - - -#define MPR121_E0TTH_VAL 12 - - -#define MPR121_E0RTH_REG 0x42 - - -#define MPR121_E0RTH_VAL 6 - - -#define MPR121_CDT_REG 0x5D - - -#define MPR121_CDT_VAL 0x20 - - -#define MPR121_ECR_REG 0x5E - - -#define MPR121_ECR_VAL 0x8F - - - -#define MPR121_SRST_REG 0x80 - - -#define MPR121_SRST_VAL 0x63 - - -#define BITC(sensor,position) ((pS->current[sensor] >> position) & 1) - - -#define BITP(sensor,position) ((pS->previous[sensor] >> position) & 1) -# 195 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_30_mpr121.ino" -typedef struct mpr121 mpr121; -struct mpr121 { - const uint8_t i2c_addr[4] = { 0x5A, 0x5B, 0x5C, 0x5D }; - const char id[4] = { 'A', 'B', 'C', 'D' }; - bool connected[4] = { false, false, false, false }; - bool running[4] = { false, false, false, false }; - uint16_t current[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; - uint16_t previous[4] = { 0x0000, 0x0000, 0x0000, 0x0000 }; -}; - -bool mpr21_found = false; -# 217 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_30_mpr121.ino" -void Mpr121Init(struct mpr121 *pS, bool initial) -{ - - for (uint32_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) { - - if (initial && I2cActive(pS->i2c_addr[i])) { continue; } - - - pS->connected[i] = (I2cWrite8(pS->i2c_addr[i], MPR121_SRST_REG, MPR121_SRST_VAL) - && (0x24 == I2cRead8(pS->i2c_addr[i], 0x5D))); - if (pS->connected[i]) { - - - mpr21_found = true; - char device_name[16]; - snprintf_P(device_name, sizeof(device_name), PSTR("MPR121(%c)"), pS->id[i]); - I2cSetActiveFound(pS->i2c_addr[i], device_name); - - - for (uint32_t j = 0; j < 13; j++) { - - - I2cWrite8(pS->i2c_addr[i], MPR121_E0TTH_REG + 2 * j, MPR121_E0TTH_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_E0RTH_REG + 2 * j, MPR121_E0RTH_VAL); - } - - - I2cWrite8(pS->i2c_addr[i], MPR121_MHDR_REG, MPR121_MHDR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NHDR_REG, MPR121_NHDR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NCLR_REG, MPR121_NCLR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_MHDF_REG, MPR121_MHDF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NHDF_REG, MPR121_NHDF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NCLF_REG, MPR121_NCLF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXR_REG, MPR121_MHDPROXR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXR_REG, MPR121_NHDPROXR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXR_REG, MPR121_NCLPROXR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXR_REG, MPR121_FDLPROXR_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_MHDPROXF_REG, MPR121_MHDPROXF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NHDPROXF_REG, MPR121_NHDPROXF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_NCLPROXF_REG, MPR121_NCLPROXF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_FDLPROXF_REG, MPR121_FDLPROXF_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_CDT_REG, MPR121_CDT_VAL); - - - I2cWrite8(pS->i2c_addr[i], MPR121_ECR_REG, MPR121_ECR_VAL); - - - pS->running[i] = (0x00 != I2cRead8(pS->i2c_addr[i], MPR121_ECR_REG)); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_I2C "MPR121%c: %sRunning"), pS->id[i], (pS->running[i]) ? "" : "NOT"); - - } else { - - - pS->running[i] = false; - } - } - - - if (!(pS->connected[0] || pS->connected[1] || pS->connected[2] - || pS->connected[3])) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C "MPR121: No sensors found")); - } -} -# 326 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_30_mpr121.ino" -void Mpr121Show(struct mpr121 *pS, uint8_t function) -{ - - - for (uint32_t i = 0; i < sizeof(pS->i2c_addr[i]); i++) { - - - if (pS->connected[i]) { - - - if (!I2cValidRead16LE(&pS->current[i], pS->i2c_addr[i], MPR121_ELEX_REG)) { - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Cannot read data!"), pS->id[i]); - Mpr121Init(pS, false); - return; - } - - if (BITC(i, 15)) { - - - I2cWrite8(pS->i2c_addr[i], MPR121_ELEX_REG, 0x00); - AddLog_P2(LOG_LEVEL_ERROR, PSTR(D_LOG_I2C "MPR121%c: ERROR: Excess current detected! Fix circuits if it happens repeatedly! Soft-resetting MPR121 ..."), pS->id[i]); - Mpr121Init(pS, false); - return; - } - } - - if (pS->running[i]) { - - - if (FUNC_JSON_APPEND == function) { - ResponseAppend_P(PSTR(",\"MPR121%c\":{"), pS->id[i]); - } - - for (uint32_t j = 0; j < 13; j++) { - - - if ((FUNC_EVERY_50_MSECOND == function) - && (BITC(i, j) != BITP(i, j))) { - Response_P(PSTR("{\"MPR121%c\":{\"Button%i\":%i}}"), pS->id[i], j, BITC(i, j)); - MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data); - } - -#ifdef USE_WEBSERVER - if (FUNC_WEB_SENSOR == function) { - WSContentSend_PD(PSTR("{s}MPR121%c Button%d{m}%d{e}"), pS->id[i], j, BITC(i, j)); - } -#endif - - - if (FUNC_JSON_APPEND == function) { - ResponseAppend_P(PSTR("%s\"Button%i\":%i"), (j > 0 ? "," : ""), j, BITC(i, j)); - } - } - - - pS->previous[i] = pS->current[i]; - - - if (FUNC_JSON_APPEND == function) { - ResponseJsonEnd(); - } - } - } -} -# 410 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_30_mpr121.ino" -bool Xsns30(uint8_t function) -{ - if (!I2cEnabled(XI2C_23)) { return false; } - - bool result = false; - - - static struct mpr121 mpr121; - - if (FUNC_INIT == function) { - - Mpr121Init(&mpr121, true); - } - else if (mpr21_found) { - - switch (function) { - - - case FUNC_EVERY_50_MSECOND: - Mpr121Show(&mpr121, FUNC_EVERY_50_MSECOND); - break; - - - case FUNC_JSON_APPEND: - Mpr121Show(&mpr121, FUNC_JSON_APPEND); - break; - -#ifdef USE_WEBSERVER - - case FUNC_WEB_SENSOR: - Mpr121Show(&mpr121, FUNC_WEB_SENSOR); - break; -#endif - } - } - - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_31_ccs811.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_31_ccs811.ino" -#ifdef USE_I2C -#ifdef USE_CCS811 -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_31_ccs811.ino" -#define XSNS_31 31 -#define XI2C_24 24 - -#define EVERYNSECONDS 5 - -#include "Adafruit_CCS811.h" - -Adafruit_CCS811 ccs; -uint8_t CCS811_ready = 0; -uint8_t CCS811_type = 0;; -uint16_t eCO2; -uint16_t TVOC; -uint8_t tcnt = 0; -uint8_t ecnt = 0; - - - -void CCS811Detect(void) -{ - if (I2cActive(CCS811_ADDRESS)) { return; } - - if (!ccs.begin(CCS811_ADDRESS)) { - CCS811_type = 1; - I2cSetActiveFound(CCS811_ADDRESS, "CCS811"); - } -} - -void CCS811Update(void) -{ - tcnt++; - if (tcnt >= EVERYNSECONDS) { - tcnt = 0; - CCS811_ready = 0; - if (ccs.available()) { - if (!ccs.readData()){ - TVOC = ccs.getTVOC(); - eCO2 = ccs.geteCO2(); - CCS811_ready = 1; - if (global_update && global_humidity>0 && global_temperature!=9999) { ccs.setEnvironmentalData((uint8_t)global_humidity, global_temperature); } - ecnt = 0; - } - } else { - - ecnt++; - if (ecnt > 6) { - - ccs.begin(CCS811_ADDRESS); - } - } - } -} - -const char HTTP_SNS_CCS811[] PROGMEM = - "{s}CCS811 " D_ECO2 "{m}%d " D_UNIT_PARTS_PER_MILLION "{e}" - "{s}CCS811 " D_TVOC "{m}%d " D_UNIT_PARTS_PER_BILLION "{e}"; - -void CCS811Show(bool json) -{ - if (CCS811_ready) { - if (json) { - ResponseAppend_P(PSTR(",\"CCS811\":{\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TVOC "\":%d}"), eCO2,TVOC); -#ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, eCO2); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_CCS811, eCO2, TVOC); -#endif - } - } -} - - - - - -bool Xsns31(uint8_t function) -{ - if (!I2cEnabled(XI2C_24)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - CCS811Detect(); - } - else if (CCS811_type) { - switch (function) { - case FUNC_EVERY_SECOND: - CCS811Update(); - break; - case FUNC_JSON_APPEND: - CCS811Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - CCS811Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_32_mpu6050.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_32_mpu6050.ino" -#ifdef USE_I2C -#ifdef USE_MPU6050 -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_32_mpu6050.ino" -#define XSNS_32 32 -#define XI2C_25 25 - -#define D_SENSOR_MPU6050 "MPU6050" - -#define MPU_6050_ADDR_AD0_LOW 0x68 -#define MPU_6050_ADDR_AD0_HIGH 0x69 - -uint8_t MPU_6050_address; -uint8_t MPU_6050_addresses[] = { MPU_6050_ADDR_AD0_LOW, MPU_6050_ADDR_AD0_HIGH }; -uint8_t MPU_6050_found; - -int16_t MPU_6050_ax = 0, MPU_6050_ay = 0, MPU_6050_az = 0; -int16_t MPU_6050_gx = 0, MPU_6050_gy = 0, MPU_6050_gz = 0; -int16_t MPU_6050_temperature = 0; - -#ifdef USE_MPU6050_DMP - #include "MPU6050_6Axis_MotionApps20.h" - #include "I2Cdev.h" - #include - typedef struct MPU6050_DMP{ - uint8_t devStatus; - uint16_t packetSize; - uint16_t fifoCount; - uint8_t fifoBuffer[64]; - Quaternion q; - VectorInt16 aa; - VectorInt16 aaReal; - VectorFloat gravity; - float euler[3]; - float yawPitchRoll[3]; - } MPU6050_DMP; - - MPU6050_DMP MPU6050_dmp; -#else - #include -#endif -MPU6050 mpu6050; - -void MPU_6050PerformReading(void) -{ -#ifdef USE_MPU6050_DMP - mpu6050.resetFIFO(); - MPU6050_dmp.fifoCount = mpu6050.getFIFOCount(); - while (MPU6050_dmp.fifoCount < MPU6050_dmp.packetSize) MPU6050_dmp.fifoCount = mpu6050.getFIFOCount(); - mpu6050.getFIFOBytes(MPU6050_dmp.fifoBuffer, MPU6050_dmp.packetSize); - MPU6050_dmp.fifoCount -= MPU6050_dmp.packetSize; - - mpu6050.dmpGetQuaternion(&MPU6050_dmp.q, MPU6050_dmp.fifoBuffer); - mpu6050.dmpGetEuler(MPU6050_dmp.euler, &MPU6050_dmp.q); - mpu6050.dmpGetAccel(&MPU6050_dmp.aa, MPU6050_dmp.fifoBuffer); - mpu6050.dmpGetGravity(&MPU6050_dmp.gravity, &MPU6050_dmp.q); - mpu6050.dmpGetLinearAccel(&MPU6050_dmp.aaReal, &MPU6050_dmp.aa, &MPU6050_dmp.gravity); - mpu6050.dmpGetYawPitchRoll(MPU6050_dmp.yawPitchRoll, &MPU6050_dmp.q, &MPU6050_dmp.gravity); - MPU_6050_gx = MPU6050_dmp.euler[0] * 180/M_PI; - MPU_6050_gy = MPU6050_dmp.euler[1] * 180/M_PI; - MPU_6050_gz = MPU6050_dmp.euler[2] * 180/M_PI; - MPU_6050_ax = MPU6050_dmp.aaReal.x; - MPU_6050_ay = MPU6050_dmp.aaReal.y; - MPU_6050_az = MPU6050_dmp.aaReal.z; -#else - mpu6050.getMotion6( - &MPU_6050_ax, - &MPU_6050_ay, - &MPU_6050_az, - &MPU_6050_gx, - &MPU_6050_gy, - &MPU_6050_gz - ); -#endif - MPU_6050_temperature = mpu6050.getTemperature(); -} -# 119 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_32_mpu6050.ino" -void MPU_6050Detect(void) -{ - for (uint32_t i = 0; i < sizeof(MPU_6050_addresses); i++) - { - MPU_6050_address = MPU_6050_addresses[i]; - if (!I2cSetDevice(MPU_6050_address)) { break; } - mpu6050.setAddr(MPU_6050_addresses[i]); - -#ifdef USE_MPU6050_DMP - MPU6050_dmp.devStatus = mpu6050.dmpInitialize(); - mpu6050.setXGyroOffset(220); - mpu6050.setYGyroOffset(76); - mpu6050.setZGyroOffset(-85); - mpu6050.setZAccelOffset(1788); - if (MPU6050_dmp.devStatus == 0) { - mpu6050.setDMPEnabled(true); - MPU6050_dmp.packetSize = mpu6050.dmpGetFIFOPacketSize(); - MPU_6050_found = true; - } -#else - mpu6050.initialize(); - MPU_6050_found = mpu6050.testConnection(); -#endif - Settings.flag2.axis_resolution = 2; - } - - if (MPU_6050_found) { - I2cSetActiveFound(MPU_6050_address, D_SENSOR_MPU6050); - } -} - -#define D_YAW "Yaw" -#define D_PITCH "Pitch" -#define D_ROLL "Roll" - -#ifdef USE_WEBSERVER -const char HTTP_SNS_AXIS[] PROGMEM = - "{s}" D_SENSOR_MPU6050 " " D_AX_AXIS "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_AY_AXIS "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_AZ_AXIS "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_GX_AXIS "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_GY_AXIS "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_GZ_AXIS "{m}%s{e}"; -#ifdef USE_MPU6050_DMP -const char HTTP_SNS_YPR[] PROGMEM = - "{s}" D_SENSOR_MPU6050 " " D_YAW "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_PITCH "{m}%s{e}" - "{s}" D_SENSOR_MPU6050 " " D_ROLL "{m}%s{e}"; -#endif -#endif - -#define D_JSON_AXIS_AX "AccelXAxis" -#define D_JSON_AXIS_AY "AccelYAxis" -#define D_JSON_AXIS_AZ "AccelZAxis" -#define D_JSON_AXIS_GX "GyroXAxis" -#define D_JSON_AXIS_GY "GyroYAxis" -#define D_JSON_AXIS_GZ "GyroZAxis" -#define D_JSON_YAW "Yaw" -#define D_JSON_PITCH "Pitch" -#define D_JSON_ROLL "Roll" - -void MPU_6050Show(bool json) -{ - MPU_6050PerformReading(); - - double tempConv = (MPU_6050_temperature / 340.0 + 35.53); - char temperature[33]; - dtostrfd(tempConv, Settings.flag2.temperature_resolution, temperature); - char axis_ax[33]; - dtostrfd(MPU_6050_ax, Settings.flag2.axis_resolution, axis_ax); - char axis_ay[33]; - dtostrfd(MPU_6050_ay, Settings.flag2.axis_resolution, axis_ay); - char axis_az[33]; - dtostrfd(MPU_6050_az, Settings.flag2.axis_resolution, axis_az); - char axis_gx[33]; - dtostrfd(MPU_6050_gx, Settings.flag2.axis_resolution, axis_gx); - char axis_gy[33]; - dtostrfd(MPU_6050_gy, Settings.flag2.axis_resolution, axis_gy); - char axis_gz[33]; - dtostrfd(MPU_6050_gz, Settings.flag2.axis_resolution, axis_gz); -#ifdef USE_MPU6050_DMP - char axis_yaw[33]; - dtostrfd(MPU6050_dmp.yawPitchRoll[0] / PI * 180.0, Settings.flag2.axis_resolution, axis_yaw); - char axis_pitch[33]; - dtostrfd(MPU6050_dmp.yawPitchRoll[1] / PI * 180.0, Settings.flag2.axis_resolution, axis_pitch); - char axis_roll[33]; - dtostrfd(MPU6050_dmp.yawPitchRoll[2] / PI * 180.0, Settings.flag2.axis_resolution, axis_roll); -#endif - - if (json) { - char json_axis_ax[25]; - snprintf_P(json_axis_ax, sizeof(json_axis_ax), PSTR(",\"" D_JSON_AXIS_AX "\":%s"), axis_ax); - char json_axis_ay[25]; - snprintf_P(json_axis_ay, sizeof(json_axis_ay), PSTR(",\"" D_JSON_AXIS_AY "\":%s"), axis_ay); - char json_axis_az[25]; - snprintf_P(json_axis_az, sizeof(json_axis_az), PSTR(",\"" D_JSON_AXIS_AZ "\":%s"), axis_az); - char json_axis_gx[25]; - snprintf_P(json_axis_gx, sizeof(json_axis_gx), PSTR(",\"" D_JSON_AXIS_GX "\":%s"), axis_gx); - char json_axis_gy[25]; - snprintf_P(json_axis_gy, sizeof(json_axis_gy), PSTR(",\"" D_JSON_AXIS_GY "\":%s"), axis_gy); - char json_axis_gz[25]; - snprintf_P(json_axis_gz, sizeof(json_axis_gz), PSTR(",\"" D_JSON_AXIS_GZ "\":%s"), axis_gz); -#ifdef USE_MPU6050_DMP - char json_ypr_y[25]; - snprintf_P(json_ypr_y, sizeof(json_ypr_y), PSTR(",\"" D_JSON_YAW "\":%s"), axis_yaw); - char json_ypr_p[25]; - snprintf_P(json_ypr_p, sizeof(json_ypr_p), PSTR(",\"" D_JSON_PITCH "\":%s"), axis_pitch); - char json_ypr_r[25]; - snprintf_P(json_ypr_r, sizeof(json_ypr_r), PSTR(",\"" D_JSON_ROLL "\":%s"), axis_roll); - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s%s%s%s}"), - D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz, - json_ypr_y, json_ypr_p, json_ypr_r); -#else - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s}"), - D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz); -#endif -#ifdef USE_DOMOTICZ - DomoticzSensor(DZ_TEMP, temperature); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, D_SENSOR_MPU6050, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_AXIS, axis_ax, axis_ay, axis_az, axis_gx, axis_gy, axis_gz); -#ifdef USE_MPU6050_DMP - WSContentSend_PD(HTTP_SNS_YPR, axis_yaw, axis_pitch, axis_roll); -#endif -#endif - } -} - - - - - -bool Xsns32(uint8_t function) -{ - if (!I2cEnabled(XI2C_25)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - MPU_6050Detect(); - } - else if (MPU_6050_found) { - switch (function) { - case FUNC_EVERY_SECOND: - if (tele_period == Settings.tele_period -3) { - MPU_6050PerformReading(); - } - break; - case FUNC_JSON_APPEND: - MPU_6050Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MPU_6050Show(0); - MPU_6050PerformReading(); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_33_ds3231.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_33_ds3231.ino" -#ifdef USE_I2C -#ifdef USE_DS3231 -# 35 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_33_ds3231.ino" -#define XSNS_33 33 -#define XI2C_26 26 - - -#ifndef USE_RTC_ADDR -#define USE_RTC_ADDR 0x68 -#endif - - -#define RTC_SECONDS 0x00 -#define RTC_MINUTES 0x01 -#define RTC_HOURS 0x02 -#define RTC_DAY 0x03 -#define RTC_DATE 0x04 -#define RTC_MONTH 0x05 -#define RTC_YEAR 0x06 -#define RTC_CONTROL 0x0E -#define RTC_STATUS 0x0F - -#define OSF 7 -#define EOSC 7 -#define BBSQW 6 -#define CONV 5 -#define RS2 4 -#define RS1 3 -#define INTCN 2 - - -#define HR1224 6 -#define CENTURY 7 -#define DYDT 6 -bool ds3231ReadStatus = false; -bool ds3231WriteStatus = false; -bool DS3231chipDetected = false; - - - - -void DS3231Detect(void) -{ - if (I2cActive(USE_RTC_ADDR)) { return; } - - if (I2cValidRead(USE_RTC_ADDR, RTC_STATUS, 1)) { - I2cSetActiveFound(USE_RTC_ADDR, "DS3231"); - DS3231chipDetected = true; - } -} - - - - -uint8_t bcd2dec(uint8_t n) -{ - return n - 6 * (n >> 4); -} - - - - -uint8_t dec2bcd(uint8_t n) -{ - return n + 6 * (n / 10); -} - - - - -uint32_t ReadFromDS3231(void) -{ - TIME_T tm; - tm.second = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_SECONDS)); - tm.minute = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MINUTES)); - tm.hour = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_HOURS) & ~_BV(HR1224)); - tm.day_of_week = I2cRead8(USE_RTC_ADDR, RTC_DAY); - tm.day_of_month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_DATE)); - tm.month = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_MONTH) & ~_BV(CENTURY)); - tm.year = bcd2dec(I2cRead8(USE_RTC_ADDR, RTC_YEAR)); - return MakeTime(tm); -} - - - -void SetDS3231Time (uint32_t epoch_time) { - TIME_T tm; - BreakTime(epoch_time, tm); - I2cWrite8(USE_RTC_ADDR, RTC_SECONDS, dec2bcd(tm.second)); - I2cWrite8(USE_RTC_ADDR, RTC_MINUTES, dec2bcd(tm.minute)); - I2cWrite8(USE_RTC_ADDR, RTC_HOURS, dec2bcd(tm.hour)); - I2cWrite8(USE_RTC_ADDR, RTC_DAY, tm.day_of_week); - I2cWrite8(USE_RTC_ADDR, RTC_DATE, dec2bcd(tm.day_of_month)); - I2cWrite8(USE_RTC_ADDR, RTC_MONTH, dec2bcd(tm.month)); - I2cWrite8(USE_RTC_ADDR, RTC_YEAR, dec2bcd(tm.year)); - I2cWrite8(USE_RTC_ADDR, RTC_STATUS, I2cRead8(USE_RTC_ADDR, RTC_STATUS) & ~_BV(OSF)); -} - -void DS3231EverySecond(void) -{ - TIME_T tmpTime; - if (!ds3231ReadStatus && Rtc.utc_time < START_VALID_TIME ) { - ntp_force_sync = true; - Rtc.utc_time = ReadFromDS3231(); - - - BreakTime(Rtc.utc_time, tmpTime); - if (Rtc.utc_time < START_VALID_TIME ) { - ds3231ReadStatus = true; - } - RtcTime.year = tmpTime.year + 1970; - Rtc.daylight_saving_time = RuleToTime(Settings.tflag[1], RtcTime.year); - Rtc.standard_time = RuleToTime(Settings.tflag[0], RtcTime.year); - AddLog_P2(LOG_LEVEL_INFO, PSTR("Set time from DS3231 to RTC (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), - GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); - if (Rtc.local_time < START_VALID_TIME) { - rules_flag.time_init = 1; - } else { - rules_flag.time_set = 1; - } - } - else if (!ds3231WriteStatus && Rtc.utc_time > START_VALID_TIME && abs(Rtc.utc_time - ReadFromDS3231()) > 60) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("Write Time TO DS3231 from NTP (" D_UTC_TIME ") %s, (" D_DST_TIME ") %s, (" D_STD_TIME ") %s"), - GetTime(0).c_str(), GetTime(2).c_str(), GetTime(3).c_str()); - SetDS3231Time (Rtc.utc_time); - ds3231WriteStatus = true; - } -} - - - - - -bool Xsns33(uint8_t function) -{ - if (!I2cEnabled(XI2C_26)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - DS3231Detect(); - } - else if (DS3231chipDetected) { - switch (function) { - case FUNC_EVERY_SECOND: - DS3231EverySecond(); - break; - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_34_hx711.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_34_hx711.ino" -#ifdef USE_HX711 -# 35 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_34_hx711.ino" -#define XSNS_34 34 - -#ifndef HX_MAX_WEIGHT -#define HX_MAX_WEIGHT 20000 -#endif -#ifndef HX_REFERENCE -#define HX_REFERENCE 250 -#endif -#ifndef HX_SCALE -#define HX_SCALE 120 -#endif - -#define HX_TIMEOUT 120 -#define HX_SAMPLES 10 -#define HX_CAL_TIMEOUT 15 - -#define HX_GAIN_128 1 -#define HX_GAIN_32 2 -#define HX_GAIN_64 3 - -#define D_JSON_WEIGHT_REF "WeightRef" -#define D_JSON_WEIGHT_CAL "WeightCal" -#define D_JSON_WEIGHT_MAX "WeightMax" -#define D_JSON_WEIGHT_ITEM "WeightItem" -#define D_JSON_WEIGHT_CHANGE "WeightChange" -#define D_JSON_WEIGHT_RAW "WeightRaw" -#define D_JSON_WEIGHT_DELTA "WeightDelta" - -enum HxCalibrationSteps { HX_CAL_END, HX_CAL_LIMBO, HX_CAL_FINISH, HX_CAL_FAIL, HX_CAL_DONE, HX_CAL_FIRST, HX_CAL_RESET, HX_CAL_START }; - -const char kHxCalibrationStates[] PROGMEM = D_HX_CAL_FAIL "|" D_HX_CAL_DONE "|" D_HX_CAL_REFERENCE "|" D_HX_CAL_REMOVE; - -struct HX { - long weight = 0; - long raw = 0; - long last_weight = 0; - long sum_weight = 0; - long sum_raw = 0; - long offset = 0; - long scale = 1; - long weight_diff = 0; - uint8_t type = 1; - uint8_t sample_count = 0; - uint8_t calibrate_step = HX_CAL_END; - uint8_t calibrate_timer = 0; - uint8_t calibrate_msg = 0; - uint8_t pin_sck; - uint8_t pin_dout; - bool tare_flg = false; - bool weight_changed = false; - uint16_t weight_delta = 4; -} Hx; - - - -bool HxIsReady(uint16_t timeout) -{ - - uint32_t start = millis(); - while ((digitalRead(Hx.pin_dout) == HIGH) && (millis() - start < timeout)) { yield(); } - return (digitalRead(Hx.pin_dout) == LOW); -} - -long HxRead(void) -{ - if (!HxIsReady(HX_TIMEOUT)) { return -1; } - - uint8_t data[3] = { 0 }; - uint8_t filler = 0x00; - - - data[2] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); - data[1] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); - data[0] = shiftIn(Hx.pin_dout, Hx.pin_sck, MSBFIRST); - - - for (unsigned int i = 0; i < HX_GAIN_128; i++) { - digitalWrite(Hx.pin_sck, HIGH); - digitalWrite(Hx.pin_sck, LOW); - } - - - if (data[2] & 0x80) { filler = 0xFF; } - - - unsigned long value = ( static_cast(filler) << 24 - | static_cast(data[2]) << 16 - | static_cast(data[1]) << 8 - | static_cast(data[0]) ); - - return static_cast(value); -} - - - -void HxResetPart(void) -{ - Hx.tare_flg = true; - Hx.sum_weight = 0; - Hx.sample_count = 0; - Hx.last_weight = 0; -} - -void HxReset(void) -{ - HxResetPart(); - Settings.energy_frequency_calibration = 0; -} - -void HxCalibrationStateTextJson(uint8_t msg_id) -{ - char cal_text[30]; - - Hx.calibrate_msg = msg_id; - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, GetTextIndexed(cal_text, sizeof(cal_text), Hx.calibrate_msg, kHxCalibrationStates)); - - if (msg_id < 3) { MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("Sensor34")); } -} - -void SetWeightDelta() -{ - - if (Settings.weight_change == 0) { - Hx.weight_delta = 4; - return; - } - - - if (Settings.weight_change > 100) { - Hx.weight_delta = (Settings.weight_change - 100) * 10 + 100; - return; - } - - - Hx.weight_delta = Settings.weight_change - 1; -} -# 192 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_34_hx711.ino" -bool HxCommand(void) -{ - bool serviced = true; - bool show_parms = false; - char sub_string[XdrvMailbox.data_len +1]; - - for (uint32_t ca = 0; ca < XdrvMailbox.data_len; ca++) { - if ((' ' == XdrvMailbox.data[ca]) || ('=' == XdrvMailbox.data[ca])) { XdrvMailbox.data[ca] = ','; } - } - - switch (XdrvMailbox.payload) { - case 1: - HxReset(); - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, "Reset"); - break; - case 2: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - } - Hx.scale = 1; - HxReset(); - Hx.calibrate_step = HX_CAL_START; - Hx.calibrate_timer = 1; - HxCalibrationStateTextJson(3); - break; - case 3: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_reference = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - } - show_parms = true; - break; - case 4: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_calibration = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - Hx.scale = Settings.weight_calibration; - } - show_parms = true; - break; - case 5: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_max = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) / 1000; - } - show_parms = true; - break; - case 6: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_item = (unsigned long)(CharToFloat(subStr(sub_string, XdrvMailbox.data, ",", 2)) * 10); - } - show_parms = true; - break; - case 7: - Settings.energy_frequency_calibration = Hx.weight; - Response_P(S_JSON_SENSOR_INDEX_SVALUE, XSNS_34, D_JSON_DONE); - break; - case 8: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.SensorBits1.hx711_json_weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10) & 1; - } - show_parms = true; - break; - case 9: - if (strstr(XdrvMailbox.data, ",") != nullptr) { - Settings.weight_change = strtol(subStr(sub_string, XdrvMailbox.data, ",", 2), nullptr, 10); - SetWeightDelta(); - } - show_parms = true; - break; - default: - show_parms = true; - } - - if (show_parms) { - char item[33]; - dtostrfd((float)Settings.weight_item / 10, 1, item); - Response_P(PSTR("{\"Sensor34\":{\"" D_JSON_WEIGHT_REF "\":%d,\"" D_JSON_WEIGHT_CAL "\":%d,\"" D_JSON_WEIGHT_MAX "\":%d,\"" - D_JSON_WEIGHT_ITEM "\":%s,\"" D_JSON_WEIGHT_CHANGE "\":%s,\"" D_JSON_WEIGHT_DELTA "\":%d}}"), - Settings.weight_reference, Settings.weight_calibration, Settings.weight_max * 1000, - item, GetStateText(Settings.SensorBits1.hx711_json_weight_change), Settings.weight_change); - } - - return serviced; -} - - - -long HxWeight(void) -{ - return (Hx.calibrate_step < HX_CAL_FAIL) ? Hx.weight : 0; -} - -void HxInit(void) -{ - Hx.type = 0; - if ((pin[GPIO_HX711_DAT] < 99) && (pin[GPIO_HX711_SCK] < 99)) { - Hx.pin_sck = pin[GPIO_HX711_SCK]; - Hx.pin_dout = pin[GPIO_HX711_DAT]; - - pinMode(Hx.pin_sck, OUTPUT); - pinMode(Hx.pin_dout, INPUT); - - digitalWrite(Hx.pin_sck, LOW); - - SetWeightDelta(); - - if (HxIsReady(8 * HX_TIMEOUT)) { - if (!Settings.weight_max) { Settings.weight_max = HX_MAX_WEIGHT / 1000; } - if (!Settings.weight_calibration) { Settings.weight_calibration = HX_SCALE; } - if (!Settings.weight_reference) { Settings.weight_reference = HX_REFERENCE; } - Hx.scale = Settings.weight_calibration; - HxRead(); - HxResetPart(); - Hx.type = 1; - } - } -} - -void HxEvery100mSecond(void) -{ - long raw = HxRead(); - Hx.sum_raw += raw; - Hx.sum_weight += raw; - - Hx.sample_count++; - if (HX_SAMPLES == Hx.sample_count) { - long average = Hx.sum_weight / Hx.sample_count; - long raw_average = Hx.sum_raw / Hx.sample_count; - long value = average - Hx.offset; - Hx.weight = value / Hx.scale; - Hx.raw = raw_average / Hx.scale; - if (Hx.weight < 0) { - if (Settings.energy_frequency_calibration) { - long difference = Settings.energy_frequency_calibration + Hx.weight; - Hx.last_weight = difference; - if (difference < 0) { HxReset(); } - } - Hx.weight = 0; - } else { - Hx.last_weight = Settings.energy_frequency_calibration; - } - - if (Hx.tare_flg) { - Hx.tare_flg = false; - Hx.offset = average; - } - - if (Hx.calibrate_step) { - Hx.calibrate_timer--; - - if (HX_CAL_START == Hx.calibrate_step) { - Hx.calibrate_step--; - Hx.calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); - } - else if (HX_CAL_RESET == Hx.calibrate_step) { - if (Hx.calibrate_timer) { - if (Hx.weight < (long)Settings.weight_reference) { - Hx.calibrate_step--; - Hx.calibrate_timer = HX_CAL_TIMEOUT * (10 / HX_SAMPLES); - HxCalibrationStateTextJson(2); - } - } else { - Hx.calibrate_step = HX_CAL_FAIL; - } - } - else if (HX_CAL_FIRST == Hx.calibrate_step) { - if (Hx.calibrate_timer) { - if (Hx.weight > (long)Settings.weight_reference) { - Hx.calibrate_step--; - } - } else { - Hx.calibrate_step = HX_CAL_FAIL; - } - } - else if (HX_CAL_DONE == Hx.calibrate_step) { - if (Hx.weight > (long)Settings.weight_reference) { - Hx.calibrate_step = HX_CAL_FINISH; - Settings.weight_calibration = Hx.weight / Settings.weight_reference; - Hx.weight = 0; - HxCalibrationStateTextJson(1); - } else { - Hx.calibrate_step = HX_CAL_FAIL; - } - } - - if (HX_CAL_FAIL == Hx.calibrate_step) { - Hx.calibrate_step--; - Hx.tare_flg = true; - HxCalibrationStateTextJson(0); - } - if (HX_CAL_FINISH == Hx.calibrate_step) { - Hx.calibrate_step--; - Hx.calibrate_timer = 3 * (10 / HX_SAMPLES); - Hx.scale = Settings.weight_calibration; - } - - if (!Hx.calibrate_timer) { - Hx.calibrate_step = HX_CAL_END; - } - } else { - Hx.weight += Hx.last_weight; - - if (Settings.SensorBits1.hx711_json_weight_change) { - if (abs(Hx.weight - Hx.weight_diff) > Hx.weight_delta) { - Hx.weight_diff = Hx.weight; - Hx.weight_changed = true; - } - else if (Hx.weight_changed && (Hx.weight == Hx.weight_diff)) { - mqtt_data[0] = '\0'; - ResponseAppendTime(); - HxShow(true); - ResponseJsonEnd(); - MqttPublishTeleSensor(); - Hx.weight_changed = false; - } - } - } - - Hx.sum_weight = 0; - Hx.sum_raw = 0; - Hx.sample_count = 0; - } -} - -void HxSaveBeforeRestart(void) -{ - Settings.energy_frequency_calibration = Hx.weight; - Hx.sample_count = HX_SAMPLES +1; -} - -#ifdef USE_WEBSERVER -const char HTTP_HX711_WEIGHT[] PROGMEM = - "{s}HX711 " D_WEIGHT "{m}%s " D_UNIT_KILOGRAM "{e}"; -const char HTTP_HX711_COUNT[] PROGMEM = - "{s}HX711 " D_COUNT "{m}%d{e}"; -const char HTTP_HX711_CAL[] PROGMEM = - "{s}HX711 %s{m}{e}"; -#endif - -void HxShow(bool json) -{ - char scount[30] = { 0 }; - - uint16_t count = 0; - float weight = 0; - if (Hx.calibrate_step < HX_CAL_FAIL) { - if (Hx.weight && Settings.weight_item) { - count = (Hx.weight * 10) / Settings.weight_item; - if (count > 1) { - snprintf_P(scount, sizeof(scount), PSTR(",\"" D_JSON_COUNT "\":%d"), count); - } - } - weight = (float)Hx.weight / 1000; - } - char weight_chr[33]; - dtostrfd(weight, Settings.flag2.weight_resolution, weight_chr); - - if (json) { - ResponseAppend_P(PSTR(",\"HX711\":{\"" D_JSON_WEIGHT "\":%s%s, \"" D_JSON_WEIGHT_RAW "\":%d}"), weight_chr, scount, Hx.raw); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_HX711_WEIGHT, weight_chr); - if (count > 1) { - WSContentSend_PD(HTTP_HX711_COUNT, count); - } - if (Hx.calibrate_step) { - char cal_text[30]; - WSContentSend_PD(HTTP_HX711_CAL, GetTextIndexed(cal_text, sizeof(cal_text), Hx.calibrate_msg, kHxCalibrationStates)); - } -#endif - } -} - -#ifdef USE_WEBSERVER -#ifdef USE_HX711_GUI - - - - -#define WEB_HANDLE_HX711 "s34" - -const char S_CONFIGURE_HX711[] PROGMEM = D_CONFIGURE_HX711; - -const char HTTP_BTN_MENU_MAIN_HX711[] PROGMEM = - "

"; - -const char HTTP_BTN_MENU_HX711[] PROGMEM = - "

"; - -const char HTTP_FORM_HX711[] PROGMEM = - "
 " D_CALIBRATION " " - "
" - "

" D_REFERENCE_WEIGHT " (" D_UNIT_KILOGRAM ")

" - "
" - "
" - "


" - - "
 " D_HX711_PARAMETERS " " - "
" - "

" D_ITEM_WEIGHT " (" D_UNIT_KILOGRAM ")

"; - -void HandleHxAction(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_HX711); - - if (WebServer->hasArg("save")) { - HxSaveSettings(); - HandleConfiguration(); - return; - } - - char stemp1[20]; - - if (WebServer->hasArg("reset")) { - snprintf_P(stemp1, sizeof(stemp1), PSTR("Sensor34 1")); - ExecuteWebCommand(stemp1, SRC_WEBGUI); - - HandleRoot(); - return; - } - - if (WebServer->hasArg("calibrate")) { - WebGetArg("p1", stemp1, sizeof(stemp1)); - Settings.weight_reference = (!strlen(stemp1)) ? 0 : (unsigned long)(CharToFloat(stemp1) * 1000); - - HxLogUpdates(); - - snprintf_P(stemp1, sizeof(stemp1), PSTR("Sensor34 2")); - ExecuteWebCommand(stemp1, SRC_WEBGUI); - - HandleRoot(); - return; - } - - WSContentStart_P(S_CONFIGURE_HX711); - WSContentSendStyle(); - dtostrfd((float)Settings.weight_reference / 1000, 3, stemp1); - char stemp2[20]; - dtostrfd((float)Settings.weight_item / 10000, 4, stemp2); - WSContentSend_P(HTTP_FORM_HX711, stemp1, stemp2); - WSContentSend_P(HTTP_FORM_END); - WSContentSpaceButton(BUTTON_CONFIGURATION); - WSContentStop(); -} - -void HxSaveSettings(void) -{ - char tmp[100]; - - WebGetArg("p2", tmp, sizeof(tmp)); - Settings.weight_item = (!strlen(tmp)) ? 0 : (unsigned long)(CharToFloat(tmp) * 10000); - - HxLogUpdates(); -} - -void HxLogUpdates(void) -{ - char weigth_ref_chr[33]; - dtostrfd((float)Settings.weight_reference / 1000, Settings.flag2.weight_resolution, weigth_ref_chr); - char weigth_item_chr[33]; - dtostrfd((float)Settings.weight_item / 10000, 4, weigth_item_chr); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_JSON_WEIGHT_REF " %s, " D_JSON_WEIGHT_ITEM " %s"), weigth_ref_chr, weigth_item_chr); -} - -#endif -#endif - - - - - -bool Xsns34(uint8_t function) -{ - bool result = false; - - if (Hx.type) { - switch (function) { - case FUNC_EVERY_100_MSECOND: - HxEvery100mSecond(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_34 == XdrvMailbox.index) { - result = HxCommand(); - } - break; - case FUNC_JSON_APPEND: - HxShow(1); - break; - case FUNC_SAVE_BEFORE_RESTART: - HxSaveBeforeRestart(); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - HxShow(0); - break; -#ifdef USE_HX711_GUI - case FUNC_WEB_ADD_MAIN_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_MAIN_HX711); - break; - case FUNC_WEB_ADD_BUTTON: - WSContentSend_P(HTTP_BTN_MENU_HX711); - break; - case FUNC_WEB_ADD_HANDLER: - WebServer->on("/" WEB_HANDLE_HX711, HandleHxAction); - break; -#endif -#endif - case FUNC_INIT: - HxInit(); - break; - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_35_tx20.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_35_tx20.ino" -#ifdef USE_TX20_WIND_SENSOR -# 29 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_35_tx20.ino" -#define XSNS_35 35 - -#define TX20_BIT_TIME 1220 -#define TX20_RESET_VALUES 60 - - - -extern "C" { -#include "gpio.h" -} - -#ifdef USE_WEBSERVER - -const char HTTP_SNS_TX20[] PROGMEM = - "{s}TX20 " D_TX20_WIND_SPEED "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" - "{s}TX20 " D_TX20_WIND_SPEED_AVG "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" - "{s}TX20 " D_TX20_WIND_SPEED_MAX "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" - "{s}TX20 " D_TX20_WIND_DIRECTION "{m}%s{e}"; - -#endif - -const char kTx20Directions[] PROGMEM = D_TX20_NORTH "|" - D_TX20_NORTH D_TX20_NORTH D_TX20_EAST "|" - D_TX20_NORTH D_TX20_EAST "|" - D_TX20_EAST D_TX20_NORTH D_TX20_EAST "|" - D_TX20_EAST "|" - D_TX20_EAST D_TX20_SOUTH D_TX20_EAST "|" - D_TX20_SOUTH D_TX20_EAST "|" - D_TX20_SOUTH D_TX20_SOUTH D_TX20_EAST "|" - D_TX20_SOUTH "|" - D_TX20_SOUTH D_TX20_SOUTH D_TX20_WEST "|" - D_TX20_SOUTH D_TX20_WEST "|" - D_TX20_WEST D_TX20_SOUTH D_TX20_WEST "|" - D_TX20_WEST "|" - D_TX20_WEST D_TX20_NORTH D_TX20_WEST "|" - D_TX20_NORTH D_TX20_WEST "|" - D_TX20_NORTH D_TX20_NORTH D_TX20_WEST; - -uint8_t tx20_sa = 0; -uint8_t tx20_sb = 0; -uint8_t tx20_sd = 0; -uint8_t tx20_se = 0; -uint16_t tx20_sc = 0; -uint16_t tx20_sf = 0; - -float tx20_wind_speed_kmh = 0; -float tx20_wind_speed_max = 0; -float tx20_wind_speed_avg = 0; -float tx20_wind_sum = 0; -int tx20_count = 0; -uint8_t tx20_wind_direction = 0; - -bool tx20_available = false; - -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 -void Tx20StartRead(void) ICACHE_RAM_ATTR; -#endif - -void Tx20StartRead(void) -{ -# 101 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_35_tx20.ino" - tx20_available = false; - - tx20_sa = 0; - tx20_sb = 0; - tx20_sd = 0; - tx20_se = 0; - tx20_sc = 0; - tx20_sf = 0; - - delayMicroseconds(TX20_BIT_TIME / 2); - - for (int32_t bitcount = 41; bitcount > 0; bitcount--) { - uint8_t dpin = (digitalRead(pin[GPIO_TX20_TXD_BLACK])); - if (bitcount > 41 - 5) { - - tx20_sa = (tx20_sa << 1) | (dpin ^ 1); - } else if (bitcount > 41 - 5 - 4) { - - tx20_sb = tx20_sb >> 1 | ((dpin ^ 1) << 3); - } else if (bitcount > 41 - 5 - 4 - 12) { - - tx20_sc = tx20_sc >> 1 | ((dpin ^ 1) << 11); - } else if (bitcount > 41 - 5 - 4 - 12 - 4) { - - tx20_sd = tx20_sd >> 1 | ((dpin ^ 1) << 3); - } else if (bitcount > 41 - 5 - 4 - 12 - 4 - 4) { - - tx20_se = tx20_se >> 1 | (dpin << 3); - } else { - - tx20_sf = tx20_sf >> 1 | (dpin << 11); - } - - delayMicroseconds(TX20_BIT_TIME); - } - - uint8_t chk = (tx20_sb + (tx20_sc & 0xf) + ((tx20_sc >> 4) & 0xf) + ((tx20_sc >> 8) & 0xf)); - chk &= 0xf; - - if ((chk == tx20_sd) && (tx20_sc < 400)) { - tx20_available = true; - } - - - - - - - - GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << pin[GPIO_TX20_TXD_BLACK]); -} - -void Tx20Read(void) -{ - if (!(uptime % TX20_RESET_VALUES)) { - tx20_count = 0; - tx20_wind_sum = 0; - tx20_wind_speed_max = 0; - } - else if (tx20_available) { - tx20_wind_speed_kmh = float(tx20_sc) * 0.36; - if (tx20_wind_speed_kmh > tx20_wind_speed_max) { - tx20_wind_speed_max = tx20_wind_speed_kmh; - } - tx20_count++; - tx20_wind_sum += tx20_wind_speed_kmh; - tx20_wind_speed_avg = tx20_wind_sum / tx20_count; - tx20_wind_direction = tx20_sb; - } -} - -void Tx20Init(void) { - pinMode(pin[GPIO_TX20_TXD_BLACK], INPUT); - attachInterrupt(pin[GPIO_TX20_TXD_BLACK], Tx20StartRead, RISING); -} - -void Tx20Show(bool json) -{ - char wind_speed_string[33]; - dtostrfd(tx20_wind_speed_kmh, 2, wind_speed_string); - char wind_speed_max_string[33]; - dtostrfd(tx20_wind_speed_max, 2, wind_speed_max_string); - char wind_speed_avg_string[33]; - dtostrfd(tx20_wind_speed_avg, 2, wind_speed_avg_string); - char wind_direction_string[4]; - GetTextIndexed(wind_direction_string, sizeof(wind_direction_string), tx20_wind_direction, kTx20Directions); - - if (json) { - ResponseAppend_P(PSTR(",\"TX20\":{\"Speed\":%s,\"SpeedAvg\":%s,\"SpeedMax\":%s,\"Direction\":\"%s\"}"), - wind_speed_string, wind_speed_avg_string, wind_speed_max_string, wind_direction_string); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TX20, wind_speed_string, wind_speed_avg_string, wind_speed_max_string, wind_direction_string); -#endif - } -} - - - - - -bool Xsns35(uint8_t function) -{ - bool result = false; - - if (pin[GPIO_TX20_TXD_BLACK] < 99) { - switch (function) { - case FUNC_INIT: - Tx20Init(); - break; - case FUNC_EVERY_SECOND: - Tx20Read(); - break; - case FUNC_JSON_APPEND: - Tx20Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Tx20Show(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_36_mgc3130.ino" -# 22 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_36_mgc3130.ino" -#ifdef USE_I2C -#ifdef USE_MGC3130 -# 35 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_36_mgc3130.ino" -#define XSNS_36 36 -#define XI2C_27 27 - -#warning **** MGC3130: It is recommended to disable all unneeded I2C-drivers **** - -#define MGC3130_I2C_ADDR 0x42 - -#define MGC3130_xfer pin[GPIO_MGC3130_XFER] -#define MGC3130_reset pin[GPIO_MGC3130_RESET] - - -bool MGC3130_type = false; -char MGC3130stype[] = "MGC3130"; - - -#define MGC3130_SYSTEM_STATUS 0x15 -#define MGC3130_REQUEST_MSG 0x06 -#define MGC3130_FW_VERSION 0x83 -#define MGC3130_SET_RUNTIME 0xA2 -#define MGC3130_SENSOR_DATA 0x91 - - -#define MGC3130_GESTURE_GARBAGE 1 -#define MGC3130_FLICK_WEST_EAST 2 -#define MGC3130_FLICK_EAST_WEST 3 -#define MGC3130_FLICK_SOUTH_NORTH 4 -#define MGC3130_FLICK_NORTH_SOUTH 5 -#define MGC3130_CIRCLE_CLOCKWISE 6 -#define MGC3130_CIRCLE_CCLOCKWISE 7 - -#define MGC3130_MIN_ROTVALUE 0 -#define MGC3130_MAX_ROTVALUE 1023 -#define MGC3130_MIN_ZVALUE 32768 - - -#ifdef USE_WEBSERVER -const char HTTP_MGC_3130_SNS[] PROGMEM = - "{s}" "%s" "{m}%s{e}" - "{s}" "HwRev" "{m}%u.%u{e}" - "{s}" "loaderVer" "{m}%u.%u{e}" - "{s}" "platVer" "{m}%u{e}"; -#endif - - - - - - - -#pragma pack(1) -union MGC3130_Union{ - uint8_t buffer[132]; - struct - { - - uint8_t msgSize; - uint8_t flag; - uint8_t counter; - uint8_t id; - - struct { - uint8_t DSPStatus:1; - uint8_t gestureInfo:1; - uint8_t touchInfo:1; - uint8_t airWheelInfo:1; - uint8_t xyzPosition:1; - uint8_t noisePower:1; - uint8_t reserved:2; - uint8_t electrodeConfiguration:3; - uint8_t CICData:1; - uint8_t SDData:1; - uint16_t reserved2:3; - } outputConfigMask; - uint8_t timestamp; - struct { - uint8_t positionValid:1; - uint8_t airWheelValid:1; - uint8_t rawDataValid:1; - uint8_t noisePowerValid:1; - uint8_t environmentalNoise:1; - uint8_t clipping:1; - uint8_t reserved:1; - uint8_t DSPRunning:1; - } systemInfo; - uint16_t dspInfo; - struct { - uint8_t gestureCode:8; - uint8_t reserved:4; - uint8_t gestureType:4; - uint8_t edgeFlick:1; - uint16_t reserved2:14; - uint8_t gestureInProgress:1; - } gestureInfo; - struct { - uint8_t touchSouth:1; - uint8_t touchWest:1; - uint8_t touchNorth:1; - uint8_t touchEast:1; - uint8_t touchCentre:1; - uint8_t tapSouth:1; - uint8_t tapWest:1; - uint8_t tapNorth:1; - uint8_t tapEast :1; - uint8_t tapCentre:1; - uint8_t doubleTapSouth:1; - uint8_t doubleTapWest:1; - uint8_t doubleTapNorth:1; - uint8_t doubleTapEast:1; - uint8_t doubleTapCentre:1; - uint8_t reserved:1; - uint8_t touchCounter; - uint8_t reserved2; - } touchInfo; - int8_t airWheel; - uint8_t reserved; - uint16_t x; - uint16_t y; - uint16_t z; - float noisePower; - float CICData[4]; - float SDData[4]; - } out; - struct { - uint8_t header[3]; - - uint8_t valid; - uint8_t hwRev[2]; - uint8_t parameterStartAddr; - uint8_t loaderVersion[2]; - uint8_t loaderPlatform; - uint8_t fwStartAddr; - char fwVersion[120]; - } fw; - struct{ - uint8_t id; - uint8_t size; - uint16_t error; - uint32_t reserved; - uint32_t reserved1; - } status; -} MGC_data; -#pragma pack() - -char MGC3130_currentGesture[12]; - -int8_t MGC3130_delta, MGC3130_lastrotation = 0; -int16_t MGC3130_rotValue, MGC3130_lastSentRotValue = 0; - -uint16_t MGC3130_lastSentX, MGC3130_lastSentY, MGC3130_lastSentZ = 0; - -uint8_t hwRev[2], loaderVersion[2], loaderPlatform = 0; -char MGC3130_firmwareInfo[20]; - -uint8_t MGC3130_touchTimeout = 0; -uint16_t MGC3130_touchCounter = 1; -uint32_t MGC3130_touchTimeStamp = millis(); -bool MGC3130_triggeredByTouch = false; - -uint8_t MGC3130_mode = 1; - - - -uint8_t MGC3130autoCal[] = {0x10, 0x00, 0x00, 0xA2, 0x80, 0x00 , 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; -uint8_t MGC3130disableAirwheel[] = {0x10, 0x00, 0x00, 0xA2, 0x90, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; -uint8_t MGC3130enableAirwheel[] = {0x10, 0x00, 0x00, 0xA2, 0x90, 0x00 , 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}; - -void MGC3130_handleSensorData(){ - if ( MGC_data.out.outputConfigMask.touchInfo && MGC3130_touchTimeout == 0){ - if (MGC3130_handleTouch()){ - MGC3130_triggeredByTouch = true; - MqttPublishSensor(); - } - } - - if(MGC3130_mode == 1){ - if( MGC_data.out.outputConfigMask.gestureInfo && MGC_data.out.gestureInfo.gestureCode > 0){ - MGC3130_handleGesture(); - MqttPublishSensor(); - } - } - if(MGC3130_mode == 2){ - if(MGC_data.out.outputConfigMask.airWheelInfo && MGC_data.out.systemInfo.airWheelValid){ - MGC3130_handleAirWheel(); - MqttPublishSensor(); - } - } - if(MGC3130_mode == 3){ - if(MGC_data.out.systemInfo.positionValid && (MGC_data.out.z > MGC3130_MIN_ZVALUE)){ - MqttPublishSensor(); - } - } -} - -void MGC3130_sendMessage(uint8_t data[], uint8_t length){ - Wire.beginTransmission(MGC3130_I2C_ADDR); - Wire.write(data,length); - Wire.endTransmission(); - delay(2); - MGC3130_receiveMessage(); -} - - -void MGC3130_handleGesture(){ - - char edge[5]; - if (MGC_data.out.gestureInfo.edgeFlick){ - snprintf_P(edge, sizeof(edge), PSTR("ED_")); - } - else{ - snprintf_P(edge, sizeof(edge), PSTR("")); - } - switch(MGC_data.out.gestureInfo.gestureCode){ - case MGC3130_GESTURE_GARBAGE: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("NONE")); - break; - case MGC3130_FLICK_WEST_EAST: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_WE"), edge); - break; - case MGC3130_FLICK_EAST_WEST: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_EW"), edge); - break; - case MGC3130_FLICK_SOUTH_NORTH: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_SN"), edge); - break; - case MGC3130_FLICK_NORTH_SOUTH: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("%sFL_NS"), edge); - break; - case MGC3130_CIRCLE_CLOCKWISE: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("CW")); - break; - case MGC3130_CIRCLE_CCLOCKWISE: - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("CCW")); - break; - } - -} - -bool MGC3130_handleTouch(){ - - bool success = false; - if (MGC_data.out.touchInfo.doubleTapCentre && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_C")); - MGC3130_touchTimeout = 5; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.doubleTapEast && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_E")); - MGC3130_touchTimeout = 5; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.doubleTapNorth && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_N")); - MGC3130_touchTimeout = 5; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.doubleTapWest && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_W")); - MGC3130_touchTimeout = 5; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.doubleTapSouth && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("DT_S")); - MGC3130_touchTimeout = 5; - success = true; - MGC3130_touchCounter = 1; - } - if (MGC_data.out.touchInfo.tapCentre && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_C")); - MGC3130_touchTimeout = 2; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.tapEast && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_E")); - MGC3130_touchTimeout = 2; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.tapNorth && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_N")); - MGC3130_touchTimeout = 2; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.tapWest && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_W")); - MGC3130_touchTimeout = 2; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.tapSouth && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TP_S")); - MGC3130_touchTimeout = 2; - success = true; - MGC3130_touchCounter = 1; - } - else if (MGC_data.out.touchInfo.touchCentre && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_C")); - success = true; - MGC3130_touchCounter++; - } - else if (MGC_data.out.touchInfo.touchEast && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_E")); - success = true; - MGC3130_touchCounter++; - } - else if (MGC_data.out.touchInfo.touchNorth && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_N")); - success = true; - MGC3130_touchCounter++; - } - else if (MGC_data.out.touchInfo.touchWest && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_W")); - success = true; - MGC3130_touchCounter++; - } - else if (MGC_data.out.touchInfo.touchSouth && !success){ - - snprintf_P(MGC3130_currentGesture, sizeof(MGC3130_currentGesture), PSTR("TH_S")); - success = true; - MGC3130_touchCounter++; - } - - return success; -} - -void MGC3130_handleAirWheel(){ - MGC3130_delta = MGC_data.out.airWheel - MGC3130_lastrotation; - MGC3130_lastrotation = MGC_data.out.airWheel; - - MGC3130_rotValue = MGC3130_rotValue + MGC3130_delta; - if(MGC3130_rotValue < MGC3130_MIN_ROTVALUE){ - MGC3130_rotValue = MGC3130_MIN_ROTVALUE; - } - if(MGC3130_rotValue > MGC3130_MAX_ROTVALUE){ - MGC3130_rotValue = MGC3130_MAX_ROTVALUE; - } -} - -void MGC3130_handleSystemStatus(){ - -} - -bool MGC3130_receiveMessage(){ - if(MGC3130_readData()){ - switch(MGC_data.out.id){ - case MGC3130_SENSOR_DATA: - MGC3130_handleSensorData(); - break; - case MGC3130_SYSTEM_STATUS: - MGC3130_handleSystemStatus(); - break; - case MGC3130_FW_VERSION: - hwRev[0] = MGC_data.fw.hwRev[1]; - hwRev[1] = MGC_data.fw.hwRev[0]; - loaderVersion[0] = MGC_data.fw.loaderVersion[0]; - loaderVersion[1] = MGC_data.fw.loaderVersion[1]; - loaderPlatform = MGC_data.fw.loaderPlatform; - snprintf_P(MGC3130_firmwareInfo, sizeof(MGC3130_firmwareInfo), PSTR("FW: %s"), MGC_data.fw.fwVersion); - MGC3130_firmwareInfo[20] = '\0'; - - break; - } - return true; - } - return false; -} - -bool MGC3130_readData() -{ - bool success = false; - if (!digitalRead(MGC3130_xfer)){ - pinMode(MGC3130_xfer, OUTPUT); - digitalWrite(MGC3130_xfer, LOW); - Wire.requestFrom(MGC3130_I2C_ADDR, (uint16_t)32); - - MGC_data.buffer[0] = 4; - unsigned char i = 0; - while(Wire.available() && (i < MGC_data.buffer[0])){ - MGC_data.buffer[i] = Wire.read(); - i++; - } - digitalWrite(MGC3130_xfer, HIGH); - pinMode(MGC3130_xfer, INPUT); - success = true; - } - return success; -} - -void MGC3130_nextMode(){ - if (MGC3130_mode < 3){ - MGC3130_mode++; - } - else{ - MGC3130_mode = 1; - } - switch(MGC3130_mode){ - case 1: - MGC3130_sendMessage(MGC3130disableAirwheel,16); - break; - case 2: - MGC3130_sendMessage(MGC3130enableAirwheel,16); - break; - case 3: - MGC3130_sendMessage(MGC3130disableAirwheel,16); - break; - } -} - -void MGC3130_loop() -{ - if(MGC3130_touchTimeout > 0){ - MGC3130_touchTimeout--; - } - MGC3130_receiveMessage(); -} - -void MGC3130_detect(void) -{ - if (MGC3130_type || I2cActive(MGC3130_I2C_ADDR)) { return; } - - pinMode(MGC3130_xfer, INPUT_PULLUP); - pinMode(MGC3130_reset, OUTPUT); - digitalWrite(MGC3130_reset, LOW); - delay(10); - digitalWrite(MGC3130_reset, HIGH); - delay(50); - - if (MGC3130_receiveMessage()) { - I2cSetActiveFound(MGC3130_I2C_ADDR, MGC3130stype); - MGC3130_currentGesture[0] = '\0'; - MGC3130_type = true; - } -} - - - - - -void MGC3130_show(bool json) -{ - if (!MGC3130_type) { return; } - - char status_chr[2]; - if (MGC_data.out.systemInfo.DSPRunning) { - sprintf (status_chr, "1"); - } - else{ - sprintf (status_chr, "0"); - } - - if (json) { - if (MGC3130_mode == 3 && !MGC3130_triggeredByTouch) { - if (MGC_data.out.systemInfo.positionValid && !(MGC_data.out.x == MGC3130_lastSentX && MGC_data.out.y == MGC3130_lastSentY && MGC_data.out.z == MGC3130_lastSentZ)) { - ResponseAppend_P(PSTR(",\"%s\":{\"X\":%u,\"Y\":%u,\"Z\":%u}"), - MGC3130stype, MGC_data.out.x/64, MGC_data.out.y/64, (MGC_data.out.z-(uint16_t)MGC3130_MIN_ZVALUE)/64); - MGC3130_lastSentX = MGC_data.out.x; - MGC3130_lastSentY = MGC_data.out.y; - MGC3130_lastSentZ = MGC_data.out.z; - } - } - MGC3130_triggeredByTouch = false; - - if (MGC3130_mode == 2) { - if (MGC_data.out.systemInfo.airWheelValid && (MGC3130_rotValue != MGC3130_lastSentRotValue)) { - ResponseAppend_P(PSTR(",\"%s\":{\"AW\":%i}"), MGC3130stype, MGC3130_rotValue); - MGC3130_lastSentRotValue = MGC3130_rotValue; - } - } - - if (MGC3130_currentGesture[0] != '\0') { - if (millis() - MGC3130_touchTimeStamp > 220 ) { - MGC3130_touchCounter = 1; - } - ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%u}"), MGC3130stype, MGC3130_currentGesture, MGC3130_touchCounter); - MGC3130_currentGesture[0] = '\0'; - MGC3130_touchTimeStamp = millis(); - } -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_MGC_3130_SNS, MGC3130stype, status_chr, hwRev[0], hwRev[1], loaderVersion[0], loaderVersion[1], loaderPlatform ); -#endif - } -} -# 557 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_36_mgc3130.ino" -bool MGC3130CommandSensor() -{ - bool serviced = true; - - switch (XdrvMailbox.payload) { - case 0: - MGC3130_nextMode(); - break; - case 1: - MGC3130_mode = 1; - MGC3130_sendMessage(MGC3130disableAirwheel,16); - break; - case 2: - MGC3130_mode = 2; - MGC3130_sendMessage(MGC3130enableAirwheel,16); - break; - case 3: - MGC3130_mode = 3; - MGC3130_sendMessage(MGC3130disableAirwheel,16); - break; - } - return serviced; -} - - - - - -bool Xsns36(uint8_t function) -{ - if (!I2cEnabled(XI2C_27)) { return false; } - - bool result = false; - - if ((FUNC_INIT == function) && (pin[GPIO_MGC3130_XFER] < 99) && (pin[GPIO_MGC3130_RESET] < 99)) { - MGC3130_detect(); - } - else if (MGC3130_type) { - switch (function) { - case FUNC_EVERY_50_MSECOND: - MGC3130_loop(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_36 == XdrvMailbox.index) { - result = MGC3130CommandSensor(); - } - break; - case FUNC_JSON_APPEND: - MGC3130_show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MGC3130_show(0); - break; -#endif - } - } - return result; -} -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_37_rfsensor.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_37_rfsensor.ino" -#ifdef USE_RF_SENSOR -# 33 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_37_rfsensor.ino" -#define XSNS_37 37 - - - - -#define RFSNS_VALID_WINDOW 1800 - -#define RFSNS_LOOPS_PER_MILLI 1900 -#define RFSNS_RAW_BUFFER_SIZE 180 -#define RFSNS_MIN_RAW_PULSES 112 - -#define RFSNS_MIN_PULSE_LENGTH 300 -#define RFSNS_RAWSIGNAL_SAMPLE 50 -#define RFSNS_SIGNAL_TIMEOUT 10 -#define RFSNS_SIGNAL_REPEAT_TIME 500 - -typedef struct RawSignalStruct -{ - int Number; - uint8_t Repeats; - uint8_t Multiply; - unsigned long Time; - uint8_t Pulses[RFSNS_RAW_BUFFER_SIZE+2]; - -} raw_signal_t; - -raw_signal_t *rfsns_raw_signal = nullptr; -uint8_t rfsns_rf_bit; -uint8_t rfsns_rf_port; -uint8_t rfsns_any_sensor = 0; - - - - - -bool RfSnsFetchSignal(uint8_t DataPin, bool StateSignal) -{ - uint8_t Fbit = digitalPinToBitMask(DataPin); - uint8_t Fport = digitalPinToPort(DataPin); - uint8_t FstateMask = (StateSignal ? Fbit : 0); - - if ((*portInputRegister(Fport) & Fbit) == FstateMask) { - const unsigned long LoopsPerMilli = RFSNS_LOOPS_PER_MILLI; - - - - - - - unsigned long PulseLength = 0; - if (rfsns_raw_signal->Time) { - if (rfsns_raw_signal->Repeats && (rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) { - PulseLength = micros() + RFSNS_SIGNAL_TIMEOUT *1000; - while (((rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && (PulseLength > micros())) { - if ((*portInputRegister(Fport) & Fbit) == FstateMask) { - PulseLength = micros() + RFSNS_SIGNAL_TIMEOUT *1000; - } - } - while (((rfsns_raw_signal->Time + RFSNS_SIGNAL_REPEAT_TIME) > millis()) && ((*portInputRegister(Fport) & Fbit) != FstateMask)); - } - } - - int RawCodeLength = 1; - bool Ftoggle = false; - unsigned long numloops = 0; - unsigned long maxloops = RFSNS_SIGNAL_TIMEOUT * LoopsPerMilli; - rfsns_raw_signal->Multiply = RFSNS_RAWSIGNAL_SAMPLE; - do { - numloops = 0; - while(((*portInputRegister(Fport) & Fbit) == FstateMask) ^ Ftoggle) { - if (numloops++ == maxloops) { break; } - } - PulseLength = (numloops *1000) / LoopsPerMilli; - if (PulseLength < RFSNS_MIN_PULSE_LENGTH) { break; } - Ftoggle = !Ftoggle; - rfsns_raw_signal->Pulses[RawCodeLength++] = PulseLength / (unsigned long)rfsns_raw_signal->Multiply; - } - while(RawCodeLength < RFSNS_RAW_BUFFER_SIZE && numloops <= maxloops); - - if ((RawCodeLength >= RFSNS_MIN_RAW_PULSES) && (RawCodeLength < RFSNS_RAW_BUFFER_SIZE -1)) { - rfsns_raw_signal->Repeats = 0; - rfsns_raw_signal->Number = RawCodeLength -1; - rfsns_raw_signal->Pulses[rfsns_raw_signal->Number] = 0; - rfsns_raw_signal->Time = millis(); - return true; - } - else - rfsns_raw_signal->Number = 0; - } - - return false; -} - -#ifdef USE_THEO_V2 -# 149 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_37_rfsensor.ino" -#define RFSNS_THEOV2_MAX_CHANNEL 2 - -#define RFSNS_THEOV2_PULSECOUNT 114 -#define RFSNS_THEOV2_RF_PULSE_MID 1000 - -typedef struct { - uint32_t time; - int16_t temp; - uint16_t lux; - uint8_t volt; -} theo_v2_t1_t; - -typedef struct { - uint32_t time; - int16_t temp; - uint16_t hum; - uint8_t volt; -} theo_v2_t2_t; - -theo_v2_t1_t *rfsns_theo_v2_t1 = nullptr; -theo_v2_t2_t *rfsns_theo_v2_t2 = nullptr; - -void RfSnsInitTheoV2(void) -{ - rfsns_theo_v2_t1 = (theo_v2_t1_t*)malloc(RFSNS_THEOV2_MAX_CHANNEL * sizeof(theo_v2_t1_t)); - rfsns_theo_v2_t2 = (theo_v2_t2_t*)malloc(RFSNS_THEOV2_MAX_CHANNEL * sizeof(theo_v2_t2_t)); - rfsns_any_sensor++; -} - -void RfSnsAnalyzeTheov2(void) -{ - if (rfsns_raw_signal->Number != RFSNS_THEOV2_PULSECOUNT) { return; } - - uint8_t Checksum; - uint8_t Channel; - uint8_t Type; - uint8_t Voltage; - int Payload1; - int Payload2; - - uint8_t b, bytes, bits, id; - - uint8_t idx = 3; - uint8_t chksum = 0; - for (bytes = 0; bytes < 7; bytes++) { - b = 0; - for (bits = 0; bits <= 7; bits++) - { - if ((rfsns_raw_signal->Pulses[idx] * rfsns_raw_signal->Multiply) > RFSNS_THEOV2_RF_PULSE_MID) { - b |= 1 << bits; - } - idx += 2; - } - if (bytes > 0) { chksum += b; } - - switch (bytes) { - case 0: - Checksum = b; - break; - case 1: - id = b; - Channel = b & 0x7; - Type = (b >> 3) & 0x1f; - break; - case 2: - Voltage = b; - break; - case 3: - Payload1 = b; - break; - case 4: - Payload1 = (b << 8) | Payload1; - break; - case 5: - Payload2 = b; - break; - case 6: - Payload2 = (b << 8) | Payload2; - break; - } - } - - if (Checksum != chksum) { return; } - if ((Channel == 0) || (Channel > RFSNS_THEOV2_MAX_CHANNEL)) { return; } - Channel--; - - rfsns_raw_signal->Repeats = 1; - - int Payload3 = Voltage & 0x3f; - - switch (Type) { - case 1: - rfsns_theo_v2_t1[Channel].time = LocalTime(); - rfsns_theo_v2_t1[Channel].volt = Payload3; - rfsns_theo_v2_t1[Channel].temp = Payload1; - rfsns_theo_v2_t1[Channel].lux = Payload2; - break; - case 2: - rfsns_theo_v2_t2[Channel].time = LocalTime(); - rfsns_theo_v2_t2[Channel].volt = Payload3; - rfsns_theo_v2_t2[Channel].temp = Payload1; - rfsns_theo_v2_t2[Channel].hum = Payload2; - break; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: TheoV2, ChkCalc %d, Chksum %d, id %d, Type %d, Ch %d, Volt %d, BattLo %d, Pld1 %d, Pld2 %d"), - chksum, Checksum, id, Type, Channel +1, Payload3, (Voltage & 0x80) >> 7, Payload1, Payload2); -} - -void RfSnsTheoV2Show(bool json) -{ - bool sensor_once = false; - - for (uint32_t i = 0; i < RFSNS_THEOV2_MAX_CHANNEL; i++) { - if (rfsns_theo_v2_t1[i].time) { - char sensor[10]; - snprintf_P(sensor, sizeof(sensor), PSTR("TV2T1C%d"), i +1); - char voltage[33]; - dtostrfd((float)rfsns_theo_v2_t1[i].volt / 10, 1, voltage); - - if (rfsns_theo_v2_t1[i].time < LocalTime() - RFSNS_VALID_WINDOW) { - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_RFRECEIVED "\":\"%s\",\"" D_JSON_VOLTAGE "\":%s}"), - sensor, GetDT(rfsns_theo_v2_t1[i].time).c_str(), voltage); - } - } else { - char temperature[33]; - dtostrfd(ConvertTemp((float)rfsns_theo_v2_t1[i].temp / 100), Settings.flag2.temperature_resolution, temperature); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_ILLUMINANCE "\":%d,\"" D_JSON_VOLTAGE "\":%s}"), - sensor, temperature, rfsns_theo_v2_t1[i].lux, voltage); -#ifdef USE_DOMOTICZ - if ((0 == tele_period) && !sensor_once) { - DomoticzSensor(DZ_TEMP, temperature); - DomoticzSensor(DZ_ILLUMINANCE, rfsns_theo_v2_t1[i].lux); - sensor_once = true; - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, sensor, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, sensor, rfsns_theo_v2_t1[i].lux); -#endif - } - } - } - } - - sensor_once = false; - for (uint32_t i = 0; i < RFSNS_THEOV2_MAX_CHANNEL; i++) { - if (rfsns_theo_v2_t2[i].time) { - char sensor[10]; - snprintf_P(sensor, sizeof(sensor), PSTR("TV2T2C%d"), i +1); - char voltage[33]; - dtostrfd((float)rfsns_theo_v2_t2[i].volt / 10, 1, voltage); - - if (rfsns_theo_v2_t2[i].time < LocalTime() - RFSNS_VALID_WINDOW) { - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_RFRECEIVED" \":\"%s\",\"" D_JSON_VOLTAGE "\":%s}"), - sensor, GetDT(rfsns_theo_v2_t2[i].time).c_str(), voltage); - } - } else { - float temp = ConvertTemp((float)rfsns_theo_v2_t2[i].temp / 100); - float humi = ConvertHumidity((float)rfsns_theo_v2_t2[i].hum / 100); - char temperature[33]; - dtostrfd(temp, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(humi, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s,\"" D_JSON_VOLTAGE "\":%s}"), - sensor, temperature, humidity, voltage); - if ((0 == tele_period) && !sensor_once) { -#ifdef USE_DOMOTICZ - DomoticzTempHumSensor(temperature, humidity); -#endif -#ifdef USE_KNX - KnxSensor(KNX_TEMPERATURE, temp); - KnxSensor(KNX_HUMIDITY, humi); -#endif - sensor_once = true; - } -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, sensor, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, sensor, humidity); -#endif - } - } - } - } -} - -#endif - -#ifdef USE_ALECTO_V2 -# 392 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_37_rfsensor.ino" -#define RFSNS_DKW2012_PULSECOUNT 176 -#define RFSNS_ACH2010_MIN_PULSECOUNT 160 -#define RFSNS_ACH2010_MAX_PULSECOUNT 160 - -#define D_ALECTOV2 "AlectoV2" - -const char kAlectoV2Directions[] PROGMEM = D_TX20_NORTH "|" - D_TX20_NORTH D_TX20_NORTH D_TX20_EAST "|" - D_TX20_NORTH D_TX20_EAST "|" - D_TX20_EAST D_TX20_NORTH D_TX20_EAST "|" - D_TX20_EAST "|" - D_TX20_EAST D_TX20_SOUTH D_TX20_EAST "|" - D_TX20_SOUTH D_TX20_EAST "|" - D_TX20_SOUTH D_TX20_SOUTH D_TX20_EAST "|" - D_TX20_SOUTH "|" - D_TX20_SOUTH D_TX20_SOUTH D_TX20_WEST "|" - D_TX20_SOUTH D_TX20_WEST "|" - D_TX20_WEST D_TX20_SOUTH D_TX20_WEST "|" - D_TX20_WEST "|" - D_TX20_WEST D_TX20_NORTH D_TX20_WEST "|" - D_TX20_NORTH D_TX20_WEST "|" - D_TX20_NORTH D_TX20_NORTH D_TX20_WEST; - -typedef struct { - uint32_t time; - float temp; - float rain; - float wind; - float gust; - uint8_t type; - uint8_t humi; - uint8_t wdir; -} alecto_v2_t; - -alecto_v2_t *rfsns_alecto_v2 = nullptr; -uint16_t rfsns_alecto_rain_base = 0; - -void RfSnsInitAlectoV2(void) -{ - rfsns_alecto_v2 = (alecto_v2_t*)malloc(sizeof(alecto_v2_t)); - rfsns_any_sensor++; -} - -void RfSnsAnalyzeAlectov2() -{ - if (!(((rfsns_raw_signal->Number >= RFSNS_ACH2010_MIN_PULSECOUNT) && - (rfsns_raw_signal->Number <= RFSNS_ACH2010_MAX_PULSECOUNT)) || (rfsns_raw_signal->Number == RFSNS_DKW2012_PULSECOUNT))) { return; } - - uint8_t c = 0; - uint8_t rfbit; - uint8_t data[9] = { 0 }; - uint8_t msgtype = 0; - uint8_t rc = 0; - int temp; - uint8_t checksum = 0; - uint8_t checksumcalc = 0; - uint8_t maxidx = 8; - unsigned long atime; - float factor; - char buf1[16]; - - if (rfsns_raw_signal->Number > RFSNS_ACH2010_MAX_PULSECOUNT) { maxidx = 9; } - - uint8_t idx = maxidx; - for (uint32_t x = rfsns_raw_signal->Number; x > 0; x = x-2) { - if (rfsns_raw_signal->Pulses[x-1] * rfsns_raw_signal->Multiply < 0x300) { - rfbit = 0x80; - } else { - rfbit = 0; - } - data[idx] = (data[idx] >> 1) | rfbit; - c++; - if (c == 8) { - if (idx == 0) { break; } - c = 0; - idx--; - } - } - - checksum = data[maxidx]; - checksumcalc = RfSnsAlectoCRC8(data, maxidx); - - msgtype = (data[0] >> 4) & 0xf; - rc = (data[0] << 4) | (data[1] >> 4); - - if (checksum != checksumcalc) { return; } - if ((msgtype != 10) && (msgtype != 5)) { return; } - - rfsns_raw_signal->Repeats = 1; - - - - - - factor = 1.22; - - - - - - rfsns_alecto_v2->time = LocalTime(); - rfsns_alecto_v2->type = (RFSNS_DKW2012_PULSECOUNT == rfsns_raw_signal->Number); - rfsns_alecto_v2->temp = (float)(((data[1] & 0x3) * 256 + data[2]) - 400) / 10; - rfsns_alecto_v2->humi = data[3]; - uint16_t rain = (data[6] * 256) + data[7]; - - if (rain < rfsns_alecto_rain_base) { rfsns_alecto_rain_base = rain; } - if (rfsns_alecto_rain_base > 0) { - rfsns_alecto_v2->rain += ((float)rain - rfsns_alecto_rain_base) * 0.30; - } - rfsns_alecto_rain_base = rain; - rfsns_alecto_v2->wind = (float)data[4] * factor; - rfsns_alecto_v2->gust = (float)data[5] * factor; - if (rfsns_alecto_v2->type) { - rfsns_alecto_v2->wdir = data[8] & 0xf; - } - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: " D_ALECTOV2 ", ChkCalc %d, Chksum %d, rc %d, Temp %d, Hum %d, Rain %d, Wind %d, Gust %d, Dir %d, Factor %s"), - checksumcalc, checksum, rc, ((data[1] & 0x3) * 256 + data[2]) - 400, data[3], (data[6] * 256) + data[7], data[4], data[5], data[8] & 0xf, dtostrfd(factor, 3, buf1)); -} - -void RfSnsAlectoResetRain(void) -{ - if ((RtcTime.hour == 0) && (RtcTime.minute == 0) && (RtcTime.second == 5)) { - rfsns_alecto_v2->rain = 0; - } -} - - - - - - - -uint8_t RfSnsAlectoCRC8(uint8_t *addr, uint8_t len) -{ - uint8_t crc = 0; - while (len--) { - uint8_t inbyte = *addr++; - for (uint32_t i = 8; i; i--) { - uint8_t mix = (crc ^ inbyte) & 0x80; - crc <<= 1; - if (mix) { crc ^= 0x31; } - inbyte <<= 1; - } - } - return crc; -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_ALECTOV2[] PROGMEM = - "{s}" D_ALECTOV2 " " D_RAIN "{m}%s " D_UNIT_MILLIMETER "{e}" - "{s}" D_ALECTOV2 " " D_TX20_WIND_SPEED "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}" - "{s}" D_ALECTOV2 " " D_TX20_WIND_SPEED_MAX "{m}%s " D_UNIT_KILOMETER_PER_HOUR "{e}"; -const char HTTP_SNS_ALECTOV2_WDIR[] PROGMEM = - "{s}" D_ALECTOV2 " " D_TX20_WIND_DIRECTION "{m}%s{e}"; -#endif - -void RfSnsAlectoV2Show(bool json) -{ - if (rfsns_alecto_v2->time) { - if (rfsns_alecto_v2->time < LocalTime() - RFSNS_VALID_WINDOW) { - if (json) { - ResponseAppend_P(PSTR(",\"" D_ALECTOV2 "\":{\"" D_JSON_RFRECEIVED "\":\"%s\"}"), GetDT(rfsns_alecto_v2->time).c_str()); - } - } else { - float temp = ConvertTemp(rfsns_alecto_v2->temp); - char temperature[33]; - dtostrfd(temp, Settings.flag2.temperature_resolution, temperature); - float humi = ConvertHumidity((float)rfsns_alecto_v2->humi); - char humidity[33]; - dtostrfd(humi, Settings.flag2.humidity_resolution, humidity); - char rain[33]; - dtostrfd(rfsns_alecto_v2->rain, 2, rain); - char wind[33]; - dtostrfd(rfsns_alecto_v2->wind, 2, wind); - char gust[33]; - dtostrfd(rfsns_alecto_v2->gust, 2, gust); - char wdir[4]; - char direction[20]; - if (rfsns_alecto_v2->type) { - GetTextIndexed(wdir, sizeof(wdir), rfsns_alecto_v2->wdir, kAlectoV2Directions); - snprintf_P(direction, sizeof(direction), PSTR(",\"Direction\":\"%s\""), wdir); - } - - if (json) { - ResponseAppend_P(PSTR(",\"" D_ALECTOV2 "\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s,\"Rain\":%s,\"Wind\":%s,\"Gust\":%s%s}"), - temperature, humidity, rain, wind, gust, (rfsns_alecto_v2->type) ? direction : ""); - if (0 == tele_period) { -#ifdef USE_DOMOTICZ - - - - -#endif - } -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, D_ALECTOV2, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, D_ALECTOV2, humidity); - WSContentSend_PD(HTTP_SNS_ALECTOV2, rain, wind, gust); - if (rfsns_alecto_v2->type) { - WSContentSend_PD(HTTP_SNS_ALECTOV2_WDIR, wdir); - } -#endif - } - } - } -} -#endif - -void RfSnsInit(void) -{ - rfsns_raw_signal = (raw_signal_t*)(malloc(sizeof(raw_signal_t))); - if (rfsns_raw_signal) { - memset(rfsns_raw_signal, 0, sizeof(raw_signal_t)); -#ifdef USE_THEO_V2 - RfSnsInitTheoV2(); -#endif -#ifdef USE_ALECTO_V2 - RfSnsInitAlectoV2(); -#endif - if (rfsns_any_sensor) { - rfsns_rf_bit = digitalPinToBitMask(pin[GPIO_RF_SENSOR]); - rfsns_rf_port = digitalPinToPort(pin[GPIO_RF_SENSOR]); - pinMode(pin[GPIO_RF_SENSOR], INPUT); - } else { - free(rfsns_raw_signal); - rfsns_raw_signal = nullptr; - } - } -} - -void RfSnsAnalyzeRawSignal(void) -{ - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RFS: Pulses %d"), (int)rfsns_raw_signal->Number); - -#ifdef USE_THEO_V2 - RfSnsAnalyzeTheov2(); -#endif -#ifdef USE_ALECTO_V2 - RfSnsAnalyzeAlectov2(); -#endif -} - -void RfSnsEverySecond(void) -{ -#ifdef USE_ALECTO_V2 - RfSnsAlectoResetRain(); -#endif -} - -void RfSnsShow(bool json) -{ -#ifdef USE_THEO_V2 - RfSnsTheoV2Show(json); -#endif -#ifdef USE_ALECTO_V2 - RfSnsAlectoV2Show(json); -#endif -} - - - - - -bool Xsns37(uint8_t function) -{ - bool result = false; - - if ((pin[GPIO_RF_SENSOR] < 99) && (FUNC_INIT == function)) { - RfSnsInit(); - } - else if (rfsns_raw_signal) { - switch (function) { - case FUNC_LOOP: - if ((*portInputRegister(rfsns_rf_port) &rfsns_rf_bit) == rfsns_rf_bit) { - if (RfSnsFetchSignal(pin[GPIO_RF_SENSOR], HIGH)) { - RfSnsAnalyzeRawSignal(); - } - } - sleep = 0; - break; - case FUNC_EVERY_SECOND: - RfSnsEverySecond(); - break; - case FUNC_JSON_APPEND: - RfSnsShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - RfSnsShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_38_az7798.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_38_az7798.ino" -#ifdef USE_AZ7798 - -#define XSNS_38 38 -# 112 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_38_az7798.ino" -#include - -#ifndef CO2_LOW -#define CO2_LOW 800 -#endif -#ifndef CO2_HIGH -#define CO2_HIGH 1200 -#endif - -#define AZ_READ_TIMEOUT 400 - -#define AZ_CLOCK_UPDATE_INTERVAL (24UL * 60 * 60) -#define AZ_EPOCH (946684800UL) - -TasmotaSerial *AzSerial; - -const char ktype[] = "AZ7798"; -uint8_t az_type = 1; -uint16_t az_co2 = 0; -double az_temperature = 0; -double az_humidity = 0; -uint8_t az_received = 0; -uint8_t az_state = 0; -unsigned long az_clock_update = 10; - - - -void AzEverySecond(void) -{ - unsigned long start = millis(); - - az_state++; - if (5 == az_state) { - az_state = 0; - - AzSerial->flush(); - AzSerial->write(":\r", 2); - az_received = 0; - - uint8_t az_response[32]; - uint8_t counter = 0; - uint8_t i, j; - uint8_t response_substr[16]; - - do { - if (AzSerial->available() > 0) { - az_response[counter] = AzSerial->read(); - if(az_response[counter] == 0x0d) { az_received = 1; } - counter++; - } else { - delay(5); - } - } while(((millis() - start) < AZ_READ_TIMEOUT) && (counter < sizeof(az_response)) && !az_received); - - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, az_response, counter); - - if (!az_received) { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 comms timeout")); - return; - } - - i = 0; - while((az_response[i] != 'T') && (i < counter)) {i++;} - if(az_response[i] != 'T') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find start of response")); - return; - } - i++; - j = 0; - - while((az_response[i] != 'C') && (az_response[i] != 'F') && (i < counter)) { - response_substr[j++] = az_response[i++]; - } - if((az_response[i] != 'C') && (az_response[i] != 'F')){ - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of temperature")); - return; - } - response_substr[j] = 0; - az_temperature = CharToFloat((char*)response_substr); - if(az_response[i] == 'C') { - az_temperature = ConvertTemp((float)az_temperature); - } else { - az_temperature = ConvertTemp((az_temperature - 32) / 1.8); - } - i++; - if(az_response[i] != ':') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error first delimiter")); - return; - } - i++; - if(az_response[i] != 'C') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of CO2")); - return; - } - i++; - j = 0; - - while((az_response[i] != 'p') && (i < counter)) { - response_substr[j++] = az_response[i++]; - } - if(az_response[i] != 'p') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of CO2")); - return; - } - response_substr[j] = 0; - az_co2 = atoi((char*)response_substr); - LightSetSignal(CO2_LOW, CO2_HIGH, az_co2); - i += 3; - if(az_response[i] != ':') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error second delimiter")); - return; - } - i++; - if(az_response[i] != 'H') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 error start of humidity")); - return; - } - i++; - j = 0; - - while((az_response[i] != '%') && (i < counter)) { - response_substr[j++] = az_response[i++]; - } - if(az_response[i] != '%') { - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 failed to find end of humidity")); - return; - } - response_substr[j] = 0; - az_humidity = ConvertHumidity(CharToFloat((char*)response_substr)); - } - - - if ((az_clock_update == 0) && (LocalTime() > AZ_EPOCH)) { - char tmpString[16]; - sprintf(tmpString, "C %d\r", (int)(LocalTime() - AZ_EPOCH)); - AzSerial->write(tmpString); - - do { - if (AzSerial->available() > 0) { - if(AzSerial->read() == 0x0d) { break; } - } else { - delay(5); - } - } while(((millis() - start) < AZ_READ_TIMEOUT)); - az_clock_update = AZ_CLOCK_UPDATE_INTERVAL; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "AZ7798 clock updated")); - } else { - az_clock_update--; - } -} - - - -void AzInit(void) -{ - az_type = 0; - if ((pin[GPIO_AZ_RXD] < 99) && (pin[GPIO_AZ_TXD] < 99)) { - AzSerial = new TasmotaSerial(pin[GPIO_AZ_RXD], pin[GPIO_AZ_TXD], 1); - if (AzSerial->begin(9600)) { - if (AzSerial->hardwareSerial()) { ClaimSerial(); } - az_type = 1; - } - } -} - -void AzShow(bool json) -{ - char temperature[33]; - dtostrfd(az_temperature, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(az_humidity, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"), ktype, az_co2, temperature, humidity); -#ifdef USE_DOMOTICZ - if (0 == tele_period) DomoticzSensor(DZ_AIRQUALITY, az_co2); -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_CO2, ktype, az_co2); - WSContentSend_PD(HTTP_SNS_TEMP, ktype, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, ktype, humidity); -#endif - } -} - - - - - -bool Xsns38(uint8_t function) -{ - bool result = false; - - if(az_type){ - switch (function) { - case FUNC_INIT: - AzInit(); - break; - case FUNC_EVERY_SECOND: - AzEverySecond(); - break; - case FUNC_JSON_APPEND: - AzShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - AzShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_39_max31855.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_39_max31855.ino" -#ifdef USE_MAX31855 - -#define XSNS_39 39 - -bool initialized = false; - -struct MAX31855_ResultStruct{ - uint8_t ErrorCode; - float ProbeTemperature; - float ReferenceTemperature; -} MAX31855_Result; - -void MAX31855_Init(void){ - if(initialized) - return; - - - pinMode(pin[GPIO_MAX31855CS], OUTPUT); - pinMode(pin[GPIO_MAX31855CLK], OUTPUT); - pinMode(pin[GPIO_MAX31855DO], INPUT); - - - digitalWrite(pin[GPIO_MAX31855CS], HIGH); - digitalWrite(pin[GPIO_MAX31855CLK], LOW); - - initialized = true; -} - - - - - -void MAX31855_GetResult(void){ - int32_t RawData = MAX31855_ShiftIn(32); - uint8_t probeerror = RawData & 0x7; - - MAX31855_Result.ErrorCode = probeerror; - MAX31855_Result.ReferenceTemperature = MAX31855_GetReferenceTemperature(RawData); - if(probeerror) - MAX31855_Result.ProbeTemperature = NAN; - else - MAX31855_Result.ProbeTemperature = MAX31855_GetProbeTemperature(RawData); -} - - - - - - -float MAX31855_GetProbeTemperature(int32_t RawData){ - if(RawData & 0x80000000) - RawData = (RawData >> 18) | 0xFFFFC000; - else - RawData >>= 18; - - float result = (RawData * 0.25); - - return ConvertTemp(result); -} - - - - - -float MAX31855_GetReferenceTemperature(int32_t RawData){ - if(RawData & 0x8000) - RawData = (RawData >> 4) | 0xFFFFF000; - else - RawData = (RawData >> 4) & 0x00000FFF; - - float result = (RawData * 0.0625); - - return ConvertTemp(result); -} - - - - - -int32_t MAX31855_ShiftIn(uint8_t Length){ - int32_t dataIn = 0; - - digitalWrite(pin[GPIO_MAX31855CS], LOW); - delayMicroseconds(1); - - for (uint32_t i = 0; i < Length; i++) - { - digitalWrite(pin[GPIO_MAX31855CLK], LOW); - delayMicroseconds(1); - dataIn <<= 1; - if(digitalRead(pin[GPIO_MAX31855DO])) - dataIn |= 1; - digitalWrite(pin[GPIO_MAX31855CLK], HIGH); - delayMicroseconds(1); - } - - digitalWrite(pin[GPIO_MAX31855CS], HIGH); - digitalWrite(pin[GPIO_MAX31855CLK], LOW); - return dataIn; -} - -void MAX31855_Show(bool Json){ - char probetemp[33]; - char referencetemp[33]; - dtostrfd(MAX31855_Result.ProbeTemperature, Settings.flag2.temperature_resolution, probetemp); - dtostrfd(MAX31855_Result.ReferenceTemperature, Settings.flag2.temperature_resolution, referencetemp); - - if(Json){ - ResponseAppend_P(PSTR(",\"MAX31855\":{\"" D_JSON_PROBETEMPERATURE "\":%s,\"" D_JSON_REFERENCETEMPERATURE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ - probetemp, referencetemp, MAX31855_Result.ErrorCode); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_TEMP, probetemp); - } -#endif -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, MAX31855_Result.ProbeTemperature); - } -#endif - } else { -#ifdef USE_WEBSERVER - WSContentSend_PD(HTTP_SNS_TEMP, "MAX31855", probetemp, TempUnit()); -#endif - } -} - - - - - -bool Xsns39(uint8_t function) -{ - bool result = false; - if((pin[GPIO_MAX31855CS] < 99) && (pin[GPIO_MAX31855CLK] < 99) && (pin[GPIO_MAX31855DO] < 99)){ - - switch (function) { - case FUNC_INIT: - MAX31855_Init(); - break; - case FUNC_EVERY_SECOND: - MAX31855_GetResult(); - break; - case FUNC_JSON_APPEND: - MAX31855_Show(true); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MAX31855_Show(false); - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_40_pn532.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_40_pn532.ino" -#ifdef USE_PN532_HSU - -#define XSNS_40 40 - -#include - -TasmotaSerial *PN532_Serial; - -#define PN532_INVALID_ACK -1 -#define PN532_TIMEOUT -2 -#define PN532_INVALID_FRAME -3 -#define PN532_NO_SPACE -4 - -#define PN532_PREAMBLE 0x00 -#define PN532_STARTCODE1 0x00 -#define PN532_STARTCODE2 0xFF -#define PN532_POSTAMBLE 0x00 - -#define PN532_HOSTTOPN532 0xD4 -#define PN532_PN532TOHOST 0xD5 - -#define PN532_ACK_WAIT_TIME 0x0A - -#define PN532_COMMAND_GETFIRMWAREVERSION 0x02 -#define PN532_COMMAND_SAMCONFIGURATION 0x14 -#define PN532_COMMAND_RFCONFIGURATION 0x32 -#define PN532_COMMAND_INDATAEXCHANGE 0x40 -#define PN532_COMMAND_INLISTPASSIVETARGET 0x4A - -#define PN532_MIFARE_ISO14443A 0x00 -#define MIFARE_CMD_READ 0x30 -#define MIFARE_CMD_AUTH_A 0x60 -#define MIFARE_CMD_AUTH_B 0x61 -#define MIFARE_CMD_WRITE 0xA0 - -uint8_t pn532_model = 0; -uint8_t pn532_command = 0; -uint8_t pn532_scantimer = 0; - -uint8_t pn532_packetbuffer[64]; - -#ifdef USE_PN532_DATA_FUNCTION -uint8_t pn532_function = 0; -uint8_t pn532_newdata[16]; -uint8_t pn532_newdata_len = 0; -#endif - -void PN532_Init(void) -{ - if ((pin[GPIO_PN532_RXD] < 99) && (pin[GPIO_PN532_TXD] < 99)) { - PN532_Serial = new TasmotaSerial(pin[GPIO_PN532_RXD], pin[GPIO_PN532_TXD], 1); - if (PN532_Serial->begin(115200)) { - if (PN532_Serial->hardwareSerial()) { ClaimSerial(); } - PN532_wakeup(); - uint32_t ver = PN532_getFirmwareVersion(); - if (ver) { - PN532_setPassiveActivationRetries(0xFF); - PN532_SAMConfig(); - pn532_model = 1; - AddLog_P2(LOG_LEVEL_INFO,"NFC: PN532 NFC Reader detected (V%u.%u)",(ver>>16) & 0xFF, (ver>>8) & 0xFF); - } - } - } -} - -int8_t PN532_receive(uint8_t *buf, int len, uint16_t timeout) -{ - int read_bytes = 0; - int ret; - unsigned long start_millis; - while (read_bytes < len) { - start_millis = millis(); - do { - ret = PN532_Serial->read(); - if (ret >= 0) { - break; - } - } while((timeout == 0) || ((millis()- start_millis ) < timeout)); - - if (ret < 0) { - if (read_bytes) { - return read_bytes; - } else { - return PN532_TIMEOUT; - } - } - buf[read_bytes] = (uint8_t)ret; - read_bytes++; - } - return read_bytes; -} - -int8_t PN532_readAckFrame(void) -{ - const uint8_t PN532_ACK[] = {0, 0, 0xFF, 0, 0xFF, 0}; - uint8_t ackBuf[sizeof(PN532_ACK)]; - - if (PN532_receive(ackBuf, sizeof(PN532_ACK), PN532_ACK_WAIT_TIME) <= 0) { - return PN532_TIMEOUT; - } - - if (memcmp(&ackBuf, &PN532_ACK, sizeof(PN532_ACK))) { - return PN532_INVALID_ACK; - } - return 0; -} - -int8_t PN532_writeCommand(const uint8_t *header, uint8_t hlen, const uint8_t *body = 0, uint8_t blen = 0) -{ - - PN532_Serial->flush(); - - pn532_command = header[0]; - PN532_Serial->write((uint8_t)PN532_PREAMBLE); - PN532_Serial->write((uint8_t)PN532_STARTCODE1); - PN532_Serial->write(PN532_STARTCODE2); - - uint8_t length = hlen + blen + 1; - PN532_Serial->write(length); - PN532_Serial->write(~length + 1); - - PN532_Serial->write(PN532_HOSTTOPN532); - uint8_t sum = PN532_HOSTTOPN532; - - PN532_Serial->write(header, hlen); - for (uint32_t i = 0; i < hlen; i++) { - sum += header[i]; - } - - PN532_Serial->write(body, blen); - for (uint32_t i = 0; i < blen; i++) { - sum += body[i]; - } - - uint8_t checksum = ~sum + 1; - PN532_Serial->write(checksum); - PN532_Serial->write((uint8_t)PN532_POSTAMBLE); - - return PN532_readAckFrame(); -} - -int16_t PN532_readResponse(uint8_t buf[], uint8_t len, uint16_t timeout = 50) -{ - uint8_t tmp[3]; - - - if (PN532_receive(tmp, 3, timeout)<=0) { - return PN532_TIMEOUT; - } - if (0 != tmp[0] || 0!= tmp[1] || 0xFF != tmp[2]) { - return PN532_INVALID_FRAME; - } - - - uint8_t length[2]; - if (PN532_receive(length, 2, timeout) <= 0) { - return PN532_TIMEOUT; - } - - if (0 != (uint8_t)(length[0] + length[1])) { - return PN532_INVALID_FRAME; - } - length[0] -= 2; - if (length[0] > len) { - return PN532_NO_SPACE; - } - - - uint8_t cmd = pn532_command + 1; - if (PN532_receive(tmp, 2, timeout) <= 0) { - return PN532_TIMEOUT; - } - if (PN532_PN532TOHOST != tmp[0] || cmd != tmp[1]) { - return PN532_INVALID_FRAME; - } - - if (PN532_receive(buf, length[0], timeout) != length[0]) { - return PN532_TIMEOUT; - } - - uint8_t sum = PN532_PN532TOHOST + cmd; - for (uint32_t i=0; i status) { - return 0; - } - - response = pn532_packetbuffer[0]; - response <<= 8; - response |= pn532_packetbuffer[1]; - response <<= 8; - response |= pn532_packetbuffer[2]; - response <<= 8; - response |= pn532_packetbuffer[3]; - - return response; -} - -void PN532_wakeup(void) -{ - uint8_t wakeup[5] = {0x55, 0x55, 0, 0, 0 }; - PN532_Serial->write(wakeup,sizeof(wakeup)); - - - PN532_Serial->flush(); -} - -bool PN532_readPassiveTargetID(uint8_t cardbaudrate, uint8_t *uid, uint8_t *uidLength, uint16_t timeout = 50) -{ - pn532_packetbuffer[0] = PN532_COMMAND_INLISTPASSIVETARGET; - pn532_packetbuffer[1] = 1; - pn532_packetbuffer[2] = cardbaudrate; - if (PN532_writeCommand(pn532_packetbuffer, 3)) { - return 0x0; - } - - if (PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer), timeout) < 0) { - return 0x0; - } -# 274 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_40_pn532.ino" - if (pn532_packetbuffer[0] != 1) { - return 0; - } - - uint16_t sens_res = pn532_packetbuffer[2]; - sens_res <<= 8; - sens_res |= pn532_packetbuffer[3]; - - - *uidLength = pn532_packetbuffer[5]; - - for (uint32_t i = 0; i < pn532_packetbuffer[5]; i++) { - uid[i] = pn532_packetbuffer[6 + i]; - } - - return 1; -} - -bool PN532_setPassiveActivationRetries(uint8_t maxRetries) -{ - pn532_packetbuffer[0] = PN532_COMMAND_RFCONFIGURATION; - pn532_packetbuffer[1] = 5; - pn532_packetbuffer[2] = 0xFF; - pn532_packetbuffer[3] = 0x01; - pn532_packetbuffer[4] = maxRetries; - if (PN532_writeCommand(pn532_packetbuffer, 5)) { - return 0; - } - return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); -} - -bool PN532_SAMConfig(void) -{ - pn532_packetbuffer[0] = PN532_COMMAND_SAMCONFIGURATION; - pn532_packetbuffer[1] = 0x01; - pn532_packetbuffer[2] = 0x14; - pn532_packetbuffer[3] = 0x00; - if (PN532_writeCommand(pn532_packetbuffer, 4)) { - return false; - } - return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); -} - -#ifdef USE_PN532_DATA_FUNCTION - -uint8_t mifareclassic_AuthenticateBlock (uint8_t *uid, uint8_t uidLen, uint32_t blockNumber, uint8_t keyNumber, uint8_t *keyData) -{ - uint8_t i; - uint8_t _key[6]; - uint8_t _uid[7]; - uint8_t _uidLen; - - - memcpy(&_key, keyData, 6); - memcpy(&_uid, uid, uidLen); - _uidLen = uidLen; - - - pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; - pn532_packetbuffer[1] = 1; - pn532_packetbuffer[2] = (keyNumber) ? MIFARE_CMD_AUTH_B : MIFARE_CMD_AUTH_A; - pn532_packetbuffer[3] = blockNumber; - memcpy(&pn532_packetbuffer[4], &_key, 6); - for (i = 0; i < _uidLen; i++) { - pn532_packetbuffer[10 + i] = _uid[i]; - } - - if (PN532_writeCommand(pn532_packetbuffer, 10 + _uidLen)) { return 0; } - - - PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer)); - - - - - if (pn532_packetbuffer[0] != 0x00) { - - return 0; - } - - return 1; -} - -uint8_t mifareclassic_ReadDataBlock (uint8_t blockNumber, uint8_t *data) -{ - - pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; - pn532_packetbuffer[1] = 1; - pn532_packetbuffer[2] = MIFARE_CMD_READ; - pn532_packetbuffer[3] = blockNumber; - - - if (PN532_writeCommand(pn532_packetbuffer, 4)) { - return 0; - } - - - PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer)); - - - if (pn532_packetbuffer[0] != 0x00) { - return 0; - } - - - - memcpy (data, &pn532_packetbuffer[1], 16); - - return 1; -} - -uint8_t mifareclassic_WriteDataBlock (uint8_t blockNumber, uint8_t *data) -{ - - pn532_packetbuffer[0] = PN532_COMMAND_INDATAEXCHANGE; - pn532_packetbuffer[1] = 1; - pn532_packetbuffer[2] = MIFARE_CMD_WRITE; - pn532_packetbuffer[3] = blockNumber; - memcpy(&pn532_packetbuffer[4], data, 16); - - - if (PN532_writeCommand(pn532_packetbuffer, 20)) { - return 0; - } - - - return (0 < PN532_readResponse(pn532_packetbuffer, sizeof(pn532_packetbuffer))); -} - -#endif - -void PN532_ScanForTag(void) -{ - if (!pn532_model) { return; } - uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; - uint8_t uid_len = 0; - uint8_t card_data[16]; - bool erase_success = false; - bool set_success = false; - if (PN532_readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uid_len)) { - char uids[15]; - -#ifdef USE_PN532_DATA_FUNCTION - char card_datas[34]; -#endif - - ToHex_P((unsigned char*)uid, uid_len, uids, sizeof(uids)); - -#ifdef USE_PN532_DATA_FUNCTION - if (uid_len == 4) { - uint8_t keyuniversal[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - if (mifareclassic_AuthenticateBlock (uid, uid_len, 1, 1, keyuniversal)) { - if (mifareclassic_ReadDataBlock(1, card_data)) { -#ifdef USE_PN532_DATA_RAW - memcpy(&card_datas,&card_data,sizeof(card_data)); -#else - for (uint32_t i = 0;i < sizeof(card_data);i++) { - if ((isalpha(card_data[i])) || ((isdigit(card_data[i])))) { - card_datas[i] = char(card_data[i]); - } else { - card_datas[i] = '\0'; - } - } -#endif - } - if (pn532_function == 1) { - for (uint32_t i = 0;i<16;i++) { - card_data[i] = 0x00; - } - if (mifareclassic_WriteDataBlock(1, card_data)) { - erase_success = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase success")); - memcpy(&card_datas,&card_data,sizeof(card_data)); - } - } - if (pn532_function == 2) { -#ifdef USE_PN532_DATA_RAW - memcpy(&card_data,&pn532_newdata,sizeof(card_data)); - if (mifareclassic_WriteDataBlock(1, card_data)) { - set_success = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); - memcpy(&card_datas,&card_data,sizeof(card_data)); - } -#else - bool IsAlphaNumeric = true; - for (uint32_t i = 0;i < pn532_newdata_len;i++) { - if ((!isalpha(pn532_newdata[i])) && (!isdigit(pn532_newdata[i]))) { - IsAlphaNumeric = false; - } - } - if (IsAlphaNumeric) { - memcpy(&card_data,&pn532_newdata,pn532_newdata_len); - card_data[pn532_newdata_len] = '\0'; - if (mifareclassic_WriteDataBlock(1, card_data)) { - set_success = true; - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data write successful")); - memcpy(&card_datas,&card_data,sizeof(card_data)); - } - } else { - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Data must be alphanumeric")); - } -#endif - } - } else { - sprintf(card_datas,"AUTHFAIL"); - } - } - switch (pn532_function) { - case 0x01: - if (!erase_success) { - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Erase fail - exiting erase mode")); - } - break; - case 0x02: - if (!set_success) { - AddLog_P(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Write failed - exiting set mode")); - } - default: - break; - } - pn532_function = 0; -#endif - -#ifdef USE_PN532_DATA_FUNCTION - ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\", \"DATA\":\"%s\"}}"), uids, card_datas); -#else - ResponseTime_P(PSTR(",\"PN532\":{\"UID\":\"%s\"}}"), uids); -#endif - - MqttPublishTeleSensor(); - -#ifdef USE_PN532_CAUSE_EVENTS - - char command[71]; -#ifdef USE_PN532_DATA_FUNCTION - sprintf(command,"backlog event PN532_UID=%s;event PN532_DATA=%s",uids,card_datas); -#else - sprintf(command,"event PN532_UID=%s",uids); -#endif - ExecuteCommand(command, SRC_RULE); -#endif - - pn532_scantimer = 7; - } -} - -#ifdef USE_PN532_DATA_FUNCTION - -bool PN532_Command(void) -{ - bool serviced = true; - uint8_t paramcount = 0; - if (XdrvMailbox.data_len > 0) { - paramcount=1; - } else { - serviced = false; - return serviced; - } - char sub_string[XdrvMailbox.data_len]; - char sub_string_tmp[XdrvMailbox.data_len]; - for (uint32_t ca=0;ca 1) { - if (XdrvMailbox.data[XdrvMailbox.data_len-1] == ',') { - serviced = false; - return serviced; - } - sprintf(sub_string_tmp,subStr(sub_string, XdrvMailbox.data, ",", 2)); - pn532_newdata_len = strlen(sub_string_tmp); - if (pn532_newdata_len > 15) { pn532_newdata_len = 15; } - memcpy(&pn532_newdata,&sub_string_tmp,pn532_newdata_len); - pn532_newdata[pn532_newdata_len] = 0x00; - pn532_function = 2; - AddLog_P2(LOG_LEVEL_INFO, PSTR("NFC: PN532 NFC - Next scanned tag data block 1 will be set to '%s'"), pn532_newdata); - ResponseTime_P(PSTR(",\"PN532\":{\"COMMAND\":\"S\"}}")); - return serviced; - } - } -} - -#endif - -bool Xsns40(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_INIT: - PN532_Init(); - result = true; - break; - case FUNC_EVERY_50_MSECOND: - break; - case FUNC_EVERY_100_MSECOND: - break; - case FUNC_EVERY_250_MSECOND: - if (pn532_scantimer > 0) { - pn532_scantimer--; - } else { - PN532_ScanForTag(); - } - break; - case FUNC_EVERY_SECOND: - break; -#ifdef USE_PN532_DATA_FUNCTION - case FUNC_COMMAND_SENSOR: - if (XSNS_40 == XdrvMailbox.index) { - result = PN532_Command(); - } - break; -#endif - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_41_max44009.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_41_max44009.ino" -#ifdef USE_I2C -#ifdef USE_MAX44009 - - - - - - -#define XSNS_41 41 -#define XI2C_28 28 - -#define MAX44009_ADDR1 0x4A -#define MAX44009_ADDR2 0x4B -#define MAX44009_NO_REGISTERS 8 -#define REG_CONFIG 0x02 -#define REG_LUMINANCE 0x03 -#define REG_LOWER_THRESHOLD 0x06 -#define REG_THRESHOLD_TIMER 0x07 - -#define MAX44009_CONTINUOUS_AUTO_MODE 0x80 - -uint8_t max44009_address; -uint8_t max44009_addresses[] = { MAX44009_ADDR1, MAX44009_ADDR2, 0 }; -uint8_t max44009_found = 0; -uint8_t max44009_valid = 0; -float max44009_illuminance = 0; -char max44009_types[] = "MAX44009"; - -bool Max4409Read_lum(void) -{ - max44009_valid = 0; - uint8_t regdata[2]; - - - if (I2cValidRead16((uint16_t *)®data, max44009_address, REG_LUMINANCE)) { - int exponent = (regdata[0] & 0xF0) >> 4; - int mantissa = ((regdata[0] & 0x0F) << 4) | (regdata[1] & 0x0F); - max44009_illuminance = (float)(((0x00000001 << exponent) * (float)mantissa) * 0.045); - max44009_valid = 1; - return true; - } else { - return false; - } -} - - - -void Max4409Detect(void) -{ - uint8_t buffer1; - uint8_t buffer2; - for (uint32_t i = 0; 0 != max44009_addresses[i]; i++) { - - max44009_address = max44009_addresses[i]; - if (I2cActive(max44009_address)) { continue; } - - if ((I2cValidRead8(&buffer1, max44009_address, REG_LOWER_THRESHOLD)) && - (I2cValidRead8(&buffer2, max44009_address, REG_THRESHOLD_TIMER))) { - - if ((0x00 == buffer1) && - (0xFF == buffer2)) { - - - - Wire.beginTransmission(max44009_address); - - - Wire.write(REG_CONFIG); - Wire.write(MAX44009_CONTINUOUS_AUTO_MODE); - if (0 == Wire.endTransmission()) { - I2cSetActiveFound(max44009_address, max44009_types); - max44009_found = 1; - break; - } - } - } - } -} - -void Max4409EverySecond(void) -{ - Max4409Read_lum(); -} - -void Max4409Show(bool json) -{ - char illum_str[8]; - - if (max44009_valid) { - - - - uint8_t prec = 0; - if (10 > max44009_illuminance ) { - prec = 3; - } else if (100 > max44009_illuminance) { - prec = 2; - } else if (1000 > max44009_illuminance) { - prec = 1; - } - dtostrf(max44009_illuminance, sizeof(illum_str) -1, prec, illum_str); - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_ILLUMINANCE "\":%s}"), max44009_types, illum_str); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_ILLUMINANCE, illum_str); - } -#endif -#ifdef USE_WEBSERVER - } else { - - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, max44009_types, (int)max44009_illuminance); -#endif - } - } -} - - - - - -bool Xsns41(uint8_t function) -{ - if (!I2cEnabled(XI2C_28)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Max4409Detect(); - } - else if (max44009_found) { - switch (function) { - case FUNC_EVERY_SECOND: - Max4409EverySecond(); - break; - case FUNC_JSON_APPEND: - Max4409Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Max4409Show(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_42_scd30.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_42_scd30.ino" -#ifdef USE_I2C -#ifdef USE_SCD30 - -#define XSNS_42 42 -#define XI2C_29 29 - - - -#define SCD30_ADDRESS 0x61 - -#define SCD30_MAX_MISSED_READS 3 -#define SCD30_STATE_NO_ERROR 0 -#define SCD30_STATE_ERROR_DATA_CRC 1 -#define SCD30_STATE_ERROR_READ_MEAS 2 -#define SCD30_STATE_ERROR_SOFT_RESET 3 -#define SCD30_STATE_ERROR_I2C_RESET 4 -#define SCD30_STATE_ERROR_UNKNOWN 5 - -#include "Arduino.h" -#include - -#define D_CMND_SCD30 "SCD30" - -const char S_JSON_SCD30_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d}"; -const char S_JSON_SCD30_COMMAND_NFW_VALUE[] PROGMEM = "{\"" D_CMND_SCD30 "%s\":%d.%d}"; -const char S_JSON_SCD30_COMMAND[] PROGMEM = "{\"" D_CMND_SCD30 "%s\"}"; -const char kSCD30_Commands[] PROGMEM = "Alt|Auto|Cal|FW|Int|Pres|TOff"; - - - - - -enum SCD30_Commands { - CMND_SCD30_ALTITUDE, - CMND_SCD30_AUTOMODE, - CMND_SCD30_CALIBRATE, - CMND_SCD30_FW, - CMND_SCD30_INTERVAL, - CMND_SCD30_PRESSURE, - CMND_SCD30_TEMPOFFSET -}; - -FrogmoreScd30 scd30; - -bool scd30Found = false; -bool scd30IsDataValid = false; -int scd30ErrorState = SCD30_STATE_NO_ERROR; -uint16_t scd30Interval_sec; -int scd30Loop_count = 0; -int scd30DataNotAvailable_count = 0; -int scd30GoodMeas_count = 0; -int scd30Reset_count = 0; -int scd30CrcError_count = 0; -int scd30Co2Zero_count = 0; -int i2cReset_count = 0; -uint16_t scd30_CO2 = 0; -uint16_t scd30_CO2EAvg = 0; -float scd30_Humid = 0.0; -float scd30_Temp = 0.0; - -void Scd30Detect(void) -{ - if (I2cActive(SCD30_ADDRESS)) { return; } - - scd30.begin(); - - uint8_t major = 0; - uint8_t minor = 0; - if (scd30.getFirmwareVersion(&major, &minor)) { return; } - uint16_t interval_sec; - if (scd30.getMeasurementInterval(&scd30Interval_sec)) { return; } - if (scd30.beginMeasuring()) { return; } - - I2cSetActiveFound(SCD30_ADDRESS, "SCD30"); - scd30Found = true; - - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("SCD: FW v%d.%d"), major, minor); -} - - -void Scd30Update(void) -{ - scd30Loop_count++; - if (scd30Loop_count > (scd30Interval_sec - 1)) { - int error = 0; - switch (scd30ErrorState) { - case SCD30_STATE_NO_ERROR: { - error = scd30.readMeasurement(&scd30_CO2, &scd30_CO2EAvg, &scd30_Temp, &scd30_Humid); - switch (error) { - case ERROR_SCD30_NO_ERROR: - scd30Loop_count = 0; - scd30IsDataValid = true; - scd30GoodMeas_count++; - break; - - case ERROR_SCD30_NO_DATA: - scd30DataNotAvailable_count++; - break; - - case ERROR_SCD30_CRC_ERROR: - scd30ErrorState = SCD30_STATE_ERROR_DATA_CRC; - scd30CrcError_count++; -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: CRC error, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld"), - scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); -#endif - break; - - case ERROR_SCD30_CO2_ZERO: - scd30Co2Zero_count++; -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: CO2 zero, CRC error: %ld, CO2 zero: %ld, good: %ld, no data: %ld, sc30_reset: %ld, i2c_reset: %ld"), - scd30CrcError_count, scd30Co2Zero_count, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); -#endif - break; - - default: { - scd30ErrorState = SCD30_STATE_ERROR_READ_MEAS; -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: Update: ReadMeasurement error: 0x%lX, counter: %ld"), error, scd30Loop_count); -#endif - return; - } - break; - } - } - break; - - case SCD30_STATE_ERROR_DATA_CRC: { - -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), - scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: got CRC error, try again, counter: %ld"), scd30Loop_count); -#endif - scd30ErrorState = ERROR_SCD30_NO_ERROR; - } - break; - - case SCD30_STATE_ERROR_READ_MEAS: { - -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), - scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: not answering, sending soft reset, counter: %ld"), scd30Loop_count); -#endif - scd30Reset_count++; - error = scd30.softReset(); - if (error) { -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: resetting got error: 0x%lX"), error); -#endif - error >>= 8; - if (error == 4) { - scd30ErrorState = SCD30_STATE_ERROR_SOFT_RESET; - } else { - scd30ErrorState = SCD30_STATE_ERROR_UNKNOWN; - } - } else { - scd30ErrorState = ERROR_SCD30_NO_ERROR; - } - } - break; - - case SCD30_STATE_ERROR_SOFT_RESET: { - -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: in error state: %d, good: %ld, no data: %ld, sc30 reset: %ld, i2c reset: %ld"), - scd30ErrorState, scd30GoodMeas_count, scd30DataNotAvailable_count, scd30Reset_count, i2cReset_count); - AddLog_P(LOG_LEVEL_ERROR, PSTR("SCD30: clearing i2c bus")); -#endif - i2cReset_count++; - error = scd30.clearI2CBus(); - if (error) { - scd30ErrorState = SCD30_STATE_ERROR_I2C_RESET; -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: error clearing i2c bus: 0x%lX"), error); -#endif - } else { - scd30ErrorState = ERROR_SCD30_NO_ERROR; - } - } - break; - - default: { - -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: unknown error state: 0x%lX"), scd30ErrorState); -#endif - scd30ErrorState = SCD30_STATE_ERROR_SOFT_RESET; - } - } - - if (scd30Loop_count > (SCD30_MAX_MISSED_READS * scd30Interval_sec)) { - scd30IsDataValid = false; - } - } -} - - -int Scd30GetCommand(int command_code, uint16_t *pvalue) -{ - switch (command_code) - { - case CMND_SCD30_ALTITUDE: - return scd30.getAltitudeCompensation(pvalue); - break; - - case CMND_SCD30_AUTOMODE: - return scd30.getCalibrationType(pvalue); - break; - - case CMND_SCD30_CALIBRATE: - return scd30.getForcedRecalibrationFactor(pvalue); - break; - - case CMND_SCD30_INTERVAL: - return scd30.getMeasurementInterval(pvalue); - break; - - case CMND_SCD30_PRESSURE: - return scd30.getAmbientPressure(pvalue); - break; - - case CMND_SCD30_TEMPOFFSET: - return scd30.getTemperatureOffset(pvalue); - break; - - default: - - break; - } -} - -int Scd30SetCommand(int command_code, uint16_t value) -{ - switch (command_code) - { - case CMND_SCD30_ALTITUDE: - return scd30.setAltitudeCompensation(value); - break; - - case CMND_SCD30_AUTOMODE: - return scd30.setCalibrationType(value); - break; - - case CMND_SCD30_CALIBRATE: - return scd30.setForcedRecalibrationFactor(value); - break; - - case CMND_SCD30_INTERVAL: - { - int error = scd30.setMeasurementInterval(value); - if (!error) - { - scd30Interval_sec = value; - } - - return error; - } - break; - - case CMND_SCD30_PRESSURE: - return scd30.setAmbientPressure(value); - break; - - case CMND_SCD30_TEMPOFFSET: - return scd30.setTemperatureOffset(value); - break; - - default: - - break; - } -} - - - - - -bool Scd30CommandSensor() -{ - char command[CMDSZ]; - bool serviced = true; - uint8_t prefix_len = strlen(D_CMND_SCD30); - - if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_SCD30), prefix_len)) { - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + prefix_len, kSCD30_Commands); - - switch (command_code) { - case CMND_SCD30_ALTITUDE: - case CMND_SCD30_AUTOMODE: - case CMND_SCD30_CALIBRATE: - case CMND_SCD30_INTERVAL: - case CMND_SCD30_PRESSURE: - case CMND_SCD30_TEMPOFFSET: - { - uint16_t value = 0; - if (XdrvMailbox.data_len > 0) - { - value = XdrvMailbox.payload; - Scd30SetCommand(command_code, value); - } - else - { - Scd30GetCommand(command_code, &value); - } - - Response_P(S_JSON_SCD30_COMMAND_NVALUE, command, value); - } - break; - - case CMND_SCD30_FW: - { - uint8_t major = 0; - uint8_t minor = 0; - int error; - error = scd30.getFirmwareVersion(&major, &minor); - if (error) - { -#ifdef SCD30_DEBUG - AddLog_P2(LOG_LEVEL_ERROR, PSTR("SCD30: error getting FW version: 0x%lX"), error); -#endif - serviced = false; - } - else - { - Response_P(S_JSON_SCD30_COMMAND_NFW_VALUE, command, major, minor); - } - } - break; - - default: - - serviced = false; - break; - } - } - return serviced; -} - -void Scd30Show(bool json) -{ - if (scd30IsDataValid) - { - char humidity[10]; - dtostrfd(ConvertHumidity(scd30_Humid), Settings.flag2.humidity_resolution, humidity); - char temperature[10]; - dtostrfd(ConvertTemp(scd30_Temp), Settings.flag2.temperature_resolution, temperature); - - if (json) { - - ResponseAppend_P(PSTR(",\"SCD30\":{\"" D_JSON_CO2 "\":%d,\"" D_JSON_ECO2 "\":%d,\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}"), - scd30_CO2, scd30_CO2EAvg, temperature, humidity); -#ifdef USE_DOMOTICZ - if (0 == tele_period) - { - DomoticzSensor(DZ_AIRQUALITY, scd30_CO2); - DomoticzTempHumSensor(temperature, humidity); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_CO2EAVG, "SCD30", scd30_CO2EAvg); - WSContentSend_PD(HTTP_SNS_CO2, "SCD30", scd30_CO2); - WSContentSend_PD(HTTP_SNS_TEMP, "SCD30", temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, "SCD30", humidity); -#endif - } - } -} - - - - - -bool Xsns42(byte function) -{ - if (!I2cEnabled(XI2C_29)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Scd30Detect(); - } - else if (scd30Found) { - switch (function) { - case FUNC_EVERY_SECOND: - Scd30Update(); - break; - case FUNC_COMMAND: - result = Scd30CommandSensor(); - break; - case FUNC_JSON_APPEND: - Scd30Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Scd30Show(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_43_hre.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_43_hre.ino" -#ifdef USE_HRE -# 49 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_43_hre.ino" -#define XSNS_43 43 - -enum hre_states { - hre_idle, - hre_sync, - hre_syncing, - hre_read, - hre_reading, - hre_sleep, - hre_sleeping -}; - -hre_states hre_state = hre_idle; - -float hre_usage = 0; -float hre_rate = 0; -uint32_t hre_usage_time = 0; - -int hre_read_errors = 0; -bool hre_good = false; - - - -int hreReadBit() -{ - digitalWrite(pin[GPIO_HRE_CLOCK], HIGH); - delay(1); - int bit = digitalRead(pin[GPIO_HRE_DATA]); - digitalWrite(pin[GPIO_HRE_CLOCK], LOW); - delay(1); - return bit; -} - - - -char hreReadChar(int &parity_errors) -{ - - hreReadBit(); - - unsigned ch=0; - int sum=0; - for (uint32_t i=0; i<7; i++) - { - int b = hreReadBit(); - ch |= b << i; - sum += b; - } - - - if ( (sum & 0x1) != hreReadBit()) - parity_errors++; - - - hreReadBit(); - - return ch; -} - -void hreInit(void) -{ - hre_read_errors = 0; - hre_good = false; - - pinMode(pin[GPIO_HRE_CLOCK], OUTPUT); - pinMode(pin[GPIO_HRE_DATA], INPUT); - - - - digitalWrite(pin[GPIO_HRE_CLOCK], LOW); - - hre_state = hre_sync; -} - - -void hreEvery50ms(void) -{ - static int sync_counter = 0; - static int sync_run = 0; - - static uint32_t curr_start = 0; - static int read_counter = 0; - static int parity_errors = 0; - static char buff[46]; - - static char ch; - static size_t i; - - switch (hre_state) - { - case hre_sync: - if (uptime < 10) - break; - sync_run = 0; - sync_counter = 0; - hre_state = hre_syncing; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_syncing")); - break; - - case hre_syncing: - - - for (uint32_t i=0; i<20; i++) - { - if (hreReadBit()) - sync_run++; - else - sync_run = 0; - if (sync_run == 62) - { - hre_state = hre_read; - break; - } - sync_counter++; - } - - if (sync_counter > 1000) - { - hre_state = hre_sleep; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE D_ERROR)); - } - break; - - - case hre_read: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "sync_run:%d, sync_counter:%d"), sync_run, sync_counter); - read_counter = 0; - parity_errors = 0; - curr_start = uptime; - memset(buff, 0, sizeof(buff)); - hre_state = hre_reading; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_reading")); - - - - - - case hre_reading: - - buff[read_counter++] = hreReadChar(parity_errors); - buff[read_counter++] = hreReadChar(parity_errors); - - if (read_counter == 46) - { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "pe:%d, re:%d, buff:%s"), - parity_errors, hre_read_errors, buff); - if (parity_errors == 0) - { - float curr_usage; - curr_usage = 0.01 * atol(buff+24); - if (hre_usage_time) - { - double dt = 1.666e-2 * (curr_start - hre_usage_time); - hre_rate = (curr_usage - hre_usage)/dt; - } - hre_usage = curr_usage; - hre_usage_time = curr_start; - hre_good = true; - - hre_state = hre_sleep; - } - else - { - hre_read_errors++; - hre_state = hre_sleep; - } - } - break; - - case hre_sleep: - hre_usage_time = curr_start; - hre_state = hre_sleeping; - AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HRE "hre_state:hre_sleeping")); - - case hre_sleeping: - - - if (uptime - hre_usage_time >= 27) - hre_state = hre_sync; - } -} - -void hreShow(boolean json) -{ - if (!hre_good) - return; - - const char *id = "HRE"; - - char usage[16]; - char rate[16]; - dtostrfd(hre_usage, 2, usage); - dtostrfd(hre_rate, 3, rate); - - if (json) - { - ResponseAppend_P(JSON_SNS_GNGPM, id, usage, rate); -#ifdef USE_WEBSERVER - } - else - { - WSContentSend_PD(HTTP_SNS_GALLONS, id, usage); - WSContentSend_PD(HTTP_SNS_GPM, id, rate); -#endif - } -} - - - - - -bool Xsns43(byte function) -{ - - if (pin[GPIO_HRE_CLOCK] >= 99 || pin[GPIO_HRE_DATA] >= 99) - return false; - - switch (function) - { - case FUNC_INIT: - hreInit(); - break; - case FUNC_EVERY_50_MSECOND: - hreEvery50ms(); - break; - case FUNC_EVERY_SECOND: - break; - case FUNC_JSON_APPEND: - hreShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - hreShow(0); - break; -#endif - } - return false; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_44_sps30.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_44_sps30.ino" -#ifdef USE_I2C -#ifdef USE_SPS30 - -#define XSNS_44 44 -#define XI2C_30 30 - -#define SPS30_ADDR 0x69 - -#include -#include - -uint8_t sps30_ready = 0; -uint8_t sps30_running; - -struct SPS30 { - float PM1_0; - float PM2_5; - float PM4_0; - float PM10; - float NCPM0_5; - float NCPM1_0; - float NCPM2_5; - float NCPM4_0; - float NCPM10; - float TYPSIZ; -} sps30_result; - -#define SPS_CMD_START_MEASUREMENT 0x0010 -#define SPS_CMD_START_MEASUREMENT_ARG 0x0300 -#define SPS_CMD_STOP_MEASUREMENT 0x0104 -#define SPS_CMD_READ_MEASUREMENT 0x0300 -#define SPS_CMD_GET_DATA_READY 0x0202 -#define SPS_CMD_AUTOCLEAN_INTERVAL 0x8004 -#define SPS_CMD_CLEAN 0x5607 -#define SPS_CMD_GET_ACODE 0xd025 -#define SPS_CMD_GET_SERIAL 0xd033 -#define SPS_CMD_RESET 0xd304 -#define SPS_WRITE_DELAY_US 20000 -#define SPS_MAX_SERIAL_LEN 32 - -uint8_t sps30_calc_CRC(uint8_t *data) { - uint8_t crc = 0xFF; - for (uint32_t i = 0; i < 2; i++) { - crc ^= data[i]; - for (uint32_t bit = 8; bit > 0; --bit) { - if(crc & 0x80) { - crc = (crc << 1) ^ 0x31u; - } else { - crc = (crc << 1); - } - } - } - return crc; -} - -void CmdClean(void); - -unsigned char twi_readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop); - -void sps30_get_data(uint16_t cmd, uint8_t *data, uint8_t dlen) { -unsigned char cmdb[2]; -uint8_t tmp[3]; -uint8_t index=0; -memset(data,0,dlen); -uint8_t twi_buff[64]; - - Wire.beginTransmission(SPS30_ADDR); - cmdb[0]=cmd>>8; - cmdb[1]=cmd; - Wire.write(cmdb,2); - Wire.endTransmission(); - - - dlen/=2; - dlen*=3; - - twi_readFrom(SPS30_ADDR,twi_buff,dlen,1); - - uint8_t bind=0; - while (bind>8; - cmdb[1]=cmd; - - if (cmd==SPS_CMD_START_MEASUREMENT) { - cmdb[2]=SPS_CMD_START_MEASUREMENT_ARG>>8; - cmdb[3]=SPS_CMD_START_MEASUREMENT_ARG&0xff; - cmdb[4]=sps30_calc_CRC(&cmdb[2]); - Wire.write(cmdb,5); - } else { - Wire.write(cmdb,2); - } - Wire.endTransmission(); -} - -void SPS30_Detect(void) -{ - if (!I2cSetDevice(SPS30_ADDR)) { return; } - I2cSetActiveFound(SPS30_ADDR, "SPS30"); - - uint8_t dcode[32]; - sps30_get_data(SPS_CMD_GET_SERIAL,dcode,sizeof(dcode)); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("sps30 found with serial: %s"),dcode); - sps30_cmd(SPS_CMD_START_MEASUREMENT); - sps30_running = 1; - sps30_ready = 1; -} - -#define D_UNIT_PM "ug/m3" -#define D_UNIT_NCPM "#/m3" - -#ifdef USE_WEBSERVER -const char HTTP_SNS_SPS30_a[] PROGMEM ="{s}SPS30 " "%s" "{m}%s " D_UNIT_PM "{e}"; -const char HTTP_SNS_SPS30_b[] PROGMEM ="{s}SPS30 " "%s" "{m}%s " D_UNIT_NCPM "{e}"; -const char HTTP_SNS_SPS30_c[] PROGMEM ="{s}SPS30 " "TYPSIZ" "{m}%s " "um" "{e}"; -#endif - -#define PMDP 2 - -#define SPS30_HOURS Settings.sps30_inuse_hours - - - -void SPS30_Every_Second() { - if (!sps30_running) return; - - if (uptime%10==0) { - uint8_t vars[sizeof(float)*10]; - sps30_get_data(SPS_CMD_READ_MEASUREMENT,vars,sizeof(vars)); - float *fp=&sps30_result.PM1_0; - - typedef union { - uint8_t array[4]; - float value; - } ByteToFloat; - - ByteToFloat conv; - - for (uint32_t count=0; count<10; count++) { - for (uint32_t i = 0; i < 4; i++){ - conv.array[3-i] = vars[count*sizeof(float)+i]; - } - *fp++=conv.value; - } - } - - if (uptime%3600==0 && uptime>60) { - - - SPS30_HOURS++; - if (SPS30_HOURS>(7*24)) { - CmdClean(); - SPS30_HOURS=0; - } - } - -} - -void SPS30_Show(bool json) -{ - if (!sps30_running) { return; } - - char str[64]; - if (json) { - dtostrfd(sps30_result.PM1_0,PMDP,str); - ResponseAppend_P(PSTR(",\"SPS30\":{\"" "PM1_0" "\":%s"), str); - dtostrfd(sps30_result.PM2_5,PMDP,str); - ResponseAppend_P(PSTR(",\"" "PM2_5" "\":%s"), str); - dtostrfd(sps30_result.PM4_0,PMDP,str); - ResponseAppend_P(PSTR(",\"" "PM4_0" "\":%s"), str); - dtostrfd(sps30_result.PM10,PMDP,str); - ResponseAppend_P(PSTR(",\"" "PM10" "\":%s"), str); - dtostrfd(sps30_result.NCPM0_5,PMDP,str); - ResponseAppend_P(PSTR(",\"" "NCPM0_5" "\":%s"), str); - dtostrfd(sps30_result.NCPM1_0,PMDP,str); - ResponseAppend_P(PSTR(",\"" "NCPM1_0" "\":%s"), str); - dtostrfd(sps30_result.NCPM2_5,PMDP,str); - ResponseAppend_P(PSTR(",\"" "NCPM2_5" "\":%s"), str); - dtostrfd(sps30_result.NCPM4_0,PMDP,str); - ResponseAppend_P(PSTR(",\"" "NCPM4_0" "\":%s"), str); - dtostrfd(sps30_result.NCPM10,PMDP,str); - ResponseAppend_P(PSTR(",\"" "NCPM10" "\":%s"), str); - dtostrfd(sps30_result.TYPSIZ,PMDP,str); - ResponseAppend_P(PSTR(",\"" "TYPSIZ" "\":%s}"), str); - -#ifdef USE_WEBSERVER - } else { - dtostrfd(sps30_result.PM1_0,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 1.0",str); - dtostrfd(sps30_result.PM2_5,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 2.5",str); - dtostrfd(sps30_result.PM4_0,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 4.0",str); - dtostrfd(sps30_result.PM10,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_a,"PM 10",str); - dtostrfd(sps30_result.NCPM0_5,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 0.5",str); - dtostrfd(sps30_result.NCPM1_0,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 1.0",str); - dtostrfd(sps30_result.NCPM2_5,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 2.5",str); - dtostrfd(sps30_result.NCPM4_0,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 4.0",str); - dtostrfd(sps30_result.NCPM10,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_b,"NCPM 10",str); - dtostrfd(sps30_result.TYPSIZ,PMDP,str); - WSContentSend_PD(HTTP_SNS_SPS30_c,str); -#endif - } -} - -void CmdClean(void) -{ - sps30_cmd(SPS_CMD_CLEAN); - ResponseTime_P(PSTR(",\"SPS30\":{\"CFAN\":\"true\"}}")); - MqttPublishTeleSensor(); -} - -bool SPS30_cmd(void) -{ - bool serviced = true; - if (XdrvMailbox.data_len > 0) { - char *cp=XdrvMailbox.data; - if (*cp=='c') { - - CmdClean(); - } else if (*cp=='0' || *cp=='1') { - sps30_running=*cp&1; - sps30_cmd(sps30_running?SPS_CMD_START_MEASUREMENT:SPS_CMD_STOP_MEASUREMENT); - } else { - serviced=false; - } - } - Response_P(PSTR("{\"SPS30\":\"%s\"}"), sps30_running?"running":"stopped"); - - return serviced; -} - - - - - -bool Xsns44(byte function) -{ - if (!I2cEnabled(XI2C_30)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - SPS30_Detect(); - } - else if (sps30_ready) { - switch (function) { - case FUNC_EVERY_SECOND: - SPS30_Every_Second(); - break; - case FUNC_JSON_APPEND: - SPS30_Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - SPS30_Show(0); - break; - #endif - case FUNC_COMMAND_SENSOR: - if (XSNS_44 == XdrvMailbox.index) { - result = SPS30_cmd(); - } - break; - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_45_vl53l0x.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_45_vl53l0x.ino" -#ifdef USE_I2C -#ifdef USE_VL53L0X - -#define XSNS_45 45 -#define XI2C_31 31 - -#include -#include "VL53L0X.h" -VL53L0X sensor; - -uint8_t vl53l0x_ready = 0; -uint16_t vl53l0x_distance; -uint16_t Vl53l0_buffer[5]; -uint8_t Vl53l0_index; - - - -void Vl53l0Detect(void) -{ - if (!I2cSetDevice(0x29)) { return; } - - if (!sensor.init()) { return; } - - I2cSetActiveFound(sensor.getAddress(), "VL53L0X"); - - sensor.setTimeout(500); - - - - - - sensor.startContinuous(); - vl53l0x_ready = 1; - - Vl53l0_index=0; -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_VL53L0X[] PROGMEM = - "{s}VL53L0X " D_DISTANCE "{m}%d" D_UNIT_MILLIMETER "{e}"; -#endif - -#define USE_VL_MEDIAN - -void Vl53l0Every_250MSecond(void) -{ - uint16_t tbuff[5],tmp; - uint8_t flag; - - - uint16_t dist = sensor.readRangeContinuousMillimeters(); - if (dist==0 || dist>2000) { - dist=9999; - } - -#ifdef USE_VL_MEDIAN - - Vl53l0_buffer[Vl53l0_index]=dist; - Vl53l0_index++; - if (Vl53l0_index>=5) Vl53l0_index=0; - - - memmove(tbuff,Vl53l0_buffer,sizeof(tbuff)); - for (byte ocnt=0; ocnt<5; ocnt++) { - flag=0; - for (byte count=0; count<4; count++) { - if (tbuff[count]>tbuff[count+1]) { - tmp=tbuff[count]; - tbuff[count]=tbuff[count+1]; - tbuff[count+1]=tmp; - flag=1; - } - } - if (!flag) break; - } - vl53l0x_distance=tbuff[2]; -#else - vl53l0x_distance=dist; -#endif -} - -void Vl53l0Show(boolean json) -{ - if (json) { - ResponseAppend_P(PSTR(",\"VL53L0X\":{\"" D_JSON_DISTANCE "\":%d}"), vl53l0x_distance); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_VL53L0X, vl53l0x_distance); -#endif - } -} - - - - - -bool Xsns45(byte function) -{ - if (!I2cEnabled(XI2C_31)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Vl53l0Detect(); - } - else if (vl53l0x_ready) { - switch (function) { - case FUNC_EVERY_250_MSECOND: - Vl53l0Every_250MSecond(); - break; - case FUNC_JSON_APPEND: - Vl53l0Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Vl53l0Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_46_MLX90614.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_46_MLX90614.ino" -#ifdef USE_I2C -#ifdef USE_MLX90614 - -#define XSNS_46 46 -#define XI2C_32 32 - -#define I2_ADR_IRT 0x5a - -#define MLX90614_RAWIR1 0x04 -#define MLX90614_RAWIR2 0x05 -#define MLX90614_TA 0x06 -#define MLX90614_TOBJ1 0x07 -#define MLX90614_TOBJ2 0x08 - -struct { - union { - uint16_t value; - uint32_t i2c_buf; - }; - float obj_temp; - float amb_temp; - bool ready = false; -} mlx90614; - -void MLX90614_Init(void) -{ - if (!I2cSetDevice(I2_ADR_IRT)) { return; } - I2cSetActiveFound(I2_ADR_IRT, "MLX90614"); - mlx90614.ready = true; -} - -void MLX90614_Every_Second(void) -{ - mlx90614.i2c_buf = I2cRead24(I2_ADR_IRT, MLX90614_TOBJ1); - if (mlx90614.value & 0x8000) { - mlx90614.obj_temp = -999; - } else { - mlx90614.obj_temp = ((float)mlx90614.value * 0.02) - 273.15; - } - mlx90614.i2c_buf = I2cRead24(I2_ADR_IRT,MLX90614_TA); - if (mlx90614.value & 0x8000) { - mlx90614.amb_temp = -999; - } else { - mlx90614.amb_temp = ((float)mlx90614.value * 0.02) - 273.15; - } -} - -#ifdef USE_WEBSERVER - const char HTTP_IRTMP[] PROGMEM = - "{s}MXL90614 " "OBJ-" D_TEMPERATURE "{m}%s C" "{e}" - "{s}MXL90614 " "AMB-" D_TEMPERATURE "{m}%s C" "{e}"; -#endif - -void MLX90614_Show(uint8_t json) -{ - char obj_tstr[16]; - dtostrfd(mlx90614.obj_temp, Settings.flag2.temperature_resolution, obj_tstr); - char amb_tstr[16]; - dtostrfd(mlx90614.amb_temp, Settings.flag2.temperature_resolution, amb_tstr); - - if (json) { - ResponseAppend_P(PSTR(",\"MLX90614\":{\"OBJTMP\":%s,\"AMBTMP\":%s}"), obj_tstr, amb_tstr); -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_IRTMP, obj_tstr, amb_tstr); -#endif - } -} - - - - - -bool Xsns46(byte function) -{ - if (!I2cEnabled(XI2C_32)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - MLX90614_Init(); - } - else if (mlx90614.ready) { - switch (function) { - case FUNC_EVERY_SECOND: - MLX90614_Every_Second(); - break; - case FUNC_JSON_APPEND: - MLX90614_Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MLX90614_Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_47_max31865.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_47_max31865.ino" -#ifdef USE_MAX31865 - -#ifndef USE_SPI -#error "MAX31865 requires USE_SPI enabled" -#endif - -#include "Adafruit_MAX31865.h" - -#define XSNS_47 47 - -#if MAX31865_PTD_WIRES == 4 - #define PTD_WIRES MAX31865_4WIRE -#elif MAX31865_PTD_WIRES == 3 - #define PTD_WIRES MAX31865_3WIRE -#else - #define PTD_WIRES MAX31865_2WIRE -#endif - -int8_t init_status = 0; - -Adafruit_MAX31865 max31865; - -struct MAX31865_Result_Struct { - uint8_t ErrorCode; - uint16_t Rtd; - float PtdResistance; - float PtdTemp; -} MAX31865_Result; - -void MAX31865_Init(void){ - if(init_status) - return; - - max31865.setPins( - pin[GPIO_SSPI_CS], - pin[GPIO_SSPI_MOSI], - pin[GPIO_SSPI_MISO], - pin[GPIO_SSPI_SCLK] - ); - - if(max31865.begin(PTD_WIRES)) - init_status = 1; - else - init_status = -1; -} - - - - - -void MAX31865_GetResult(void){ - uint16_t rtd; - - rtd = max31865.readRTD(); - MAX31865_Result.Rtd = rtd; - MAX31865_Result.PtdResistance = max31865.rtd_to_resistance(rtd, MAX31865_REF_RES); - MAX31865_Result.PtdTemp = max31865.rtd_to_temperature(rtd, MAX31865_PTD_RES, MAX31865_REF_RES) + MAX31865_PTD_BIAS; -} - -void MAX31865_Show(bool Json){ - char temperature[33]; - char resistance[33]; - - dtostrfd(MAX31865_Result.PtdResistance, Settings.flag2.temperature_resolution, resistance); - dtostrfd(MAX31865_Result.PtdTemp, Settings.flag2.temperature_resolution, temperature); - - if(Json){ - ResponseAppend_P(PSTR(",\"MAX31865\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_RESISTANCE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ - temperature, resistance, MAX31865_Result.ErrorCode); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_TEMP, temperature); - } -#endif -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, MAX31865_Result.PtdTemp); - } -#endif - } else { -#ifdef USE_WEBSERVER - WSContentSend_PD(HTTP_SNS_TEMP, "MAX31865", temperature, TempUnit()); -#endif - } -} - - - - - -bool Xsns47(uint8_t function) -{ - bool result = false; - if((pin[GPIO_SSPI_MISO] < 99) && (pin[GPIO_SSPI_MOSI] < 99) && - (pin[GPIO_SSPI_SCLK] < 99) && (pin[GPIO_SSPI_CS] < 99)) { - - switch (function) { - case FUNC_INIT: - MAX31865_Init(); - break; - - case FUNC_EVERY_SECOND: - MAX31865_GetResult(); - break; - - case FUNC_JSON_APPEND: - MAX31865_Show(true); - break; - -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MAX31865_Show(false); - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_48_chirp.ino" -# 35 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_48_chirp.ino" -#ifdef USE_I2C -#ifdef USE_CHIRP -# 47 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_48_chirp.ino" -#define XSNS_48 48 -#define XI2C_33 33 - -#define CHIRP_MAX_SENSOR_COUNT 3 - -#define CHIRP_ADDR_STANDARD 0x20 - - - - - -#define D_CMND_CHIRP "CHIRP" - -const char S_JSON_CHIRP_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_CHIRP "%s\":%d}"; -const char S_JSON_CHIRP_COMMAND[] PROGMEM = "{\"" D_CMND_CHIRP "%s\"}"; -const char kCHIRP_Commands[] PROGMEM = "Select|Set|Scan|Reset|Sleep|Wake"; - -const char kChirpTypes[] PROGMEM = "CHIRP"; - - - - - -enum CHIRP_Commands { - CMND_CHIRP_SELECT, - CMND_CHIRP_SET, - CMND_CHIRP_SCAN, - CMND_CHIRP_RESET, - CMND_CHIRP_SLEEP, - CMND_CHIRP_WAKE }; - - - - - - -#define CHIRP_GET_CAPACITANCE 0x00 -#define CHIRP_SET_ADDRESS 0x01 -#define CHIRP_GET_ADDRESS 0x02 -#define CHIRP_MEASURE_LIGHT 0x03 -#define CHIRP_GET_LIGHT 0x04 -#define CHIRP_GET_TEMPERATURE 0x05 -#define CHIRP_RESET 0x06 -#define CHIRP_GET_VERSION 0x07 -#define CHIRP_SLEEP 0x08 -#define CHIRP_GET_BUSY 0x09 - - - - - -void ChirpWriteI2CRegister(uint8_t addr, uint8_t reg) { - Wire.beginTransmission(addr); - Wire.write(reg); - Wire.endTransmission(); -} - -uint16_t ChirpFinishReadI2CRegister16bit(uint8_t addr) { - Wire.requestFrom(addr,(uint8_t)2); - uint16_t t = Wire.read() << 8; - t = t | Wire.read(); - return t; -} - - - - - -uint8_t chirp_current = 0; -uint8_t chirp_found_sensors = 0; - -char chirp_name[7]; -uint8_t chirp_next_job = 0; -uint32_t chirp_timeout_count = 0; - -#pragma pack(1) -struct ChirpSensor_t{ - uint16_t moisture = 0; - uint16_t light = 0; - int16_t temperature = 0; - uint8_t version = 0; - uint8_t address:7; - uint8_t explicitSleep:1; -}; -#pragma pack() - -ChirpSensor_t chirp_sensor[CHIRP_MAX_SENSOR_COUNT]; - - - -void ChirpReset(uint8_t addr) { - ChirpWriteI2CRegister(addr, CHIRP_RESET); -} - - - -void ChirpResetAll(void) { - for (uint32_t i = 0; i < chirp_found_sensors; i++) { - if (chirp_sensor[i].version) { - ChirpReset(chirp_sensor[i].address); - } - } -} - - -void ChirpClockSet() { - Wire.setClockStretchLimit(4000); - Wire.setClock(50000); -} - - - -void ChirpSleep(uint8_t addr) { - ChirpWriteI2CRegister(addr, CHIRP_SLEEP); -} -# 185 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_48_chirp.ino" -void ChirpSelect(uint8_t sensor) { - if(sensor < chirp_found_sensors) { - chirp_current = sensor; - DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u now active."), chirp_current); - } - if (sensor == 255) { - DEBUG_SENSOR_LOG(PSTR("CHIRP: Sensor %u active at address 0x%x."), chirp_current, chirp_sensor[chirp_current].address); - } -} - - - -uint8_t ChirpReadVersion(uint8_t addr) { - return (I2cRead8(addr, CHIRP_GET_VERSION)); -} - - - -bool ChirpSet(uint8_t addr) { - if(addr < 128){ - if (I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr)){ - if(chirp_sensor[chirp_current].version>0x25 && chirp_sensor[chirp_current].version != 255){ - delay(5); - I2cWrite8(chirp_sensor[chirp_current].address, CHIRP_SET_ADDRESS, addr); - - } - DEBUG_SENSOR_LOG(PSTR("CHIRP: Wrote adress %u "), addr); - ChirpReset(chirp_sensor[chirp_current].address); - chirp_sensor[chirp_current].address = addr; - chirp_timeout_count = 10; - chirp_next_job = 0; - if(chirp_sensor[chirp_current].version == 255){ - AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: wrote new address %u, please power off device"), addr); - chirp_sensor[chirp_current].version == 0; - } - return true; - } - } - AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: address %u incorrect and not used"), addr); - return false; -} - - - -bool ChirpScan() -{ - ChirpClockSet(); - chirp_found_sensors = 0; - for (uint8_t address = 1; address <= 127; address++) { - chirp_sensor[chirp_found_sensors].version = 0; - chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address); - delay(2); - chirp_sensor[chirp_found_sensors].version = ChirpReadVersion(address); - if (chirp_sensor[chirp_found_sensors].version > 0) { - I2cSetActiveFound(address, "CHIRP"); - if (chirp_found_sensors 0); -} - - - -void ChirpDetect(void) -{ - if (chirp_next_job > 0) { return; } - - DEBUG_SENSOR_LOG(PSTR("CHIRP: scan will start ...")); - if (ChirpScan()) { - uint8_t chirp_model = 0; - GetTextIndexed(chirp_name, sizeof(chirp_name), chirp_model, kChirpTypes); - } -} - - -void ChirpServiceAllSensors(uint8_t job){ - for (uint32_t i = 0; i < chirp_found_sensors; i++) { - if (chirp_sensor[i].version && !chirp_sensor[i].explicitSleep) { - DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare for sensor at address 0x%x"), chirp_sensor[i].address); - switch(job){ - case 0: - ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_CAPACITANCE); - break; - case 1: - chirp_sensor[i].moisture = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); - break; - case 2: - ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_TEMPERATURE); - break; - case 3: - chirp_sensor[i].temperature = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); - break; - case 4: - ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_MEASURE_LIGHT); - break; - case 5: - ChirpWriteI2CRegister(chirp_sensor[i].address, CHIRP_GET_LIGHT); - break; - case 6: - chirp_sensor[i].light = ChirpFinishReadI2CRegister16bit(chirp_sensor[i].address); - break; - default: - break; - } - } - } -} - - - -void ChirpEvery100MSecond(void) -{ - - if(chirp_timeout_count == 0) { - switch(chirp_next_job) { - case 0: - DEBUG_SENSOR_LOG(PSTR("CHIRP: reset all")); - ChirpResetAll(); - chirp_timeout_count = 10; - chirp_next_job++; - break; - case 1: - - - chirp_next_job++; - break; - case 2: - DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read")); - ChirpServiceAllSensors(0); - chirp_timeout_count = 11; - chirp_next_job++; - break; - case 3: - DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read")); - ChirpServiceAllSensors(1); - chirp_next_job++; - break; - case 4: - DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare moisture read - 2nd")); - ChirpServiceAllSensors(0); - chirp_timeout_count = 11; - chirp_next_job++; - break; - case 5: - DEBUG_SENSOR_LOG(PSTR("CHIRP: finish moisture read - 2nd")); - ChirpServiceAllSensors(1); - chirp_next_job++; - break; - case 6: - DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read")); - ChirpServiceAllSensors(2); - chirp_timeout_count = 11; - chirp_next_job++; - break; - case 7: - DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read")); - ChirpServiceAllSensors(3); - chirp_next_job++; - break; - case 8: - DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare temperature read - 2nd")); - ChirpServiceAllSensors(2); - chirp_timeout_count = 11; - chirp_next_job++; - break; - case 9: - DEBUG_SENSOR_LOG(PSTR("CHIRP: finish temperature read - 2nd")); - ChirpServiceAllSensors(3); - chirp_next_job++; - break; - case 10: - DEBUG_SENSOR_LOG(PSTR("CHIRP: start light measure process")); - ChirpServiceAllSensors(4); - chirp_timeout_count = 90; - chirp_next_job++; - break; - case 11: - DEBUG_SENSOR_LOG(PSTR("CHIRP: prepare light read")); - ChirpServiceAllSensors(5); - chirp_timeout_count = 11; - chirp_next_job++; - break; - case 12: - DEBUG_SENSOR_LOG(PSTR("CHIRP: finish light read")); - ChirpServiceAllSensors(6); - chirp_next_job++; - break; - case 13: - DEBUG_SENSOR_LOG(PSTR("CHIRP: paused, waiting for TELE")); - break; - case 14: - if (Settings.tele_period > 16){ - chirp_timeout_count = (Settings.tele_period - 17) * 10; - DEBUG_SENSOR_LOG(PSTR("CHIRP: timeout 1/10 sec: %u, tele: %u"), chirp_timeout_count, Settings.tele_period); - } - else{ - AddLog_P2(LOG_LEVEL_INFO, PSTR("CHIRP: TELEPERIOD must be > 16 seconds !")); - - } - chirp_next_job = 1; - break; - } - } - else { - chirp_timeout_count--; - } -} - - - - -#ifdef USE_WEBSERVER - - -const char HTTP_SNS_DARKNESS[] PROGMEM = "{s} " D_JSON_DARKNESS "{m}%s %%{e}"; -const char HTTP_SNS_CHIRPVER[] PROGMEM = "{s} CHIRP-sensor %u at address{m}0x%x{e}" - "{s} FW-version{m}%s {e}"; ; -const char HTTP_SNS_CHIRPSLEEP[] PROGMEM = "{s} {m} is sleeping ...{e}"; -#endif - - - -void ChirpShow(bool json) -{ - for (uint32_t i = 0; i < chirp_found_sensors; i++) { - if (chirp_sensor[i].version) { - - char str_temperature[33]; - double t_temperature = ((double) chirp_sensor[i].temperature )/10.0; - dtostrfd(t_temperature, Settings.flag2.temperature_resolution, str_temperature); - char str_light[33]; - dtostrfd(chirp_sensor[i].light, 0, str_light); - char str_version[7]; - if(chirp_sensor[i].version == 0xff){ - strncpy_P(str_version, PSTR("Chirp!"), sizeof(str_version)); - } - else{ - sprintf(str_version, "%x", chirp_sensor[i].version); - } - - if (json) { - if(!chirp_sensor[i].explicitSleep) { - ResponseAppend_P(PSTR(",\"%s%u\":{\"" D_JSON_MOISTURE "\":%d"), chirp_name, i, chirp_sensor[i].moisture); - if(chirp_sensor[i].temperature!=-1){ - ResponseAppend_P(PSTR(",\"" D_JSON_TEMPERATURE "\":%s"),str_temperature); - } - ResponseAppend_P(PSTR(",\"" D_JSON_DARKNESS "\":%s}"),str_light); - } - else { - ResponseAppend_P(PSTR(",\"%s%u\":{\"sleeping\"}"),chirp_name, i); - } - #ifdef USE_DOMOTICZ - if (0 == tele_period) { - char str_moisture[33]; - dtostrfd(chirp_sensor[i].moisture, 0, str_moisture); - DomoticzTempHumSensor(str_temperature, str_moisture); - DomoticzSensor(DZ_ILLUMINANCE,chirp_sensor[i].light); - } - #endif - #ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_CHIRPVER, i, chirp_sensor[i].address, str_version); - if (chirp_sensor[i].explicitSleep){ - WSContentSend_PD(HTTP_SNS_CHIRPSLEEP); - } - else { - WSContentSend_PD(HTTP_SNS_MOISTURE, "", chirp_sensor[i].moisture); - WSContentSend_PD(HTTP_SNS_DARKNESS, str_light); - if (chirp_sensor[i].temperature!=-1) { - WSContentSend_PD(HTTP_SNS_TEMP, "", str_temperature, TempUnit()); - } - } - - #endif - } - } - } -} - - - - - -bool ChirpCmd(void) { - char command[CMDSZ]; - bool serviced = true; - uint8_t disp_len = strlen(D_CMND_CHIRP); - - if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_CHIRP), disp_len)) { - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kCHIRP_Commands); - - switch (command_code) { - case CMND_CHIRP_SELECT: - case CMND_CHIRP_SET: - if (XdrvMailbox.data_len > 0) { - if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(XdrvMailbox.payload); } - if (command_code == CMND_CHIRP_SET) { ChirpSet((uint8_t)XdrvMailbox.payload); } - Response_P(S_JSON_CHIRP_COMMAND_NVALUE, command, XdrvMailbox.payload); - } - else { - if (command_code == CMND_CHIRP_SELECT) { ChirpSelect(255); } - Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload); - } - break; - case CMND_CHIRP_SCAN: - case CMND_CHIRP_SLEEP: - case CMND_CHIRP_WAKE: - case CMND_CHIRP_RESET: - if (command_code == CMND_CHIRP_SCAN) { chirp_next_job = 0; - ChirpDetect(); } - if (command_code == CMND_CHIRP_SLEEP) { chirp_sensor[chirp_current].explicitSleep = true; - ChirpSleep(chirp_sensor[chirp_current].address); } - if (command_code == CMND_CHIRP_WAKE) { chirp_sensor[chirp_current].explicitSleep = false; - ChirpReadVersion(chirp_sensor[chirp_current].address); } - if (command_code == CMND_CHIRP_RESET) { ChirpReset(chirp_sensor[chirp_current].address); } - Response_P(S_JSON_CHIRP_COMMAND, command, XdrvMailbox.payload); - break; - default: - - serviced = false; - break; - } - } - return serviced; -} - - - - - -bool Xsns48(uint8_t function) -{ - if (!I2cEnabled(XI2C_33)) { return false; } - - bool result = false; - - switch (function) { - case FUNC_EVERY_100_MSECOND: - if(chirp_found_sensors > 0){ - ChirpEvery100MSecond(); - } - break; - case FUNC_COMMAND: - result = ChirpCmd(); - break; - case FUNC_JSON_APPEND: - ChirpShow(1); - chirp_next_job = 14; - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - ChirpShow(0); - break; -#endif - case FUNC_INIT: - ChirpDetect(); - break; - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_50_paj7620.ino" -# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_50_paj7620.ino" -#ifdef USE_I2C -#ifdef USE_PAJ7620 - - - - - - -#define XSNS_50 50 -#define XI2C_34 34 - -#define PAJ7620_ADDR 0x73 - -#define PAJ7620_BANK_SEL 0xEF - - - -#define PAJ7620_GET_GESTURE 0x43 -#define PAJ7620_PROXIMITY_AVG_Y 0x6c - -#define PAJ7620_OBJECT_CENTER_X 0xad -#define PAJ7620_OBJECT_CENTER_Y 0xaf - -#define PAJ7620_DOWN 1 -#define PAJ7620_UP 2 -#define PAJ7620_RIGHT 4 -#define PAJ7620_LEFT 8 -#define PAJ7620_NEAR 16 -#define PAJ7620_FAR 32 -#define PAJ7620_CW 64 -#define PAJ7620_CCW 128 - - - - -const uint8_t PAJ7620initRegisterArray[][2] PROGMEM = { - {0xEF,0x00}, - {0x32,0x29}, {0x33,0x01}, {0x34,0x00}, {0x35,0x01}, {0x36,0x00}, {0x37,0x07}, {0x38,0x17}, {0x39,0x06}, - {0x3A,0x12}, {0x3F,0x00}, {0x40,0x02}, {0x41,0xFF}, {0x42,0x01}, {0x46,0x2D}, {0x47,0x0F}, {0x48,0x3C}, - {0x49,0x00}, {0x4A,0x1E}, {0x4B,0x00}, {0x4C,0x20}, {0x4D,0x00}, {0x4E,0x1A}, {0x4F,0x14}, {0x50,0x00}, - {0x51,0x10}, {0x52,0x00}, {0x5C,0x02}, {0x5D,0x00}, {0x5E,0x10}, {0x5F,0x3F}, {0x60,0x27}, {0x61,0x28}, - {0x62,0x00}, {0x63,0x03}, {0x64,0xF7}, {0x65,0x03}, {0x66,0xD9}, {0x67,0x03}, {0x68,0x01}, {0x69,0xC8}, - {0x6A,0x40}, {0x6D,0x04}, {0x6E,0x00}, {0x6F,0x00}, {0x70,0x80}, {0x71,0x00}, {0x72,0x00}, {0x73,0x00}, - {0x74,0xF0}, {0x75,0x00}, {0x80,0x42}, {0x81,0x44}, {0x82,0x04}, {0x83,0x20}, {0x84,0x20}, {0x85,0x00}, - {0x86,0x10}, {0x87,0x00}, {0x88,0x05}, {0x89,0x18}, {0x8A,0x10}, {0x8B,0x01}, {0x8C,0x37}, {0x8D,0x00}, - {0x8E,0xF0}, {0x8F,0x81}, {0x90,0x06}, {0x91,0x06}, {0x92,0x1E}, {0x93,0x0D}, {0x94,0x0A}, {0x95,0x0A}, - {0x96,0x0C}, {0x97,0x05}, {0x98,0x0A}, {0x99,0x41}, {0x9A,0x14}, {0x9B,0x0A}, {0x9C,0x3F}, {0x9D,0x33}, - {0x9E,0xAE}, {0x9F,0xF9}, {0xA0,0x48}, {0xA1,0x13}, {0xA2,0x10}, {0xA3,0x08}, {0xA4,0x30}, {0xA5,0x19}, - {0xA6,0x10}, {0xA7,0x08}, {0xA8,0x24}, {0xA9,0x04}, {0xAA,0x1E}, {0xAB,0x1E}, {0xCC,0x19}, {0xCD,0x0B}, - {0xCE,0x13}, {0xCF,0x64}, {0xD0,0x21}, {0xD1,0x0F}, {0xD2,0x88}, {0xE0,0x01}, {0xE1,0x04}, {0xE2,0x41}, - {0xE3,0xD6}, {0xE4,0x00}, {0xE5,0x0C}, {0xE6,0x0A}, {0xE7,0x00}, {0xE8,0x00}, {0xE9,0x00}, {0xEE,0x07}, - {0xEF,0x01}, - {0x00,0x1E}, {0x01,0x1E}, {0x02,0x0F}, {0x03,0x10}, {0x04,0x02}, {0x05,0x00}, {0x06,0xB0}, {0x07,0x04}, - {0x08,0x0D}, {0x09,0x0E}, {0x0A,0x9C}, {0x0B,0x04}, {0x0C,0x05}, {0x0D,0x0F}, {0x0E,0x02}, {0x0F,0x12}, - {0x10,0x02}, {0x11,0x02}, {0x12,0x00}, {0x13,0x01}, {0x14,0x05}, {0x15,0x07}, {0x16,0x05}, {0x17,0x07}, - {0x18,0x01}, {0x19,0x04}, {0x1A,0x05}, {0x1B,0x0C}, {0x1C,0x2A}, {0x1D,0x01}, {0x1E,0x00}, {0x21,0x00}, - {0x22,0x00}, {0x23,0x00}, {0x25,0x01}, {0x26,0x00}, {0x27,0x39}, {0x28,0x7F}, {0x29,0x08}, {0x30,0x03}, - {0x31,0x00}, {0x32,0x1A}, {0x33,0x1A}, {0x34,0x07}, {0x35,0x07}, {0x36,0x01}, {0x37,0xFF}, {0x38,0x36}, - {0x39,0x07}, {0x3A,0x00}, {0x3E,0xFF}, {0x3F,0x00}, {0x40,0x77}, {0x41,0x40}, {0x42,0x00}, {0x43,0x30}, - {0x44,0xA0}, {0x45,0x5C}, {0x46,0x00}, {0x47,0x00}, {0x48,0x58}, {0x4A,0x1E}, {0x4B,0x1E}, {0x4C,0x00}, - {0x4D,0x00}, {0x4E,0xA0}, {0x4F,0x80}, {0x50,0x00}, {0x51,0x00}, {0x52,0x00}, {0x53,0x00}, {0x54,0x00}, - {0x57,0x80}, {0x59,0x10}, {0x5A,0x08}, {0x5B,0x94}, {0x5C,0xE8}, {0x5D,0x08}, {0x5E,0x3D}, {0x5F,0x99}, - {0x60,0x45}, {0x61,0x40}, {0x63,0x2D}, {0x64,0x02}, {0x65,0x96}, {0x66,0x00}, {0x67,0x97}, {0x68,0x01}, - {0x69,0xCD}, {0x6A,0x01}, {0x6B,0xB0}, {0x6C,0x04}, {0x6D,0x2C}, {0x6E,0x01}, {0x6F,0x32}, {0x71,0x00}, - {0x72,0x01}, {0x73,0x35}, {0x74,0x00}, {0x75,0x33}, {0x76,0x31}, {0x77,0x01}, {0x7C,0x84}, {0x7D,0x03}, - {0x7E,0x01}, - {0xEF,0x00} -}; - - - - - -const char kPaj7620Directions[] PROGMEM = "Down|Up|Right|Left|Near|Far|CW|CCW"; - -const uint8_t PAJ7620_PIN[]= {1,2,3,4}; - - - - - -char PAJ7620_name[] = "PAJ7620"; - -uint32_t PAJ7620_timeout_counter = 10; -uint32_t PAJ7620_next_job = 0; -uint32_t PAJ7620_mode = 1; - -struct { - uint8_t current; - uint8_t last; - uint8_t same; - uint8_t unfinished; -} PAJ7620_gesture; - -bool PAJ7620_finished_gesture = false; -char PAJ7620_currentGestureName[6]; - -struct{ - uint8_t x; - uint8_t y; - uint8_t last_x; - uint8_t last_y; - uint8_t proximity; - uint8_t last_proximity; - uint8_t corner; - struct { - uint8_t step:3; - uint8_t countdown:3; - uint8_t valid:1; - } PIN; -} PAJ7620_state; - - - - - -void PAJ7620SelectBank(uint8_t bank) -{ - I2cWrite(PAJ7620_ADDR, PAJ7620_BANK_SEL, bank &1, 1); -} - - - -void PAJ7620DecodeGesture(void) -{ - uint32_t index = 0; - switch (PAJ7620_gesture.current) { - case PAJ7620_LEFT: - index++; - case PAJ7620_RIGHT: - index++; - case PAJ7620_UP: - index++; - case PAJ7620_DOWN: - if (PAJ7620_gesture.unfinished) { - PAJ7620_finished_gesture = true; - break; - } - PAJ7620_gesture.unfinished = PAJ7620_gesture.current; - PAJ7620_timeout_counter = 5; - break; - case PAJ7620_NEAR: - index = 4; - PAJ7620_finished_gesture = true; - PAJ7620_timeout_counter = 25; - break; - case PAJ7620_FAR: - index = 5; - PAJ7620_finished_gesture = true; - PAJ7620_timeout_counter = 25; - break; - case PAJ7620_CW: - index = 6; - PAJ7620_finished_gesture = true; - break; - case PAJ7620_CCW: - index = 7; - PAJ7620_finished_gesture = true; - break; - default: - index = 8; - if (PAJ7620_gesture.unfinished) { - PAJ7620_finished_gesture = true; - } - break; - } - if (index < 8) { - GetTextIndexed(PAJ7620_currentGestureName, sizeof(PAJ7620_currentGestureName), index, kPaj7620Directions); - } - - if (PAJ7620_finished_gesture) { - if (PAJ7620_gesture.unfinished) { - if ((PAJ7620_gesture.current != PAJ7620_NEAR) && (PAJ7620_gesture.current != PAJ7620_FAR)) { - PAJ7620_gesture.current = PAJ7620_gesture.unfinished; - } - } - if (PAJ7620_gesture.current == PAJ7620_gesture.last) { - PAJ7620_gesture.same++; - } else { - PAJ7620_gesture.same = 1; - } - PAJ7620_gesture.last = PAJ7620_gesture.current; - PAJ7620_finished_gesture = false; - PAJ7620_gesture.unfinished = 0; - PAJ7620_timeout_counter += 3; - MqttPublishSensor(); - } -} - - - -void PAJ7620ReadGesture(void) -{ - switch (PAJ7620_mode) { - case 1: - PAJ7620_gesture.current = I2cRead8(PAJ7620_ADDR,PAJ7620_GET_GESTURE); - if ((PAJ7620_gesture.current > 0) || PAJ7620_gesture.unfinished) { - DEBUG_SENSOR_LOG(PSTR("PAJ: gesture: %u"), PAJ7620_gesture.current); - PAJ7620DecodeGesture(); - } - break; - case 2: - PAJ7620_state.proximity = I2cRead8(PAJ7620_ADDR, PAJ7620_PROXIMITY_AVG_Y); - if ((PAJ7620_state.proximity > 0) || (PAJ7620_state.last_proximity > 0)) { - if (PAJ7620_state.proximity != PAJ7620_state.last_proximity) { - PAJ7620_state.last_proximity = PAJ7620_state.proximity; - DEBUG_SENSOR_LOG(PSTR("PAJ: Proximity: %u"), PAJ7620_state.proximity); - MqttPublishSensor(); - } - } - break; - case 3: - case 4: - case 5: - PAJ7620_state.x = I2cRead8(PAJ7620_ADDR, PAJ7620_OBJECT_CENTER_X); - PAJ7620_state.y = I2cRead8(PAJ7620_ADDR, PAJ7620_OBJECT_CENTER_Y); - if ((PAJ7620_state.y > 0) && (PAJ7620_state.x > 0)) { - if ((PAJ7620_state.y != PAJ7620_state.last_y) || (PAJ7620_state.x != PAJ7620_state.last_x)) { - PAJ7620_state.last_y = PAJ7620_state.y; - PAJ7620_state.last_x = PAJ7620_state.x; - DEBUG_SENSOR_LOG(PSTR("PAJ: x: %u y: %u"), PAJ7620_state.x, PAJ7620_state.y); - - PAJ7620_state.corner = 0; - - - - switch (PAJ7620_state.y) { - case 0: case 1: case 2: case 3: case 4: case 5: - PAJ7620_state.corner = 3; - break; - case 9: case 10: case 11: case 12: case 13: case 14: - PAJ7620_state.corner = 1; - break; - } - if (PAJ7620_state.corner != 0) { - switch (PAJ7620_state.x) { - case 0: case 1: case 2: case 3: case 4: case 5: - break; - case 9: case 10: case 11: case 12: case 13: case 14: - PAJ7620_state.corner++; - break; - default: - PAJ7620_state.corner = 0; - break; - } - } - DEBUG_SENSOR_LOG(PSTR("PAJ: corner: %u"), PAJ7620_state.corner); - - if (PAJ7620_state.PIN.countdown == 0) { - PAJ7620_state.PIN.step = 0; - PAJ7620_state.PIN.valid = 0; - } - if (!PAJ7620_state.PIN.step) { - if (PAJ7620_state.corner == PAJ7620_PIN[PAJ7620_state.PIN.step]) { - PAJ7620_state.PIN.step = 1; - PAJ7620_state.PIN.countdown = 7; - } - } else { - if (PAJ7620_state.corner == PAJ7620_PIN[PAJ7620_state.PIN.step]) { - PAJ7620_state.PIN.step += 1; - PAJ7620_state.PIN.countdown = 7; - } else { - PAJ7620_state.PIN.countdown -= 1; - } - } - if (PAJ7620_state.PIN.step == 4) { - PAJ7620_state.PIN.valid = 1; - DEBUG_SENSOR_LOG(PSTR("PAJ: PIN valid!!")); - PAJ7620_state.PIN.countdown = 0; - } - MqttPublishSensor(); - } - } - break; - } -} - - - -void PAJ7620Detect(void) -{ - if (I2cActive(PAJ7620_ADDR)) { return; } - - PAJ7620SelectBank(0); - PAJ7620SelectBank(0); - uint16_t PAJ7620_id = I2cRead16LE(PAJ7620_ADDR,0); - uint8_t PAJ7620_ver = I2cRead8(PAJ7620_ADDR,2); - if (0x7620 == PAJ7620_id) { - I2cSetActiveFound(PAJ7620_ADDR, PAJ7620_name); - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PAJ: ID: 0x%x and VER: %u"), PAJ7620_id, PAJ7620_ver); - PAJ7620_next_job = 1; - } - else { - DEBUG_SENSOR_LOG(PSTR("PAJ: sensor not found, false ID 0x%x"), PAJ7620_id); - } -} - - - -void PAJ7620Init(void) -{ - DEBUG_SENSOR_LOG(PSTR("PAJ: init sensor start %u"),millis()); - union{ - uint32_t raw; - uint8_t reg_val[4]; - } buf; - - for (uint32_t i = 0; i < (sizeof(PAJ7620initRegisterArray) / 2); i += 2) - { - buf.raw = pgm_read_dword(PAJ7620initRegisterArray + i); - DEBUG_SENSOR_LOG("PAJ: %x %x %x %x",buf.reg_val[0],buf.reg_val[1],buf.reg_val[2],buf.reg_val[3]); - I2cWrite(PAJ7620_ADDR, buf.reg_val[0], buf.reg_val[1], 1); - I2cWrite(PAJ7620_ADDR, buf.reg_val[2], buf.reg_val[3], 1); - } - DEBUG_SENSOR_LOG(PSTR("PAJ: init sensor done %u"),millis()); - PAJ7620_next_job = 2; -} - - - -void PAJ7620Loop(void) -{ - if (0 == PAJ7620_timeout_counter) { - switch (PAJ7620_next_job) { - case 1: - PAJ7620Init(); - break; - case 2: - if (PAJ7620_mode != 0) { - PAJ7620ReadGesture(); - } - break; - } - } else { - PAJ7620_timeout_counter--; - } -} - - - -void PAJ7620Show(bool json) -{ - if (json) { - if (PAJ7620_currentGestureName[0] != '\0' ) { - ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%u}"), PAJ7620_name, PAJ7620_currentGestureName, PAJ7620_gesture.same); - PAJ7620_currentGestureName[0] = '\0'; - return; - } - switch (PAJ7620_mode) { - case 2: - ResponseAppend_P(PSTR(",\"%s\":{\"Proximity\":%u}"), PAJ7620_name, PAJ7620_state.proximity); - break; - case 3: - if (PAJ7620_state.corner > 0) { - ResponseAppend_P(PSTR(",\"%s\":{\"Corner\":%u}"), PAJ7620_name, PAJ7620_state.corner); - } - break; - case 4: - if (PAJ7620_state.PIN.valid) { - ResponseAppend_P(PSTR(",\"%s\":{\"PIN\":%u}"), PAJ7620_name, 1); - PAJ7620_state.PIN.valid = 0; - } - break; - case 5: - ResponseAppend_P(PSTR(",\"%s\":{\"x\":%u,\"y\":%u}"), PAJ7620_name, PAJ7620_state.x, PAJ7620_state.y); - break; - } - } -} -# 411 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_50_paj7620.ino" -bool PAJ7620CommandSensor(void) -{ - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 5)) { - PAJ7620_mode = XdrvMailbox.payload; - } - Response_P(S_JSON_SENSOR_INDEX_NVALUE, XSNS_50, PAJ7620_mode); - - return true; -} - - - - - -bool Xsns50(uint8_t function) -{ - if (!I2cEnabled(XI2C_34)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - PAJ7620Detect(); - } - else if (PAJ7620_next_job) { - switch (function) { - case FUNC_COMMAND_SENSOR: - if (XSNS_50 == XdrvMailbox.index){ - result = PAJ7620CommandSensor(); - } - break; - case FUNC_EVERY_100_MSECOND: - PAJ7620Loop(); - break; - case FUNC_JSON_APPEND: - PAJ7620Show(1); - break; - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_51_rdm6300.ino" -# 21 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_51_rdm6300.ino" -#ifdef USE_RDM6300 - -#define XSNS_51 51 - -#define RDM6300_BAUDRATE 9600 - -#include - -#define RDM_TIMEOUT 100 -char rdm_uid_str[10]; - - -#define RDM6300_BLOCK 2*10 - -uint8_t rdm_blcnt; -TasmotaSerial *RDM6300_Serial = nullptr; - -void RDM6300_Init() { - if (pin[GPIO_RDM6300_RX] < 99) { - RDM6300_Serial = new TasmotaSerial(pin[GPIO_RDM6300_RX],-1,1); - if (RDM6300_Serial->begin(RDM6300_BAUDRATE)) { - if (RDM6300_Serial->hardwareSerial()) { - ClaimSerial(); - } - } - } - rdm_blcnt=0; -} - - -void RDM6300_ScanForTag() { - char rdm_buffer[14]; - uint8_t rdm_index; - uint8_t rdm_array[6]; - - if (!RDM6300_Serial) return; - - if (rdm_blcnt>0) { - rdm_blcnt--; - while (RDM6300_Serial->available()) RDM6300_Serial->read(); - return; - } - - if (RDM6300_Serial->available()) { - - char c=RDM6300_Serial->read(); - if (c!=2) return; - - - rdm_index=0; - uint32_t cmillis=millis(); - while (1) { - if (RDM6300_Serial->available()) { - char c=RDM6300_Serial->read(); - if (c==3) { - - break; - } - rdm_buffer[rdm_index++]=c; - if (rdm_index>13) { - - return; - } - } - if ((millis()-cmillis)>RDM_TIMEOUT) { - - return; - } - } - - - rdm_blcnt=RDM6300_BLOCK; - - - rm6300_hstring_to_array(rdm_array,sizeof(rdm_array),rdm_buffer); - uint8_t accu=0; - for (uint8_t count=0;count<5;count++) { - accu^=rdm_array[count]; - } - if (accu!=rdm_array[5]) { - - return; - } - - - memcpy(rdm_uid_str,&rdm_buffer[2],8); - rdm_uid_str[9]=0; - - ResponseTime_P(PSTR(",\"RDM6300\":{\"UID\":\"%s\"}}"), rdm_uid_str); - MqttPublishTeleSensor(); - - - - - - } - - -} - -uint8_t rm6300_hexnibble(char chr) { - uint8_t rVal = 0; - if (isdigit(chr)) { - rVal = chr - '0'; - } else { - if (chr >= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; - if (chr >= 'a' && chr <= 'f') rVal = chr + 10 - 'a'; - } - return rVal; -} - - -void rm6300_hstring_to_array(uint8_t array[], uint8_t len, char buffer[]) -{ - char *cp=buffer; - for (uint8_t i = 0; i < len; i++) { - uint8_t val = rm6300_hexnibble(*cp++) << 4; - array[i]= val | rm6300_hexnibble(*cp++); - } -} - -#ifdef USE_WEBSERVER -const char HTTP_RDM6300[] PROGMEM = - "{s}RDM6300 " "UID" "{m}%s" "{e}"; - -void RDM6300_Show(void) { - if (!RDM6300_Serial) return; - if (!rdm_uid_str[0]) strcpy(rdm_uid_str,"????"); - WSContentSend_PD(HTTP_RDM6300,rdm_uid_str); -} -#endif - - - - - -bool Xsns51(byte function) -{ - bool result = false; - - switch (function) { - case FUNC_INIT: - RDM6300_Init(); - break; - case FUNC_EVERY_100_MSECOND: - RDM6300_ScanForTag(); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - RDM6300_Show(); - break; -#endif - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_52_ibeacon.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_52_ibeacon.ino" -#ifdef USE_IBEACON - - - -#define XSNS_52 52 - -#include - -#define HM17_BAUDRATE 9600 - -#define IBEACON_DEBUG - - -#define HM17_V110 - - - -#define IB_TIMEOUT_INTERVAL 30 - -#define IB_UPDATE_TIME_INTERVAL 10 - -TasmotaSerial *IBEACON_Serial = nullptr; - - -uint8_t hm17_found,hm17_cmd,hm17_flag; - -#ifdef IBEACON_DEBUG -uint8_t hm17_debug=0; -#endif - - - -#define HM17_BSIZ 128 -char hm17_sbuffer[HM17_BSIZ]; -uint8_t hm17_sindex,hm17_result,hm17_scanning,hm17_connecting; -uint32_t hm17_lastms; -char ib_mac[14]; - - -#if 1 -uint8_t ib_upd_interval,ib_tout_interval; -#define IB_UPDATE_TIME ib_upd_interval -#define IB_TIMEOUT_TIME ib_tout_interval -#else -#undef IB_UPDATE_TIME -#undef IB_TIMEOUT_TIME -#define IB_UPDATE_TIME Settings.ib_upd_interval -#define IB_TIMEOUT_TIME Settings.ib_tout_interval -#endif - -enum {HM17_TEST,HM17_ROLE,HM17_IMME,HM17_DISI,HM17_IBEA,HM17_SCAN,HM17_DISC,HM17_RESET,HM17_RENEW,HM17_CON}; -#define HM17_SUCESS 99 - -struct IBEACON { - char FACID[8]; - char UID[32]; - char MAJOR[4]; - char MINOR[4]; - char PWR[2]; - char MAC[12]; - char RSSI[4]; -}; - -#define MAX_IBEACONS 16 - -struct IBEACON_UID { - char MAC[12]; - char RSSI[4]; - uint8_t FLAGS; - uint8_t TIME; -} ibeacons[MAX_IBEACONS]; - - -void IBEACON_Init() { - - hm17_found=0; - - - if ((pin[GPIO_IBEACON_RX] < 99) && (pin[GPIO_IBEACON_TX] < 99)) { - IBEACON_Serial = new TasmotaSerial(pin[GPIO_IBEACON_RX], pin[GPIO_IBEACON_TX],1); - if (IBEACON_Serial->begin(HM17_BAUDRATE)) { - if (IBEACON_Serial->hardwareSerial()) { - ClaimSerial(); - } - hm17_sendcmd(HM17_TEST); - hm17_lastms=millis(); - - IB_UPDATE_TIME=IB_UPDATE_TIME_INTERVAL; - IB_TIMEOUT_TIME=IB_TIMEOUT_INTERVAL; - } - } -} - -void hm17_every_second(void) { - if (!IBEACON_Serial) return; - - if (hm17_found) { - if (IB_UPDATE_TIME && (uptime%IB_UPDATE_TIME==0)) { - if (hm17_cmd!=99) { - if (hm17_flag&2) { - ib_sendbeep(); - } else { - if (!hm17_connecting) { - hm17_sendcmd(HM17_DISI); - } - } - } - } - for (uint32_t cnt=0;cntIB_TIMEOUT_TIME) { - ibeacons[cnt].FLAGS=0; - ibeacon_mqtt(ibeacons[cnt].MAC,"0000"); - } - } - } - } else { - if (uptime%20==0) { - hm17_sendcmd(HM17_TEST); - } - } -} - -void hm17_sbclr(void) { - memset(hm17_sbuffer,0,HM17_BSIZ); - hm17_sindex=0; - IBEACON_Serial->flush(); -} - -void hm17_sendcmd(uint8_t cmd) { - hm17_sbclr(); - hm17_cmd=cmd; -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("hm17cmd %d"),cmd); -#endif - switch (cmd) { - case HM17_TEST: - IBEACON_Serial->write("AT"); - break; - case HM17_ROLE: - IBEACON_Serial->write("AT+ROLE1"); - break; - case HM17_IMME: - IBEACON_Serial->write("AT+IMME1"); - break; - case HM17_DISI: - IBEACON_Serial->write("AT+DISI?"); - hm17_scanning=1; - break; - case HM17_IBEA: - IBEACON_Serial->write("AT+IBEA1"); - break; - case HM17_RESET: - IBEACON_Serial->write("AT+RESET"); - break; - case HM17_RENEW: - IBEACON_Serial->write("AT+RENEW"); - break; - case HM17_SCAN: - IBEACON_Serial->write("AT+SCAN5"); - break; - case HM17_DISC: - IBEACON_Serial->write("AT+DISC?"); - hm17_scanning=1; - break; - case HM17_CON: - IBEACON_Serial->write((const uint8_t*)"AT+CON",6); - IBEACON_Serial->write((const uint8_t*)ib_mac,12); - hm17_connecting=1; - break; - } -} - -uint32_t ibeacon_add(struct IBEACON *ib) { - - if (!strncmp(ib->MAC,"FFFF",4) || strncmp(ib->FACID,"00000000",8)) { - for (uint32_t cnt=0;cntMAC,12)) { - - memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); - ibeacons[cnt].TIME=0; - return 1; - } - } - } - for (uint32_t cnt=0;cntMAC,12); - memcpy(ibeacons[cnt].RSSI,ib->RSSI,4); - ibeacons[cnt].FLAGS=1; - ibeacons[cnt].TIME=0; - return 1; - } - } - } - return 0; -} - -void hm17_decode(void) { - struct IBEACON ib; - switch (hm17_cmd) { - case HM17_TEST: - if (!strncmp(hm17_sbuffer,"OK",2)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("AT OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - hm17_found=1; - } - break; - case HM17_ROLE: - if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("ROLE OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - } - break; - case HM17_IMME: - if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("IMME OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - } - break; - case HM17_IBEA: - if (!strncmp(hm17_sbuffer,"OK+Set:1",8)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("IBEA OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - } - break; - case HM17_SCAN: - if (!strncmp(hm17_sbuffer,"OK+Set:5",8)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("SCAN OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - } - break; - case HM17_RESET: - if (!strncmp(hm17_sbuffer,"OK+RESET",8)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("RESET OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - } - break; - case HM17_RENEW: - if (!strncmp(hm17_sbuffer,"OK+RENEW",8)) { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("RENEW OK")); -#endif - hm17_sbclr(); - hm17_result=HM17_SUCESS; - } - break; - case HM17_CON: - if (!strncmp(hm17_sbuffer,"OK+CONNA",8)) { - hm17_sbclr(); -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNA OK")); -#endif - hm17_connecting=2; - break; - } - if (!strncmp(hm17_sbuffer,"OK+CONNE",8)) { - hm17_sbclr(); -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNE ERROR")); -#endif - break; - } - if (!strncmp(hm17_sbuffer,"OK+CONNF",8)) { - hm17_sbclr(); -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONNF ERROR")); -#endif - break; - } - if (hm17_connecting==2 && !strncmp(hm17_sbuffer,"OK+CONN",7)) { - hm17_sbclr(); -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("CONN OK")); -#endif - hm17_connecting=3; - hm17_sendcmd(HM17_TEST); - hm17_connecting=0; - break; - } - break; - - case HM17_DISI: - case HM17_DISC: - if (!strncmp(hm17_sbuffer,"OK+DISCS",8)) { - hm17_sbclr(); - hm17_result=1; -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISCS OK")); -#endif - break; - } - if (!strncmp(hm17_sbuffer,"OK+DISIS",8)) { - hm17_sbclr(); - hm17_result=1; -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISIS OK")); -#endif - break; - } - if (!strncmp(hm17_sbuffer,"OK+DISCE",8)) { - hm17_sbclr(); - hm17_result=HM17_SUCESS; -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR("DISCE OK")); -#endif - hm17_scanning=0; - break; - } - if (!strncmp(hm17_sbuffer,"OK+NAME:",8)) { - if (hm17_sbuffer[hm17_sindex-1]=='\n') { - hm17_result=HM17_SUCESS; -#ifdef IBEACON_DEBUG - if (hm17_debug) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("NAME OK")); - AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); - } -#endif - hm17_sbclr(); - } - break; - } - if (!strncmp(hm17_sbuffer,"OK+DIS0:",8)) { - if (hm17_cmd==HM17_DISI) { -#ifdef HM17_V110 - goto hm17_v110; -#endif - } else { - if (hm17_sindex==20) { - hm17_result=HM17_SUCESS; -#ifdef IBEACON_DEBUG - if (hm17_debug) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("DIS0 OK")); - AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); - } -#endif - hm17_sbclr(); - } - } - break; - } - if (!strncmp(hm17_sbuffer,"OK+DISC:",8)) { -hm17_v110: - if (hm17_cmd==HM17_DISI) { - if (hm17_sindex==78) { -#ifdef IBEACON_DEBUG - if (hm17_debug) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("DISC: OK")); - - AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); - } -#endif - memcpy(ib.FACID,&hm17_sbuffer[8],8); - memcpy(ib.UID,&hm17_sbuffer[8+8+1],32); - memcpy(ib.MAJOR,&hm17_sbuffer[8+8+1+32+1],4); - memcpy(ib.MINOR,&hm17_sbuffer[8+8+1+32+1+4],4); - memcpy(ib.PWR,&hm17_sbuffer[8+8+1+32+1+4+4],2); - memcpy(ib.MAC,&hm17_sbuffer[8+8+1+32+1+4+4+2+1],12); - memcpy(ib.RSSI,&hm17_sbuffer[8+8+1+32+1+4+4+2+1+12+1],4); - - if (ibeacon_add(&ib)) { - ibeacon_mqtt(ib.MAC,ib.RSSI); - } - hm17_sbclr(); - hm17_result=1; - } - } else { -#ifdef IBEACON_DEBUG - if (hm17_debug) AddLog_P2(LOG_LEVEL_INFO, PSTR(">>%s"),&hm17_sbuffer[8]); -#endif - } - break; - } - } -} - -void IBEACON_loop() { - - if (!IBEACON_Serial) return; - -uint32_t difftime=millis()-hm17_lastms; - - while (IBEACON_Serial->available()) { - hm17_lastms=millis(); - - if (hm17_sindexread(); - hm17_sindex++; - hm17_decode(); - } else { - hm17_sindex=0; - break; - } - } - - if (hm17_cmd==99) { - if (hm17_sindex>=HM17_BSIZ-2 || (hm17_sindex && (difftime>100))) { - AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),hm17_sbuffer); - hm17_sbclr(); - } - } - -} - -#ifdef USE_WEBSERVER -const char HTTP_IBEACON[] PROGMEM = - "{s}IBEACON-UID : %s" " - RSSI : %s" "{m}{e}"; - -void IBEACON_Show(void) { -char mac[14]; -char rssi[6]; - - for (uint32_t cnt=0;cnt 0) { - char *cp=XdrvMailbox.data; - if (*cp>='0' && *cp<='8') { - hm17_sendcmd(*cp&7); - Response_P(S_JSON_IBEACON, XSNS_52,"hm17cmd",*cp&7); - } else if (*cp=='s') { - cp++; - len--; - while (*cp==' ') { - len--; - cp++; - } - IBEACON_Serial->write((uint8_t*)cp,len); - hm17_cmd=99; - Response_P(S_JSON_IBEACON1, XSNS_52,"hm17cmd",cp); - } else if (*cp=='u') { - cp++; - if (*cp) IB_UPDATE_TIME=atoi(cp); - Response_P(S_JSON_IBEACON, XSNS_52,"uintv",IB_UPDATE_TIME); - } else if (*cp=='t') { - cp++; - if (*cp) IB_TIMEOUT_TIME=atoi(cp); - Response_P(S_JSON_IBEACON, XSNS_52,"lintv",IB_TIMEOUT_TIME); - } else if (*cp=='c') { - for (uint32_t cnt=0;cnt - - -#define SPECIAL_SS - - - - - -#if MY_LANGUAGE==de-DE - -#define D_TPWRIN "Verbrauch" -#define D_TPWROUT "Einspeisung" -#define D_TPWRCURR "Aktueller Verbrauch" -#define D_TPWRCURR1 "Verbrauch P1" -#define D_TPWRCURR2 "Verbrauch P2" -#define D_TPWRCURR3 "Verbrauch P3" -#define D_Strom_L1 "Strom L1" -#define D_Strom_L2 "Strom L2" -#define D_Strom_L3 "Strom L3" -#define D_Spannung_L1 "Spannung L1" -#define D_Spannung_L2 "Spannung L2" -#define D_Spannung_L3 "Spannung L3" -#define D_METERNR "Zähler Nr" -#define D_METERSID "Service ID" -#define D_GasIN "Zählerstand" -#define D_H2oIN "Zählerstand" -#define D_StL1L2L3 "Ströme L1+L2+L3" -#define D_SpL1L2L3 "Spannung L1+L2+L3/3" - -#else - -#undef D_TPWRIN -#undef D_TPWROUT -#undef D_TPWRCURR -#undef D_TPWRCURR1 -#undef D_TPWRCURR2 -#undef D_TPWRCURR3 -#undef D_Strom_L1 -#undef D_Strom_L2 -#undef D_Strom_L3 -#undef D_Spannung_L1 -#undef D_Spannung_L2 -#undef D_Spannung_L3 -#undef D_METERNR -#undef D_METERSID -#undef D_GasIN -#undef D_H2oIN -#undef D_StL1L2L3 -#undef D_SpL1L2L3 - -#define D_TPWRIN "Total-In" -#define D_TPWROUT "Total-Out" -#define D_TPWRCURR "Current-In/Out" -#define D_TPWRCURR1 "Current-In p1" -#define D_TPWRCURR2 "Current-In p2" -#define D_TPWRCURR3 "Current-In p3" -#define D_Strom_L1 "Current L1" -#define D_Strom_L2 "Current L2" -#define D_Strom_L3 "Current L3" -#define D_Spannung_L1 "Voltage L1" -#define D_Spannung_L2 "Voltage L2" -#define D_Spannung_L3 "Voltage L3" -#define D_METERNR "Meter_number" -#define D_METERSID "Service ID" -#define D_GasIN "Counter" -#define D_H2oIN "Counter" -#define D_StL1L2L3 "Current L1+L2+L3" -#define D_SpL1L2L3 "Voltage L1+L2+L3/3" - -#endif - - - -#define DJ_TPWRIN "Total_in" -#define DJ_TPWROUT "Total_out" -#define DJ_TPWRCURR "Power_curr" -#define DJ_TPWRCURR1 "Power_p1" -#define DJ_TPWRCURR2 "Power_p2" -#define DJ_TPWRCURR3 "Power_p3" -#define DJ_CURR1 "Curr_p1" -#define DJ_CURR2 "Curr_p2" -#define DJ_CURR3 "Curr_p3" -#define DJ_VOLT1 "Volt_p1" -#define DJ_VOLT2 "Volt_p2" -#define DJ_VOLT3 "Volt_p3" -#define DJ_METERNR "Meter_number" -#define DJ_METERSID "Meter_id" -#define DJ_CSUM "Curr_summ" -#define DJ_VAVG "Volt_avg" -#define DJ_COUNTER "Count" - -struct METER_DESC { - uint8_t srcpin; - uint8_t type; - uint16_t flag; - int32_t params; - char prefix[8]; - int8_t trxpin; - uint8_t tsecs; - char *txmem; - uint8_t index; - uint8_t max_index; -}; - - - - - - -#define EHZ161_0 1 -#define EHZ161_1 2 -#define EHZ363 3 -#define EHZH 4 -#define EDL300 5 -#define Q3B 6 -#define COMBO3 7 -#define COMBO2 8 -#define COMBO3a 9 -#define Q3B_V1 10 -#define EHZ363_2 11 -#define COMBO3b 12 -#define WGS_COMBO 13 -#define EBZD_G 14 - - -#define METER EHZ161_1 - - -#if METER==EHZ161_0 -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; -const uint8_t meter[]= -"1,1-0:1.8.0*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" -"1,1-0:2.8.0*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" -"1,1-0:21.7.0*255(@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",0|" -"1,1-0:41.7.0*255(@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",0|" -"1,1-0:61.7.0*255(@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",0|" -"1,=m 3+4+5 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; - -#endif - - - -#if METER==EHZ161_1 -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; -const uint8_t meter[]= -"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" -"1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" -"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; -#endif - - - -#if METER==EHZ363 -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; - -const uint8_t meter[]= - -"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" - -"1,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" - -"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" - -"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; -#endif - - - -#if METER==EHZH -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; - - -const uint8_t meter[]= - -"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" - -"1,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" - -"1,770701000f0700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; -#endif - - - -#if METER==EDL300 -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; - - -const uint8_t meter[]= - -"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" - -"1,77070100020801ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" - -"1,770701000f0700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; -#endif - -#if METER==EBZD_G -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"strom",-1,1,0}}; -const uint8_t meter[]= - -"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" - -"1,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" - -"1,77070100010801ff@1000," D_TPWRCURR1 ",KWh," DJ_TPWRCURR1 ",4|" - -"1,77070100010802ff@1000," D_TPWRCURR2 ",KWh," DJ_TPWRCURR2 ",4|" - -"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" - -"1,77070100600100ff@#," D_METERNR ",," DJ_METERNR ",0"; -#endif - - - - -#if METER==Q3B -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; -const uint8_t meter[]= - -"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" - -"1,77070100020801ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" - -"1,77070100010700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0"; -#endif - -#if METER==COMBO3 - -#undef METERS_USED -#define METERS_USED 3 - -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, - [1]={14,'s',0,SML_BAUDRATE,"SML",-1,1,0}, - [2]={4,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}}; - - -const uint8_t meter[]= -"1,1-0:1.8.0*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" -"1,1-0:2.8.0*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" -"1,1-0:21.7.0*255(@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",0|" -"1,1-0:41.7.0*255(@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",0|" -"1,1-0:61.7.0*255(@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",0|" -"1,=m 3+4+5 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" -"2,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" -"2,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" -"2,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"3,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" -"3,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" -"3,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"3,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; - -#endif - -#if METER==COMBO2 - -#undef METERS_USED -#define METERS_USED 2 - -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, - [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}}; - - -const uint8_t meter[]= -"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" -"1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" -"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" - -"2,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" -"2,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" -"2,=d 6 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"2,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; - -#endif - -#if METER==COMBO3a -#undef METERS_USED -#define METERS_USED 3 - -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS1",-1,1,0}, - [1]={14,'o',0,SML_BAUDRATE,"OBIS2",-1,1,0}, - [2]={1,'o',0,SML_BAUDRATE,"OBIS3",-1,1,0}}; - - -const uint8_t meter[]= -"1,=h --- Zähler Nr 1 ---|" -"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" -"1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" -"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" -"2,=h --- Zähler Nr 2 ---|" -"2,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" -"2,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" -"2,=d 6 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"2,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" -"3,=h --- Zähler Nr 3 ---|" -"3,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" -"3,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" -"3,=d 10 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"3,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; - -#endif - - - -#if METER==Q3B_V1 -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ -[0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}}; -const uint8_t meter[]= -"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" -"1,=d 1 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0"; -#endif - - - -#if METER==EHZ363_2 -#undef METERS_USED -#define METERS_USED 1 -struct METER_DESC const meter_desc[METERS_USED]={ -[0]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; - -const uint8_t meter[]= - -"1,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" - -"1,77070100020800ff@1000," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" - -"1,77070100010801ff@1000," D_TPWRCURR1 ",KWh," DJ_TPWRCURR1 ",4|" - -"1,77070100010802ff@1000," D_TPWRCURR2 ",KWh," DJ_TPWRCURR2 ",4|" - -"1,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" - -"1,77070100000009ff@#," D_METERNR ",," DJ_METERNR ",0"; -#endif - - -#if METER==COMBO3b -#undef METERS_USED -#define METERS_USED 3 -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={3,'o',0,SML_BAUDRATE,"OBIS",-1,1,0}, - [1]={14,'c',0,50,"Gas"}, - [2]={1,'c',0,10,"Wasser"}}; - - -const uint8_t meter[]= -"1,1-0:1.8.1*255(@1," D_TPWRIN ",KWh," DJ_TPWRIN ",4|" -"1,1-0:2.8.1*255(@1," D_TPWROUT ",KWh," DJ_TPWROUT ",4|" -"1,=d 2 10 @1," D_TPWRCURR ",W," DJ_TPWRCURR ",0|" -"1,1-0:0.0.0*255(@#)," D_METERNR ",," DJ_METERNR ",0|" - - -"2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",2|" - -"3,1-0:1.8.0*255(@100," D_H2oIN ",cbm," DJ_COUNTER ",2"; -#endif - - -#if METER==WGS_COMBO -#undef METERS_USED -#define METERS_USED 3 - -struct METER_DESC const meter_desc[METERS_USED]={ - [0]={1,'c',0,10,"H20",-1,1,0}, - [1]={4,'c',0,50,"GAS",-1,1,0}, - [2]={3,'s',0,SML_BAUDRATE,"SML",-1,1,0}}; - -const uint8_t meter[]= - - -"1,1-0:1.8.0*255(@10000," D_H2oIN ",cbm," DJ_COUNTER ",4|" - - -"2,=h==================|" -"2,1-0:1.8.0*255(@100," D_GasIN ",cbm," DJ_COUNTER ",3|" - -"3,=h==================|" - -"3,77070100010800ff@1000," D_TPWRIN ",KWh," DJ_TPWRIN ",3|" -"3,=h==================|" - -"3,77070100100700ff@1," D_TPWRCURR ",W," DJ_TPWRCURR ",2|" -"3,=h -------------------------------|" -"3,=m 10+11+12 @100," D_StL1L2L3 ",A," DJ_CSUM ",2|" - -"3,=m 13+14+15/#3 @100," D_SpL1L2L3 ",V," DJ_VAVG ",2|" -"3,=h==================|" - -"3,77070100240700ff@1," D_TPWRCURR1 ",W," DJ_TPWRCURR1 ",2|" - -"3,77070100380700ff@1," D_TPWRCURR2 ",W," DJ_TPWRCURR2 ",2|" - -"3,770701004c0700ff@1," D_TPWRCURR3 ",W," DJ_TPWRCURR3 ",2|" -"3,=h -------------------------------|" - -"3,770701001f0700ff@100," D_Strom_L1 ",A," DJ_CURR1 ",2|" - -"3,77070100330700ff@100," D_Strom_L2 ",A," DJ_CURR2 ",2|" - -"3,77070100470700ff@100," D_Strom_L3 ",A," DJ_CURR3 ",2|" -"3,=h -------------------------------|" - -"3,77070100200700ff@100," D_Spannung_L1 ",V," DJ_VOLT1 ",2|" - -"3,77070100340700ff@100," D_Spannung_L2 ",V," DJ_VOLT2 ",2|" - -"3,77070100480700ff@100," D_Spannung_L3 ",V," DJ_VOLT3 ",2|" -"3,=h==================|" - -"3,77070100000009ff@#," D_METERSID ",," DJ_METERSID ",0|" -"3,=h--------------------------------"; -#endif -# 499 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_53_sml.ino" -#define USE_SML_MEDIAN_FILTER - - -#ifndef SML_MAX_VARS -#define SML_MAX_VARS 20 -#endif - - -#define MAX_METERS 5 -double meter_vars[SML_MAX_VARS]; - -#define MAX_DVARS MAX_METERS*2 -double dvalues[MAX_DVARS]; -uint32_t dtimes[MAX_DVARS]; -uint8_t meters_used; - -struct METER_DESC const *meter_desc_p; -const uint8_t *meter_p; -uint8_t meter_spos[MAX_METERS]; - - -TasmotaSerial *meter_ss[MAX_METERS]; - - -#define SML_BSIZ 48 -uint8_t smltbuf[MAX_METERS][SML_BSIZ]; - - -#define METER_ID_SIZE 24 -char meter_id[MAX_METERS][METER_ID_SIZE]; - -#define EBUS_SYNC 0xaa -#define EBUS_ESC 0xa9 - -uint8_t sml_send_blocks; -uint8_t sml_100ms_cnt; -uint8_t sml_desc_cnt; - -#ifdef USE_SML_MEDIAN_FILTER - -#define MEDIAN_SIZE 5 -struct SML_MEDIAN_FILTER { -double buffer[MEDIAN_SIZE]; -int8_t index; -} sml_mf[SML_MAX_VARS]; - -#ifndef FLT_MAX -#define FLT_MAX 99999999 -#endif - -double sml_median_array(double *array,uint8_t len) { - uint8_t ind[len]; - uint8_t mind=0,index=0,flg; - double min=FLT_MAX; - - for (uint8_t hcnt=0; hcntbuffer[mf->index]=in; - mf->index++; - if (mf->index>=MEDIAN_SIZE) mf->index=0; - - return sml_median_array(mf->buffer,MEDIAN_SIZE); -# 603 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_53_sml.ino" -} -#endif - -#ifdef ANALOG_OPTO_SENSOR - -uint8_t ads1115_up; - - -#define SAMPLE_BIT (0x8000) - -#define ADS1115_COMP_QUEUE_SHIFT 0 -#define ADS1115_COMP_LATCH_SHIFT 2 -#define ADS1115_COMP_POLARITY_SHIFT 3 -#define ADS1115_COMP_MODE_SHIFT 4 -#define ADS1115_DATA_RATE_SHIFT 5 -#define ADS1115_MODE_SHIFT 8 -#define ADS1115_PGA_SHIFT 9 -#define ADS1115_MUX_SHIFT 12 - -enum ads1115_comp_queue { - ADS1115_COMP_QUEUE_AFTER_ONE = 0, - ADS1115_COMP_QUEUE_AFTER_TWO = 0x1 << ADS1115_COMP_QUEUE_SHIFT, - ADS1115_COMP_QUEUE_AFTER_FOUR = 0x2 << ADS1115_COMP_QUEUE_SHIFT, - ADS1115_COMP_QUEUE_DISABLE = 0x3 << ADS1115_COMP_QUEUE_SHIFT, - ADS1115_COMP_QUEUE_MASK = 0x3 << ADS1115_COMP_QUEUE_SHIFT, -}; - -enum ads1115_comp_latch { - ADS1115_COMP_LATCH_NO = 0, - ADS1115_COMP_LATCH_YES = 1 << ADS1115_COMP_LATCH_SHIFT, - ADS1115_COMP_LATCH_MASK = 1 << ADS1115_COMP_LATCH_SHIFT, -}; - -enum ads1115_comp_polarity { - ADS1115_COMP_POLARITY_ACTIVE_LOW = 0, - ADS1115_COMP_POLARITY_ACTIVE_HIGH = 1 << ADS1115_COMP_POLARITY_SHIFT, - ADS1115_COMP_POLARITY_MASK = 1 << ADS1115_COMP_POLARITY_SHIFT, -}; - -enum ads1115_comp_mode { - ADS1115_COMP_MODE_WINDOW = 0, - ADS1115_COMP_MODE_HYSTERESIS = 1 << ADS1115_COMP_MODE_SHIFT, - ADS1115_COMP_MODE_MASK = 1 << ADS1115_COMP_MODE_SHIFT, -}; - -enum ads1115_data_rate { - ADS1115_DATA_RATE_8_SPS = 0, - ADS1115_DATA_RATE_16_SPS = 0x1 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_32_SPS = 0x2 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_64_SPS = 0x3 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_128_SPS = 0x4 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_250_SPS = 0x5 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_475_SPS = 0x6 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_860_SPS = 0x7 << ADS1115_DATA_RATE_SHIFT, - ADS1115_DATA_RATE_MASK = 0x7 << ADS1115_DATA_RATE_SHIFT, -}; - -enum ads1115_mode { - ADS1115_MODE_CONTINUOUS = 0, - ADS1115_MODE_SINGLE_SHOT = 1 << ADS1115_MODE_SHIFT, - ADS1115_MODE_MASK = 1 << ADS1115_MODE_SHIFT, -}; - -enum ads1115_pga { - ADS1115_PGA_TWO_THIRDS = 0, - ADS1115_PGA_ONE = 0x1 << ADS1115_PGA_SHIFT, - ADS1115_PGA_TWO = 0x2 << ADS1115_PGA_SHIFT, - ADS1115_PGA_FOUR = 0x3 << ADS1115_PGA_SHIFT, - ADS1115_PGA_EIGHT = 0x4 << ADS1115_PGA_SHIFT, - ADS1115_PGA_SIXTEEN = 0x5 << ADS1115_PGA_SHIFT, - ADS1115_PGA_MASK = 0x7 << ADS1115_PGA_SHIFT, -}; - - -enum ads1115_mux { - ADS1115_MUX_DIFF_AIN0_AIN1 = 0, - ADS1115_MUX_DIFF_AIN0_AIN3 = 0x1 << ADS1115_MUX_SHIFT, - ADS1115_MUX_DIFF_AIN1_AIN3 = 0x2 << ADS1115_MUX_SHIFT, - ADS1115_MUX_DIFF_AIN2_AIN3 = 0x3 << ADS1115_MUX_SHIFT, - ADS1115_MUX_GND_AIN0 = 0x4 << ADS1115_MUX_SHIFT, - ADS1115_MUX_GND_AIN1 = 0x5 << ADS1115_MUX_SHIFT, - ADS1115_MUX_GND_AIN2 = 0x6 << ADS1115_MUX_SHIFT, - ADS1115_MUX_GND_AIN3 = 0x7 << ADS1115_MUX_SHIFT, - ADS1115_MUX_MASK = 0x7 << ADS1115_MUX_SHIFT, -}; - -class ADS1115 { -public: - ADS1115(uint8_t address = 0x48); - - void begin(); - uint8_t trigger_sample(); - uint8_t reset(); - bool is_sample_in_progress(); - int16_t read_sample(); - float sample_to_float(int16_t val); - float read_sample_float(); - - void set_comp_queue(enum ads1115_comp_queue val) { set_config(val, ADS1115_COMP_QUEUE_MASK); } - void set_comp_latching(enum ads1115_comp_latch val) { set_config(val, ADS1115_COMP_LATCH_MASK); } - void set_comp_polarity(enum ads1115_comp_polarity val) { set_config(val, ADS1115_COMP_POLARITY_MASK); } - void set_comp_mode(enum ads1115_comp_mode val) { set_config(val, ADS1115_COMP_MODE_MASK); } - void set_data_rate(enum ads1115_data_rate val) { set_config(val, ADS1115_DATA_RATE_MASK); } - void set_mode(enum ads1115_mode val) { set_config(val, ADS1115_MODE_MASK); } - void set_pga(enum ads1115_pga val) { set_config(val, ADS1115_PGA_MASK); m_voltage_range = val >> ADS1115_PGA_SHIFT; } - void set_mux(enum ads1115_mux val) { set_config(val, ADS1115_MUX_MASK); } - -private: - void set_config(uint16_t val, uint16_t mask) { - m_config = (m_config & ~mask) | val; - } - - uint8_t write_register(uint8_t reg, uint16_t val); - uint16_t read_register(uint8_t reg); - - uint8_t m_address; - uint16_t m_config; - int m_voltage_range; -}; - - -enum ads1115_register { - ADS1115_REGISTER_CONVERSION = 0, - ADS1115_REGISTER_CONFIG = 1, - ADS1115_REGISTER_LOW_THRESH = 2, - ADS1115_REGISTER_HIGH_THRESH = 3, -}; - -#define FACTOR 32768.0 -static float ranges[] = { 6.144 / FACTOR, 4.096 / FACTOR, 2.048 / FACTOR, 1.024 / FACTOR, 0.512 / FACTOR, 0.256 / FACTOR}; - -ADS1115::ADS1115(uint8_t address) -{ - m_address = address; - m_config = ADS1115_COMP_QUEUE_AFTER_ONE | - ADS1115_COMP_LATCH_NO | - ADS1115_COMP_POLARITY_ACTIVE_LOW | - ADS1115_COMP_MODE_WINDOW | - ADS1115_DATA_RATE_128_SPS | - ADS1115_MODE_SINGLE_SHOT | - ADS1115_MUX_GND_AIN0; - set_pga(ADS1115_PGA_ONE); -} - -uint8_t ADS1115::write_register(uint8_t reg, uint16_t val) -{ - Wire.beginTransmission(m_address); - Wire.write(reg); - Wire.write(val>>8); - Wire.write(val & 0xFF); - return Wire.endTransmission(); -} - -uint16_t ADS1115::read_register(uint8_t reg) -{ - Wire.beginTransmission(m_address); - Wire.write(reg); - Wire.endTransmission(); - - uint8_t result = Wire.requestFrom((int)m_address, 2, 1); - if (result != 2) { - return 0; - } - - uint16_t val; - - val = Wire.read() << 8; - val |= Wire.read(); - return val; -} - -void ADS1115::begin() -{ - Wire.begin(); -} - -uint8_t ADS1115::trigger_sample() -{ - return write_register(ADS1115_REGISTER_CONFIG, m_config | SAMPLE_BIT); -} - -uint8_t ADS1115::reset() -{ - Wire.beginTransmission(0); - Wire.write(0x6); - return Wire.endTransmission(); -} - -bool ADS1115::is_sample_in_progress() -{ - uint16_t val = read_register(ADS1115_REGISTER_CONFIG); - return (val & SAMPLE_BIT) == 0; -} - -int16_t ADS1115::read_sample() -{ - return read_register(ADS1115_REGISTER_CONVERSION); -} - -float ADS1115::sample_to_float(int16_t val) -{ - return val * ranges[m_voltage_range]; -} - -float ADS1115::read_sample_float() -{ - return sample_to_float(read_sample()); -} - -ADS1115 adc; - -void ADS1115_init(void) { - - ads1115_up=0; - if (!i2c_flg) return; - - adc.begin(); - adc.set_data_rate(ADS1115_DATA_RATE_128_SPS); - adc.set_mode(ADS1115_MODE_CONTINUOUS); - adc.set_mux(ADS1115_MUX_DIFF_AIN0_AIN3); - adc.set_pga(ADS1115_PGA_TWO); - - int16_t val = adc.read_sample(); - ads1115_up=1; -} - -#endif - -char sml_start; -uint8_t dump2log=0; - -#define SML_SAVAILABLE Serial_available() -#define SML_SREAD Serial_read() -#define SML_SPEAK Serial_peek() - -bool Serial_available() { - uint8_t num=dump2log&7; - if (num<1 || num>meters_used) num=1; - return meter_ss[num-1]->available(); -} - -uint8_t Serial_read() { - uint8_t num=dump2log&7; - if (num<1 || num>meters_used) num=1; - return meter_ss[num-1]->read(); -} - -uint8_t Serial_peek() { - uint8_t num=dump2log&7; - if (num<1 || num>meters_used) num=1; - return meter_ss[num-1]->peek(); -} - -uint8_t sml_logindex; - -void Dump2log(void) { - -int16_t index=0,hcnt=0; -uint32_t d_lastms; -uint8_t dchars[16]; - - - - if (dump2log&8) { - - while (SML_SAVAILABLE) { - log_data[index]=':'; - index++; - log_data[index]=' '; - index++; - d_lastms=millis(); - while ((millis()-d_lastms)<40) { - if (SML_SAVAILABLE) { - uint8_t c=SML_SREAD; - sprintf(&log_data[index],"%02x ",c); - dchars[hcnt]=c; - index+=3; - hcnt++; - if (hcnt>15) { - - log_data[index]='='; - index++; - log_data[index]='>'; - index++; - log_data[index]=' '; - index++; - for (uint8_t ccnt=0; ccnt<16; ccnt++) { - if (isprint(dchars[ccnt])) { - log_data[index]=dchars[ccnt]; - } else { - log_data[index]=' '; - } - index++; - } - break; - } - } - } - if (index>0) { - log_data[index]=0; - AddLog(LOG_LEVEL_INFO); - index=0; - hcnt=0; - } - } - } else { - if (meter_desc_p[(dump2log&7)-1].type=='o') { - - while (SML_SAVAILABLE) { - char c=SML_SREAD&0x7f; - if (c=='\n' || c=='\r') { - log_data[sml_logindex]=0; - AddLog(LOG_LEVEL_INFO); - sml_logindex=2; - log_data[0]=':'; - log_data[1]=' '; - break; - } - log_data[sml_logindex]=c; - if (sml_logindex2) { - log_data[index]=0; - AddLog(LOG_LEVEL_INFO); - } - } - } -} - - -uint8_t *skip_sml(uint8_t *cp,int16_t *res) { - uint8_t len,len1,type; - len=*cp&0xf; - type=*cp&0x70; - if (type==0x70) { - - - cp++; - while (len--) { - len1=*cp&0x0f; - cp+=len1; - } - *res=0; - } else { - - *res=(signed char)*(cp+1); - cp+=len; - } - return cp; -} - - - -double sml_getvalue(unsigned char *cp,uint8_t index) { -uint8_t len,unit,type; -int16_t scaler,result; -int64_t value; -double dval; - - - - cp=skip_sml(cp,&result); - - cp=skip_sml(cp,&result); - - cp=skip_sml(cp,&result); - - cp=skip_sml(cp,&result); - scaler=result; - - type=*cp&0x70; - len=*cp&0x0f; - cp++; - if (type==0x50 || type==0x60) { - - uint64_t uvalue=0; - uint8_t nlen=len; - while (--nlen) { - uvalue<<=8; - uvalue|=*cp++; - } - if (type==0x50) { - - switch (len-1) { - case 1: - - value=(signed char)uvalue; - break; - case 2: - -#ifdef DWS74_BUG - if (scaler==-2) { - value=(uint32_t)uvalue; - } else { - value=(int16_t)uvalue; - } -#else - value=(int16_t)uvalue; -#endif - break; - case 3: - case 4: - - value=(int32_t)uvalue; - break; - case 5: - case 6: - case 7: - case 8: - - value=(int64_t)uvalue; - break; - } - } else { - - value=uvalue; - } - - } else { - if (!(type&0xf0)) { - - - - if (len==9) { - - cp++; - uint32_t s1,s2; - s1=*cp<<16|*(cp+1)<<8|*(cp+2); - cp+=4; - s2=*cp<<16|*(cp+1)<<8|*(cp+2); - sprintf(&meter_id[index][0],"%u-%u",s1,s2); - } else { - - char *str=&meter_id[index][0]; - for (type=0; type= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; - } - return rVal; -} - -uint8_t sb_counter; - - -double CharToDouble(const char *str) -{ - - char strbuf[24]; - - strlcpy(strbuf, str, sizeof(strbuf)); - char *pt = strbuf; - while ((*pt != '\0') && isblank(*pt)) { pt++; } - - signed char sign = 1; - if (*pt == '-') { sign = -1; } - if (*pt == '-' || *pt=='+') { pt++; } - - double left = 0; - if (*pt != '.') { - left = atoi(pt); - while (isdigit(*pt)) { pt++; } - } - - double right = 0; - if (*pt == '.') { - pt++; - right = atoi(pt); - while (isdigit(*pt)) { - pt++; - right /= 10.0; - } - } - - double result = left + right; - if (sign < 0) { - return -result; - } - return result; -} - - - -void ebus_esc(uint8_t *ebus_buffer, unsigned char len) { - short count,count1; - for (count=0; countavailable()) { - meter_ss[meters]->read(); - } -} - - -void sml_shift_in(uint32_t meters,uint32_t shard) { - uint32_t count; - if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p') { - - for (count=0; countread(); - - if (meter_desc_p[meters].type=='o') { - smltbuf[meters][SML_BSIZ-1]=iob&0x7f; - } else if (meter_desc_p[meters].type=='s') { - smltbuf[meters][SML_BSIZ-1]=iob; - } else if (meter_desc_p[meters].type=='r') { - smltbuf[meters][SML_BSIZ-1]=iob; - } else if (meter_desc_p[meters].type=='m' || meter_desc_p[meters].type=='M') { - smltbuf[meters][meter_spos[meters]] = iob; - meter_spos[meters]++; - if (meter_spos[meters]>=9) { - SML_Decode(meters); - sml_empty_receiver(meters); - meter_spos[meters]=0; - } - } else if (meter_desc_p[meters].type=='p') { - smltbuf[meters][meter_spos[meters]] = iob; - meter_spos[meters]++; - if (meter_spos[meters]>=7) { - SML_Decode(meters); - sml_empty_receiver(meters); - meter_spos[meters]=0; - } - } else { - if (iob==EBUS_SYNC) { - - - if (meter_spos[meters]>4+5) { - - uint8_t tlen=smltbuf[meters][4]+5; - - if (smltbuf[meters][tlen]=ebus_CalculateCRC(smltbuf[meters],tlen)) { - ebus_esc(smltbuf[meters],tlen); - SML_Decode(meters); - } else { - - - } - } - meter_spos[meters]=0; - return; - } - smltbuf[meters][meter_spos[meters]] = iob; - meter_spos[meters]++; - if (meter_spos[meters]>=SML_BSIZ) { - meter_spos[meters]=0; - } - } - sb_counter++; - if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p') SML_Decode(meters); -} - - - -void SML_Poll(void) { -uint32_t meters; - - for (meters=0; metersavailable()) { - sml_shift_in(meters,0); - } - } - } -} - - -void SML_Decode(uint8_t index) { - const char *mp=(const char*)meter_p; - int8_t mindex; - uint8_t *cp; - uint8_t dindex=0,vindex=0; - delay(0); - while (mp != NULL) { - - - - mindex=((*mp)&7)-1; - - if (mindex<0 || mindex>=meters_used) mindex=0; - mp+=2; - if (*mp=='=' && *(mp+1)=='h') { - mp = strchr(mp, '|'); - if (mp) mp++; - continue; - } - - if (index!=mindex) goto nextsect; - - - cp=&smltbuf[mindex][0]; - - - if (*mp=='=') { - - mp++; - - if (*mp=='m' && !sb_counter) { - - - mp++; - while (*mp==' ') mp++; - - double dvar; - uint8_t opr; - uint32_t ind; - ind=atoi(mp); - while (*mp>='0' && *mp<='9') mp++; - if (ind<1 || ind>SML_MAX_VARS) ind=1; - dvar=meter_vars[ind-1]; - for (uint8_t p=0;p<5;p++) { - if (*mp=='@') { - - meter_vars[vindex]=dvar; - mp++; - SML_Immediate_MQTT((const char*)mp,vindex,mindex); - break; - } - opr=*mp; - mp++; - uint8_t iflg=0; - if (*mp=='#') { - iflg=1; - mp++; - } - ind=atoi(mp); - while (*mp>='0' && *mp<='9') mp++; - if (ind<1 || ind>SML_MAX_VARS) ind=1; - switch (opr) { - case '+': - if (iflg) dvar+=ind; - else dvar+=meter_vars[ind-1]; - break; - case '-': - if (iflg) dvar-=ind; - else dvar-=meter_vars[ind-1]; - break; - case '*': - if (iflg) dvar*=ind; - else dvar*=meter_vars[ind-1]; - break; - case '/': - if (iflg) dvar/=ind; - else dvar/=meter_vars[ind-1]; - break; - } - while (*mp==' ') mp++; - if (*mp=='@') { - - meter_vars[vindex]=dvar; - mp++; - SML_Immediate_MQTT((const char*)mp,vindex,mindex); - break; - } - } - } else if (*mp=='d') { - - if (dindex='0' && *mp<='9') mp++; - if (ind<1 || ind>SML_MAX_VARS) ind=1; - uint32_t delay=atoi(mp)*1000; - uint32_t dtime=millis()-dtimes[dindex]; - if (dtime>delay) { - - dtimes[dindex]=millis(); - double vdiff = meter_vars[ind-1]-dvalues[dindex]; - dvalues[dindex]=meter_vars[ind-1]; - meter_vars[vindex]=(double)360000.0*vdiff/((double)dtime/10000.0); - - mp=strchr(mp,'@'); - if (mp) { - mp++; - SML_Immediate_MQTT((const char*)mp,vindex,mindex); - } - } - dindex++; - } - } else if (*mp=='h') { - - mp = strchr(mp, '|'); - if (mp) mp++; - continue; - } - } else { - - uint8_t found=1; - uint32_t ebus_dval=99; - float mbus_dval=99; - while (*mp!='@') { - if (meter_desc_p[mindex].type=='o' || meter_desc_p[mindex].type=='c') { - if (*mp++!=*cp++) { - found=0; - } - } else { - if (meter_desc_p[mindex].type=='s') { - - uint8_t val = hexnibble(*mp++) << 4; - val |= hexnibble(*mp++); - if (val!=*cp++) { - found=0; - } - } else { - - - if (*mp=='x' && *(mp+1)=='x') { - - mp+=2; - cp++; - } else if (!strncmp(mp,"UUuuUUuu",8)) { - uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); - ebus_dval=val; - mbus_dval=val; - mp+=8; - cp+=4; - } else if (*mp=='U' && *(mp+1)=='U' && *(mp+2)=='u' && *(mp+3)=='u'){ - uint16_t val = cp[1]|(cp[0]<<8); - mbus_dval=val; - ebus_dval=val; - mp+=4; - cp+=2; - } else if (!strncmp(mp,"SSssSSss",8)) { - int32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); - ebus_dval=val; - mbus_dval=val; - mp+=8; - cp+=4; - } else if (*mp=='u' && *(mp+1)=='u' && *(mp+2)=='U' && *(mp+3)=='U'){ - uint16_t val = cp[0]|(cp[1]<<8); - mbus_dval=val; - ebus_dval=val; - mp+=4; - cp+=2; - } else if (*mp=='u' && *(mp+1)=='u') { - uint8_t val = *cp++; - mbus_dval=val; - ebus_dval=val; - mp+=2; - } else if (*mp=='s' && *(mp+1)=='s' && *(mp+2)=='S' && *(mp+3)=='S') { - int16_t val = *cp|(*(cp+1)<<8); - mbus_dval=val; - ebus_dval=val; - mp+=4; - cp+=2; - } else if (*mp=='S' && *(mp+1)=='S' && *(mp+2)=='s' && *(mp+3)=='s') { - int16_t val = cp[1]|(cp[0]<<8); - mbus_dval=val; - ebus_dval=val; - mp+=4; - cp+=2; - } - else if (*mp=='s' && *(mp+1)=='s') { - int8_t val = *cp++; - mbus_dval=val; - ebus_dval=val; - mp+=2; - } - else if (!strncmp(mp,"ffffffff",8)) { - uint32_t val= (cp[0]<<24)|(cp[1]<<16)|(cp[2]<<8)|(cp[3]<<0); - float *fp=(float*)&val; - ebus_dval=*fp; - mbus_dval=*fp; - mp+=8; - cp+=4; - } - else if (!strncmp(mp,"FFffFFff",8)) { - - uint32_t val= (cp[1]<<0)|(cp[0]<<8)|(cp[3]<<16)|(cp[2]<<24); - float *fp=(float*)&val; - ebus_dval=*fp; - mbus_dval=*fp; - mp+=8; - cp+=4; - } - else if (!strncmp(mp,"eeeeee",6)) { - uint32_t val=(cp[0]<<16)|(cp[1]<<8)|(cp[2]<<0); - mbus_dval=val; - mp+=6; - cp+=3; - } - else if (!strncmp(mp,"vvvvvv",6)) { - mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/10.0); - mp+=6; - cp+=3; - } - else if (!strncmp(mp,"cccccc",6)) { - mbus_dval=(float)((cp[0]<<8)|(cp[1])) + ((float)cp[2]/100.0); - mp+=6; - cp+=3; - } - else if (!strncmp(mp,"pppp",4)) { - mbus_dval=(float)((cp[0]<<8)|cp[1]); - mp+=4; - cp+=2; - } - else { - uint8_t val = hexnibble(*mp++) << 4; - val |= hexnibble(*mp++); - if (val!=*cp++) { - found=0; - } - } - } - } - } - if (found) { - - mp++; - if (*mp=='#') { - - mp++; - if (meter_desc_p[mindex].type=='o') { - for (uint8_t p=0;p>=shift; - ebus_dval&=1; - mp+=2; - } - if (*mp=='i') { - - mp++; - uint8_t mb_index=strtol((char*)mp,(char**)&mp,10); - if (mb_index!=meter_desc_p[mindex].index) { - goto nextsect; - } - uint16_t crc = MBUS_calculateCRC(&smltbuf[mindex][0],7); - if (lowByte(crc)!=smltbuf[mindex][7]) goto nextsect; - if (highByte(crc)!=smltbuf[mindex][8]) goto nextsect; - dval=mbus_dval; - - mp++; - } else { - if (meter_desc_p[mindex].type=='p') { - uint8_t crc = SML_PzemCrc(&smltbuf[mindex][0],6); - if (crc!=smltbuf[mindex][6]) goto nextsect; - dval=mbus_dval; - } else { - dval=ebus_dval; - } - } - - } -#ifdef USE_SML_MEDIAN_FILTER - if (meter_desc_p[mindex].flag&16) { - meter_vars[vindex]=sml_median(&sml_mf[vindex],dval); - } else { - meter_vars[vindex]=dval; - } -#else - meter_vars[vindex]=dval; -#endif - - - double fac=CharToDouble((char*)mp); - meter_vars[vindex]/=fac; - SML_Immediate_MQTT((const char*)mp,vindex,mindex); - } - } - } -nextsect: - - if (vindex=meters_used) lastmind=0; - while (mp != NULL) { - - mindex=((*mp)&7)-1; - if (mindex<0 || mindex>=meters_used) mindex=0; - mp+=2; - if (*mp=='=' && *(mp+1)=='h') { - mp+=2; - - if (json) { - mp = strchr(mp, '|'); - if (mp) mp++; - continue; - } - - uint8_t i; - for (i=0;isml_counters[index].sml_debounce) { - RtcSettings.pulse_counter[index]++; - InjektCounterValue(sml_counters[index].sml_cnt_old_state,RtcSettings.pulse_counter[index]); - } - } else { - - sml_counters[index].sml_counter_ltime=millis(); - } -} - -void SML_CounterUpd1(void) { - SML_CounterUpd(0); -} - -void SML_CounterUpd2(void) { - SML_CounterUpd(1); -} - -void SML_CounterUpd3(void) { - SML_CounterUpd(2); -} - -void SML_CounterUpd4(void) { - SML_CounterUpd(3); -} - -#ifdef USE_SCRIPT -struct METER_DESC script_meter_desc[MAX_METERS]; -uint8_t *script_meter; -#endif - -#ifndef METER_DEF_SIZE -#define METER_DEF_SIZE 3000 -#endif - -bool Gpio_used(uint8_t gpiopin) { - for (uint16_t i=0;iM",-2,0); - if (meter_script==99) { - - if (script_meter) free(script_meter); - script_meter=0; - uint8_t *tp=0; - uint16_t index=0; - uint8_t section=0; - uint8_t srcpin=0; - char *lp=glob_script_mem.scriptptr; - sml_send_blocks=0; - while (lp) { - if (!section) { - if (*lp=='>' && *(lp+1)=='M') { - lp+=2; - meters_used=strtol(lp,0,10); - section=1; - uint32_t mlen=0; - for (uint32_t cnt=0;cnt') { - if (*(tp-1)=='|') *(tp-1)=0; - break; - } - if (*lp=='+') { - - - lp++; - index=*lp&7; - lp+=2; - if (index<1 || index>meters_used) goto next_line; - index--; - srcpin=strtol(lp,&lp,10); - if (Gpio_used(srcpin)) { - AddLog_P(LOG_LEVEL_INFO, PSTR("gpio rx double define!")); -dddef_exit: - if (script_meter) free(script_meter); - script_meter=0; - meters_used=METERS_USED; - goto init10; - } - script_meter_desc[index].srcpin=srcpin; - if (*lp!=',') goto next_line; - lp++; - script_meter_desc[index].type=*lp; - lp+=2; - script_meter_desc[index].flag=strtol(lp,&lp,10); - if (*lp!=',') goto next_line; - lp++; - script_meter_desc[index].params=strtol(lp,&lp,10); - if (*lp!=',') goto next_line; - lp++; - script_meter_desc[index].prefix[7]=0; - for (uint32_t cnt=0; cnt<8; cnt++) { - if (*lp==SCRIPT_EOL || *lp==',') { - script_meter_desc[index].prefix[cnt]=0; - break; - } - script_meter_desc[index].prefix[cnt]=*lp++; - } - if (*lp==',') { - lp++; - script_meter_desc[index].trxpin=strtol(lp,&lp,10); - if (Gpio_used(script_meter_desc[index].trxpin)) { - AddLog_P(LOG_LEVEL_INFO, PSTR("gpio tx double define!")); - goto dddef_exit; - } - if (*lp!=',') goto next_line; - lp++; - script_meter_desc[index].tsecs=strtol(lp,&lp,10); - if (*lp==',') { - lp++; - char txbuff[256]; - uint32_t txlen=0,tx_entries=1; - for (uint32_t cnt=0; cntmeters_used) goto next_line; - while (1) { - if (*lp==SCRIPT_EOL) { - if (*(tp-1)!='|') *tp++='|'; - goto next_line; - } - *tp++=*lp++; - index++; - if (index>=METER_DEF_SIZE) break; - } - } - - } - -next_line: - if (*lp==SCRIPT_EOL) { - lp++; - } else { - lp = strchr(lp, SCRIPT_EOL); - if (!lp) break; - lp++; - } - } - *tp=0; - meter_desc_p=script_meter_desc; - meter_p=script_meter; - } -#endif - -init10: - typedef void (*function)(); - function counter_callbacks[] = {SML_CounterUpd1,SML_CounterUpd2,SML_CounterUpd3,SML_CounterUpd4}; - uint8_t cindex=0; - - for (byte i = 0; i < MAX_COUNTERS; i++) { - RtcSettings.pulse_counter[i]=Settings.pulse_counter[i]; - sml_counters[i].sml_cnt_last_ts=millis(); - } - for (uint8_t meters=0; metersbegin(meter_desc_p[meters].params)) { - meter_ss[meters]->flush(); - } - if (meter_ss[meters]->hardwareSerial()) { - if (meter_desc_p[meters].type=='M') { - Serial.begin(meter_desc_p[meters].params, SERIAL_8E1); - } - ClaimSerial(); - } - - } - } - -} - - -#ifdef USE_SML_SCRIPT_CMD -uint32_t SML_SetBaud(uint32_t meter, uint32_t br) { - if (meter<1 || meter>meters_used) return 0; - meter--; - if (!meter_ss[meter]) return 0; - if (meter_ss[meter]->begin(br)) { - meter_ss[meter]->flush(); - } - if (meter_ss[meter]->hardwareSerial()) { - if (meter_desc_p[meter].type=='M') { - Serial.begin(br, SERIAL_8E1); - } - } - return 1; -} - -uint32_t SML_Write(uint32_t meter,char *hstr) { - if (meter<1 || meter>meters_used) return 0; - meter--; - if (!meter_ss[meter]) return 0; - SML_Send_Seq(meter,hstr); - return 1; -} -#endif - - -void SetDBGLed(uint8_t srcpin, uint8_t ledpin) { - pinMode(ledpin, OUTPUT); - if (digitalRead(srcpin)) { - digitalWrite(ledpin,LOW); - } else { - digitalWrite(ledpin,HIGH); - } -} - - -void SML_Counter_Poll(void) { -uint16_t meters,cindex=0; -uint32_t ctime=millis(); - - for (meters=0; meters0) { - if (ctime-sml_counters[cindex].sml_cnt_last_ts>meter_desc_p[meters].params) { - sml_counters[cindex].sml_cnt_last_ts=ctime; - - if (meter_desc_p[meters].flag&2) { - -#ifdef ANALOG_OPTO_SENSOR - if (ads1115_up) { - int16_t val = adc.read_sample(); - if (val>sml_counters[cindex].ana_max) sml_counters[cindex].ana_max=val; - if (val10) { - sml_counters[cindex].sml_cnt_last_ts=ctime; -#ifdef DEBUG_CNT_LED1 - if (cindex==0) SetDBGLed(meter_desc_p[meters].srcpin,DEBUG_CNT_LED1); -#endif -#ifdef DEBUG_CNT_LED2 - if (cindex==1) SetDBGLed(meter_desc_p[meters].srcpin,DEBUG_CNT_LED2); -#endif - } - } - cindex++; - } - } -} - -#ifdef USE_SCRIPT -char *SML_Get_Sequence(char *cp,uint32_t index) { - if (!index) return cp; - uint32_t cindex=0; - while (cp) { - cp=strchr(cp,','); - if (cp) { - cp++; - cindex++; - if (cindex==index) { - return cp; - } - } - } -} - -void SML_Check_Send(void) { - sml_100ms_cnt++; - char *cp; - for (uint32_t cnt=sml_desc_cnt; cnt=0 && script_meter_desc[cnt].txmem) { - if ((sml_100ms_cnt%script_meter_desc[cnt].tsecs)==0) { - if (script_meter_desc[cnt].max_index>1) { - script_meter_desc[cnt].index++; - if (script_meter_desc[cnt].index>=script_meter_desc[cnt].max_index) { - script_meter_desc[cnt].index=0; - sml_desc_cnt++; - } - cp=SML_Get_Sequence(script_meter_desc[cnt].txmem,script_meter_desc[cnt].index); - - } else { - cp=script_meter_desc[cnt].txmem; - - sml_desc_cnt++; - } - - SML_Send_Seq(cnt,cp); - if (sml_desc_cnt>=meters_used) { - sml_desc_cnt=0; - } - break; - } - } else { - sml_desc_cnt++; - } - - if (sml_desc_cnt>=meters_used) { - sml_desc_cnt=0; - } - } -} - -uint8_t sml_hexnibble(char chr) { - uint8_t rVal = 0; - if (isdigit(chr)) { - rVal = chr - '0'; - } else { - if (chr >= 'A' && chr <= 'F') rVal = chr + 10 - 'A'; - if (chr >= 'a' && chr <= 'f') rVal = chr + 10 - 'a'; - } - return rVal; -} - - -void SML_Send_Seq(uint32_t meter,char *seq) { - uint8_t sbuff[32]; - uint8_t *ucp=sbuff,slen=0; - char *cp=seq; - while (*cp) { - if (!*cp || !*(cp+1)) break; - if (*cp==',') break; - uint8_t iob=(sml_hexnibble(*cp) << 4) | sml_hexnibble(*(cp+1)); - cp+=2; - *ucp++=iob; - slen++; - if (slen>=sizeof(sbuff)) break; - } - if (script_meter_desc[meter].type=='m' || script_meter_desc[meter].type=='M') { - *ucp++=0; - *ucp++=2; - - uint16_t crc = MBUS_calculateCRC(sbuff,6); - *ucp++=lowByte(crc); - *ucp++=highByte(crc); - slen+=4; - } - if (script_meter_desc[meter].type=='o') { - for (uint32_t cnt=0;cntwrite(sbuff,slen); -} -#endif - -uint16_t MBUS_calculateCRC(uint8_t *frame, uint8_t num) { - uint16_t crc, flag; - crc = 0xFFFF; - for (uint32_t i = 0; i < num; i++) { - crc ^= frame[i]; - for (uint32_t j = 8; j; j--) { - if ((crc & 0x0001) != 0) { - crc >>= 1; - crc ^= 0xA001; - } else { - crc >>= 1; - } - } - } - return crc; -} - -uint8_t SML_PzemCrc(uint8_t *data, uint8_t len) { - uint16_t crc = 0; - for (uint32_t i = 0; i < len; i++) crc += *data++; - return (uint8_t)(crc & 0xFF); -} - - -uint8_t CalcEvenParity(uint8_t data) { -uint8_t parity=0; - - while(data) { - parity^=(data &1); - data>>=1; - } - return parity; -} -# 2361 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_53_sml.ino" -bool XSNS_53_cmd(void) { - bool serviced = true; - if (XdrvMailbox.data_len > 0) { - char *cp=XdrvMailbox.data; - if (*cp=='d') { - - cp++; - uint8_t index=atoi(cp); - if ((index&7)>meters_used) index=1; - if (index>0 && meter_desc_p[(index&7)-1].type=='c') { - index=0; - } - dump2log=index; - ResponseTime_P(PSTR(",\"SML\":{\"CMD\":\"dump: %d\"}}"),dump2log); - } else if (*cp=='c') { - - cp++; - uint8_t index=*cp&7; - if (index<1 || index>MAX_COUNTERS) index=1; - cp++; - while (*cp==' ') cp++; - if (isdigit(*cp)) { - uint32_t cval=atoi(cp); - while (isdigit(*cp)) cp++; - RtcSettings.pulse_counter[index-1]=cval; - uint8_t cindex=0; - for (uint8_t meters=0; metersaddress, INA226_REG_CALIBRATION, si->calibrationValue); - -} - - - - - - -bool Ina226TestPresence(uint8_t device) -{ - - - - uint16_t config = I2cRead16( slaveInfo[device].address, INA226_REG_CONFIG ); - - - if (config != slaveInfo[device].config) - return false; - - return true; - -} - -void Ina226ResetActive(void) -{ - Ina226SlaveInfo_t *p = slaveInfo; - - for (uint32_t i = 0; i < INA226_MAX_ADDRESSES; i++) { - p = &slaveInfo[i]; - - uint8_t addr = p->address; - if (addr) { - I2cResetActive(addr); - } - } -} - - - - - -void Ina226Init() -{ - uint32_t i; - - slavesFound = 0; - - Ina226SlaveInfo_t *p = slaveInfo; -# 215 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_54_ina226.ino" - for (i = 0; i < 4; i++){ - *p = {0}; - } - - - - - - for (i = 0; i < INA226_MAX_ADDRESSES; i++){ - uint8_t addr = pgm_read_byte(probeAddresses + i); - - if (I2cActive(addr)) { continue; } - - - - - if (!Settings.ina226_i_fs[i]) - continue; - - - - - - - if (!I2cWrite16( addr, INA226_REG_CONFIG, INA226_CONFIG_RESET)){ - - AddLog_P2( LOG_LEVEL_DEBUG, "No INA226 at address: %02X", addr); - continue; - } - - - - uint16_t config = I2cRead16( addr, INA226_REG_CONFIG ); - - - if (INA226_RES_CONFIG != config) - continue; - - - config = INA226_DEF_CONFIG; - - - if (!I2cWrite16( addr, INA226_REG_CONFIG, config)) - continue; - - - p = &slaveInfo[i]; - - p->address = addr; - - p->config = config; - - - p->i_lsb = (((float) Settings.ina226_i_fs[i])/10.0f)/32768.0f; - - - - uint32_t r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[i]); - - - - p->calibrationValue = ((uint16_t) (0.00512/(p->i_lsb * r_shunt_uohms/1000000.0f))); - - p->present = true; - - - Ina226SetCalibration(i); - - I2cSetActiveFound(addr, Ina226Str); - - slavesFound++; - } -} - - - - - -float Ina226ReadBus_v(uint8_t device) -{ - uint8_t addr = slaveInfo[device].address; - int16_t reg_bus_v = I2cReadS16( addr, INA226_REG_BUSVOLTAGE); - - float result = ((float) reg_bus_v) * 0.00125f; - - return result; - -} - - - - - -float Ina226ReadShunt_i(uint8_t device) -{ - uint8_t addr = slaveInfo[device].address; - int16_t reg_shunt_i = I2cReadS16( addr, INA226_REG_CURRENT); - - float result = ((float) reg_shunt_i) * slaveInfo[device].i_lsb; - - return result; -} - - - - - -float Ina226ReadPower_w(uint8_t device) -{ - uint8_t addr = slaveInfo[device].address; - int16_t reg_shunt_i = I2cReadS16( addr, INA226_REG_POWER); - - float result = ((float) reg_shunt_i) * (slaveInfo[device].i_lsb * 25.0); - - return result; -} - - - - - - -void Ina226Read(uint8_t device) -{ - - voltages[device] = Ina226ReadBus_v(device); - currents[device] = Ina226ReadShunt_i(device); - powers[device] = Ina226ReadPower_w(device); - - - - -} - - - - - -void Ina226EverySecond() -{ - - for (uint8_t device = 0; device < INA226_MAX_ADDRESSES; device++){ - - if (slavesFound && slaveInfo[device].present && Ina226TestPresence(device)){ - Ina226Read(device); - } - else { - powers[device] = currents[device] = voltages[device] = 0.0f; - - - - - - - slaveInfo[device].present = false; - } - } -} - - - - - -bool Ina226CommandSensor() -{ - bool serviced = true; - bool show_config = false; - char param_str[64]; - char *cp, *params[4]; - uint8_t i, param_count, device, p1 = XdrvMailbox.payload; - uint32_t r_shunt_uohms; - uint16_t compact_r_shunt_uohms; - - - - - - if (XdrvMailbox.data_len > 62){ - return false; - } - - strncpy(param_str, XdrvMailbox.data, XdrvMailbox.data_len + 1); - param_str[XdrvMailbox.data_len] = 0; - - - for (cp = param_str, i = 0, param_count = 0; *cp && (i < XdrvMailbox.data_len + 1) && (param_count <= 3); i++) - if (param_str[i] == ' ' || param_str[i] == ',' || param_str[i] == 0){ - param_str[i] = 0; - params[param_count] = cp; - - param_count++; - cp = param_str + i + 1; - } - - - if (p1 < 10 || p1 >= 50){ - - switch (p1){ - case 1: - Ina226ResetActive(); - Ina226Init(); - Response_P(PSTR("{\"Sensor54-Command-Result\":{\"SlavesFound\":%d}}"),slavesFound); - break; - - case 2: - restart_flag = 2; - Response_P(PSTR("{\"Sensor54-Command-Result\":{\"Restart_flag\":%d}}"),restart_flag); - break; - - default: - serviced = false; - } - } - else if (p1 < 50){ - - device = (p1 / 10) - 1; - switch (p1 % 10){ - case 0: - show_config = true; - break; - - case 1: - r_shunt_uohms = (uint32_t) ((CharToFloat(params[1])) * 1000000.0f); - - - - if (r_shunt_uohms > 32767){ - uint32_t r_shunt_mohms = r_shunt_uohms/1000UL; - Settings.ina226_r_shunt[device] = (uint16_t) (r_shunt_mohms | 0x8000); - } - else - Settings.ina226_r_shunt[device] = (uint16_t) r_shunt_uohms; - - - show_config = true; - break; - - case 2: - Settings.ina226_i_fs[device] = (uint16_t) ((CharToFloat(params[1])) * 10.0f); - - show_config = true; - break; - - - default: - serviced = false; - break; - } - } - else - serviced = false; - - if (show_config) { - char shunt_r_str[16]; - char fs_i_str[16]; - - - r_shunt_uohms = _expand_r_shunt(Settings.ina226_r_shunt[device]); - dtostrfd(((float)r_shunt_uohms)/1000000.0f, 6, shunt_r_str); - - dtostrfd(((float)Settings.ina226_i_fs[device])/10.0f, 1, fs_i_str); - - Response_P(PSTR("{\"Sensor54-device-settings-%d\":{\"SHUNT_R\":%s,\"FS_I\":%s}}"), - device + 1, shunt_r_str, fs_i_str); - } - - return serviced; -} - - - - - -#ifdef USE_WEBSERVER -const char HTTP_SNS_INA226_DATA[] PROGMEM = - "{s}%s " D_VOLTAGE "{m}%s " D_UNIT_VOLT "{e}" - "{s}%s " D_CURRENT "{m}%s " D_UNIT_AMPERE "{e}" - "{s}%s " D_POWERUSAGE "{m}%s " D_UNIT_WATT "{e}"; -#endif - -void Ina226Show(bool json) -{ - int i, num_found; - for (num_found = 0, i = 0; i < INA226_MAX_ADDRESSES; i++) { - - if (!slaveInfo[i].present) - continue; - - num_found++; - - char voltage[16]; - dtostrfd(voltages[i], Settings.flag2.voltage_resolution, voltage); - char current[16]; - dtostrfd(currents[i], Settings.flag2.current_resolution, current); - char power[16]; - dtostrfd(powers[i], Settings.flag2.wattage_resolution, power); - char name[16]; - snprintf_P(name, sizeof(name), PSTR("INA226%c%d"),IndexSeparator(), i + 1); - - - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"Id\":%d,\"" D_JSON_VOLTAGE "\":%s,\"" D_JSON_CURRENT "\":%s,\"" D_JSON_POWERUSAGE "\":%s}"), - name, i, voltage, current, power); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_VOLTAGE, voltage); - DomoticzSensor(DZ_CURRENT, current); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_INA226_DATA, name, voltage, name, current, name, power); -#endif - } - - } - -} -# 546 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_54_ina226.ino" -bool Xsns54(byte callback_id) -{ - if (!I2cEnabled(XI2C_35)) { return false; } - - - bool result = false; - - - switch (callback_id) { - case FUNC_EVERY_SECOND: - Ina226EverySecond(); - break; - case FUNC_JSON_APPEND: - Ina226Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Ina226Show(0); - break; -#endif - case FUNC_COMMAND_SENSOR: - if (XSNS_54 == XdrvMailbox.index) { - result = Ina226CommandSensor(); - } - break; - case FUNC_INIT: - Ina226Init(); - break; - } - - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_55_hih_series.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_55_hih_series.ino" -#ifdef USE_I2C -#ifdef USE_HIH6 -# 33 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_55_hih_series.ino" -#define XSNS_55 55 -#define XI2C_36 36 - -#define HIH6_ADDR 0x27 - -struct HIH6 { - float temperature = 0; - float humidity = 0; - uint8_t valid = 0; - uint8_t type = 0; - char types[4] = "HIH"; -} Hih6; - -bool Hih6Read(void) -{ - Wire.beginTransmission(HIH6_ADDR); - if (Wire.endTransmission() != 0) { return false; } - - delay(40); - - uint8_t data[4]; - Wire.requestFrom(HIH6_ADDR, 4); - if (4 == Wire.available()) { - data[0] = Wire.read(); - data[1] = Wire.read(); - data[2] = Wire.read(); - data[3] = Wire.read(); - } else { return false; } - - - - Hih6.humidity = ConvertHumidity(((float)(((data[0] & 0x3F) << 8) | data[1]) * 100.0) / 16383.0); - - int temp = ((data[2] << 8) | (data[3] & 0xFC)) / 4; - Hih6.temperature = ConvertTemp(((float)temp / 16384.0) * 165.0 - 40.0); - - Hih6.valid = SENSOR_MAX_MISS; - return true; -} - - - -void Hih6Detect(void) -{ - if (I2cActive(HIH6_ADDR)) { return; } - - if (uptime < 2) { delay(20); } - Hih6.type = Hih6Read(); - if (Hih6.type) { - I2cSetActiveFound(HIH6_ADDR, Hih6.types); - } -} - -void Hih6EverySecond(void) -{ - if (uptime &1) { - - if (!Hih6Read()) { - AddLogMissed(Hih6.types, Hih6.valid); - } - } -} - -void Hih6Show(bool json) -{ - if (Hih6.valid) { - char temperature[33]; - dtostrfd(Hih6.temperature, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(Hih6.humidity, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMPHUM, Hih6.types, temperature, humidity); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzTempHumSensor(temperature, humidity); - } -#endif -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, Hih6.temperature); - KnxSensor(KNX_HUMIDITY, Hih6.humidity); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, Hih6.types, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, Hih6.types, humidity); -#endif - } - } -} - - - - - -bool Xsns55(uint8_t function) -{ - if (!I2cEnabled(XI2C_36)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Hih6Detect(); - } - else if (Hih6.type) { - switch (function) { - case FUNC_EVERY_SECOND: - Hih6EverySecond(); - break; - case FUNC_JSON_APPEND: - Hih6Show(1); - break; - #ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Hih6Show(0); - break; - #endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_56_hpma.ino" -# 21 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_56_hpma.ino" -#ifdef USE_HPMA -# 31 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_56_hpma.ino" -#define XSNS_56 56 - -#include -#include - -TasmotaSerial *HpmaSerial; -HPMA115S0 *hpma115S0; - -uint8_t hpma_type = 1; -uint8_t hpma_valid = 0; - -struct hpmadata { - unsigned int pm10; - unsigned int pm2_5; -} hpma_data; - - - -void HpmaSecond(void) -{ - unsigned int pm2_5, pm10; - - - - - if (hpma115S0->ReadParticleMeasurement(&pm2_5, &pm10)) { - hpma_data.pm2_5 = pm2_5; - hpma_data.pm10 = pm10; - hpma_valid = 1; - } - -} - -void HpmaInit(void) -{ - hpma_type = 0; - if (pin[GPIO_HPMA_RX] < 99 && pin[GPIO_HPMA_TX] < 99) { - HpmaSerial = new TasmotaSerial(pin[GPIO_HPMA_RX], pin[GPIO_HPMA_TX], 1); - hpma115S0 = new HPMA115S0(*HpmaSerial); - - if (HpmaSerial->begin(9600)) { - if (HpmaSerial->hardwareSerial()) { - ClaimSerial(); - } - hpma_type = 1; - hpma115S0->Init(); - hpma115S0->StartParticleMeasurement(); - } - } -} - -#ifdef USE_WEBSERVER -const char HTTP_HPMA_SNS[] PROGMEM = - "{s}HPMA " D_ENVIRONMENTAL_CONCENTRATION "2.5 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}" - "{s}HPMA " D_ENVIRONMENTAL_CONCENTRATION "10 " D_UNIT_MICROMETER "{m}%s " D_UNIT_MICROGRAM_PER_CUBIC_METER "{e}"; -#endif - -void HpmaShow(bool json) -{ - if (hpma_valid) { - char pm10[33]; - snprintf_P(pm10, 33, PSTR("%d"), hpma_data.pm10); - char pm2_5[33]; - snprintf_P(pm2_5, 33, PSTR("%d"), hpma_data.pm2_5); - - if (json) { - ResponseAppend_P(PSTR(",\"HPMA\":{\"PM2.5\":%d,\"PM10\":%d}"), hpma_data.pm2_5, hpma_data.pm10); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_VOLTAGE, pm2_5); - DomoticzSensor(DZ_CURRENT, pm10); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_HPMA_SNS, pm2_5, pm10); -#endif - } - } -} - - - - - -bool Xsns56(uint8_t function) -{ - bool result = false; - - if (hpma_type) { - switch (function) { - case FUNC_INIT: - HpmaInit(); - break; - case FUNC_EVERY_SECOND: - HpmaSecond(); - break; - case FUNC_COMMAND_SENSOR: - if (XSNS_56 == XdrvMailbox.index) { - return true; - } - break; - case FUNC_JSON_APPEND: - HpmaShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - HpmaShow(0); - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_57_tsl2591.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_57_tsl2591.ino" -#ifdef USE_I2C -#ifdef USE_TSL2591 - - - - - - - -#define XSNS_57 57 -#define XI2C_40 40 - -#define TSL2591_ADDRESS 0x29 - -#include - -Adafruit_TSL2591 tsl = Adafruit_TSL2591(); - -uint8_t tsl2591_type = 0; -uint8_t tsl2591_valid = 0; -float tsl2591_lux = 0; - -void Tsl2591Init(void) -{ - - if (I2cSetDevice(0x29)) { - if (tsl.begin()) { - tsl.setGain(TSL2591_GAIN_MED); - tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS); - tsl2591_type = 1; - I2cSetActiveFound(TSL2591_ADDRESS, "TSL2591"); - } - } -} - -bool Tsl2591Read(void) -{ - uint32_t lum = tsl.getFullLuminosity(); - uint16_t ir, full; - ir = lum >> 16; - full = lum & 0xFFFF; - tsl2591_lux = tsl.calculateLux(full, ir); - tsl2591_valid = 1; -} - -void Tsl2591EverySecond(void) -{ - Tsl2591Read(); -} - -#ifdef USE_WEBSERVER -const char HTTP_SNS_TSL2591[] PROGMEM = - "{s}TSL2591 " D_ILLUMINANCE "{m}%s " D_UNIT_LUX "{e}"; -#endif - -void Tsl2591Show(bool json) -{ - if (tsl2591_valid) { - char lux_str[10]; - dtostrf(tsl2591_lux, sizeof(lux_str)-1, 3, lux_str); - if (json) { - ResponseAppend_P(PSTR(",\"TSL2591\":{\"" D_JSON_ILLUMINANCE "\":%s}"), lux_str); -#ifdef USE_DOMOTICZ - if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, tsl2591_lux); } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TSL2591, lux_str); -#endif - } - } -} - - - - - -bool Xsns57(uint8_t function) -{ - if (!I2cEnabled(XI2C_40)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Tsl2591Init(); - } - else if (tsl2591_type) { - switch (function) { - case FUNC_EVERY_SECOND: - Tsl2591EverySecond(); - break; - case FUNC_JSON_APPEND: - Tsl2591Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Tsl2591Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_58_dht12.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_58_dht12.ino" -#ifdef USE_I2C -#ifdef USE_DHT12 - - - - - - -#define XSNS_58 58 -#define XI2C_41 41 - -#define DHT12_ADDR 0x5C - -struct DHT12 { - float temperature = NAN; - float humidity = NAN; - uint8_t valid = 0; - uint8_t count = 0; - char name[6] = "DHT12"; -} Dht12; - -bool Dht12Read(void) -{ - if (Dht12.valid) { Dht12.valid--; } - - Wire.beginTransmission(DHT12_ADDR); - Wire.write(0); - if (Wire.endTransmission() != 0) { return false; } - - delay(50); - - Wire.requestFrom(DHT12_ADDR, 5); - delay(5); - uint8_t humidity = Wire.read(); - uint8_t humidityTenth = Wire.read(); - uint8_t temp = Wire.read(); - uint8_t tempTenth = Wire.read(); - uint8_t checksum = Wire.read(); - - Dht12.humidity = ConvertHumidity( (float) humidity + (float) humidityTenth/(float) 10.0 ); - Dht12.temperature = ConvertTemp( (float) temp + (float) tempTenth/(float) 10.0 ); - - if (isnan(Dht12.temperature) || isnan(Dht12.humidity)) { return false; } - - Dht12.valid = SENSOR_MAX_MISS; - return true; -} - - - -void Dht12Detect(void) -{ - if (I2cActive(DHT12_ADDR)) { return; } - - if (Dht12Read()) { - I2cSetActiveFound(DHT12_ADDR, Dht12.name); - Dht12.count = 1; - } -} - -void Dht12EverySecond(void) -{ - if (uptime &1) { - - if (!Dht12Read()) { - AddLogMissed(Dht12.name, Dht12.valid); - } - } -} - -void Dht12Show(bool json) -{ - if (Dht12.valid) { - char temperature[33]; - dtostrfd(Dht12.temperature, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(Dht12.humidity, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMPHUM, Dht12.name, temperature, humidity); -#ifdef USE_DOMOTICZ - if ((0 == tele_period)) { - DomoticzTempHumSensor(temperature, humidity); - } -#endif -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, Dht12.temperature); - KnxSensor(KNX_HUMIDITY, Dht12.humidity); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, Dht12.name, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, Dht12.name, humidity); -#endif - } - } -} - - - - - -bool Xsns58(uint8_t function) -{ - if (!I2cEnabled(XI2C_41)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - Dht12Detect(); - } - else if (Dht12.count) { - switch (function) { - case FUNC_EVERY_SECOND: - Dht12EverySecond(); - break; - case FUNC_JSON_APPEND: - Dht12Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - Dht12Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_59_ds1624.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_59_ds1624.ino" -#ifdef USE_I2C -#ifdef USE_DS1624 - - - - - - - -#define XSNS_59 59 -#define XI2C_42 42 - -#define DS1624_MEM_REGISTER 0x17 -#define DS1624_CONF_REGISTER 0xAC -#define DS1624_TEMP_REGISTER 0xAA -#define DS1624_START_REGISTER 0xEE -#define DS1624_STOP_REGISTER 0x22 - -#define DS1621_COUNTER_REGISTER 0xA8 -#define DS1621_SLOPE_REGISTER 0xA9 - -#define DS1621_CFG_1SHOT (1<<0) -#define DS1621_CFG_DONE (1<<7) - -enum { - DS1624_TYPE_DS1624, - DS1624_TYPE_DS1621 -}; - -#define DS1624_MAX_SENSORS 8 - -bool ds1624_init = false; - -struct { - float value; - uint8_t type; - int errcnt; - int misscnt; - bool valid; - char name[9]; -} ds1624_sns[DS1624_MAX_SENSORS]; - -uint32_t DS1624_Idx2Addr(uint32_t idx) { - return 0x48 + idx; -} - -int DS1624_Restart(uint8_t config, uint32_t idx) { - uint32_t addr = DS1624_Idx2Addr(idx); - if ((config & 1) == 1) { - config &= ~(DS1621_CFG_DONE|DS1621_CFG_1SHOT); - I2cWrite8(addr, DS1624_CONF_REGISTER, config); - delay(10); - AddLog_P2(LOG_LEVEL_ERROR, "%s addr %x is reset, reconfig: %x", ds1624_sns[idx].name, addr, config); - } - I2cValidRead(addr, DS1624_START_REGISTER, 1); -} - -void DS1624_HotPlugUp(uint32_t idx) -{ - uint32_t addr = DS1624_Idx2Addr(idx); - - if (I2cActive(addr)) { return; } - if (!I2cSetDevice(addr)) { return; } - - uint8_t config; - if (I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) { - uint8_t tmp; - ds1624_sns[idx].type = (I2cValidRead8(&tmp, addr, DS1624_MEM_REGISTER)) ? DS1624_TYPE_DS1624 : DS1624_TYPE_DS1621; - - snprintf_P(ds1624_sns[idx].name, sizeof(ds1624_sns[idx].name), PSTR("DS162%c%c%d"), - (ds1624_sns[idx].type == DS1624_TYPE_DS1621) ? '1' : '4', IndexSeparator(), idx); - I2cSetActiveFound(addr, ds1624_sns[idx].name); - - ds1624_sns[idx].valid = true; - ds1624_sns[idx].errcnt = 0; - ds1624_sns[idx].misscnt = 0; - DS1624_Restart(config,idx); - AddLog_P2(LOG_LEVEL_INFO, "Hot Plug %s addr %x config: %x", ds1624_sns[idx].name, addr, config); - } -} - -void DS1624_HotPlugDown(int idx) -{ - uint32_t addr = DS1624_Idx2Addr(idx); - if (!I2cActive(addr)) { return; } - I2cResetActive(addr); - ds1624_sns[idx].valid = false; - AddLog_P2(LOG_LEVEL_INFO, "Hot UnPlug %s", ds1624_sns[idx].name); -} - -bool DS1624GetTemp(float *value, int idx) -{ - uint32_t addr = DS1624_Idx2Addr(idx); - - uint8_t config; - if (!I2cValidRead8(&config, addr, DS1624_CONF_REGISTER)) { - ds1624_sns[idx].misscnt++; - AddLog_P2(LOG_LEVEL_INFO, "%s device missing (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].misscnt); - return false; - } - ds1624_sns[idx].misscnt=0; - if (config & (DS1621_CFG_1SHOT|DS1621_CFG_DONE)) { - ds1624_sns[idx].errcnt++; - AddLog_P2(LOG_LEVEL_INFO, "%s config error, restart... (errors: %i)", ds1624_sns[idx].name, ds1624_sns[idx].errcnt); - DS1624_Restart(config, idx); - return false; - } - - uint16_t t; - if (!I2cValidRead16(&t, DS1624_Idx2Addr(idx), DS1624_TEMP_REGISTER)) { return false; } - if (ds1624_sns[idx].type == DS1624_TYPE_DS1624) { - *value = ((float)(int8_t)(t>>8)) + ((t>>4)&0xf)*0.0625; - } else { - - *value = ((float)(int8_t)(t>>8)); - uint8_t remain; - if (!I2cValidRead8(&remain, addr, DS1621_COUNTER_REGISTER)) { return true; } - uint8_t perc; - if (!I2cValidRead8(&perc, addr, DS1621_SLOPE_REGISTER)) { return true; } - float fix=(float)(perc - remain)/(float)perc; - *value+=fix; - } - ds1624_sns[idx].errcnt=0; - config &= ~(DS1621_CFG_DONE); - I2cWrite8(addr, DS1624_CONF_REGISTER, config); - return true; -} - -void DS1624HotPlugScan(void) -{ - uint16_t t; - - for (uint32_t idx = 0; idx < DS1624_MAX_SENSORS; idx++) { - uint32_t addr = DS1624_Idx2Addr(idx); - if (I2cActive(addr) && !ds1624_sns[idx].valid) { - continue; - } - if (ds1624_sns[idx].valid) { - if ((ds1624_sns[idx].misscnt>2)||(ds1624_sns[idx].errcnt>2)) { - DS1624_HotPlugDown(idx); - continue; - } - } - DS1624_HotPlugUp(idx); - } -} - -void DS1624EverySecond(void) -{ - float t; - for (uint32_t i = 0; i < DS1624_MAX_SENSORS; i++) { - if (!ds1624_sns[i].valid) { continue; } - if (!DS1624GetTemp(&t, i)) { continue; } - ds1624_sns[i].value = ConvertTemp(t); - } -} - -void DS1624Show(bool json) -{ - char temperature[33]; - bool once = true; - - for (uint32_t i = 0; i < DS1624_MAX_SENSORS; i++) { - if (!ds1624_sns[i].valid) { continue; } - - dtostrfd(ds1624_sns[i].value, Settings.flag2.temperature_resolution, temperature); - if (json) { - ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}"), ds1624_sns[i].name, temperature); - if ((0 == tele_period) && once) { -#ifdef USE_DOMOTICZ - DomoticzSensor(DZ_TEMP, temperature); -#endif -#ifdef USE_KNX - KnxSensor(KNX_TEMPERATURE, temperature); -#endif - once = false; - } -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, ds1624_sns[i].name, temperature, TempUnit()); -#endif - } - } -} - - - - - -bool Xsns59(uint8_t function) -{ - if (!I2cEnabled(XI2C_42)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - if (!ds1624_init) { - memset(ds1624_sns, 0, sizeof(ds1624_sns)); - ds1624_init = true; - DS1624HotPlugScan(); - } - } - switch (function) { - case FUNC_HOTPLUG_SCAN: - DS1624HotPlugScan(); - break; - case FUNC_EVERY_SECOND: - DS1624EverySecond(); - break; - case FUNC_JSON_APPEND: - DS1624Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - DS1624Show(0); - break; -#endif - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_60_GPS.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_60_GPS.ino" -#ifdef USE_GPS -# 113 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_60_GPS.ino" -#define XSNS_60 60 - -#include "NTPServer.h" -#include "NTPPacket.h" - - - - - -#define D_CMND_UBX "UBX" - -const char S_JSON_UBX_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_UBX "%s\":%d}"; - -const char kUBXTypes[] PROGMEM = "UBX"; - -#define UBX_LAT_LON_THRESHOLD 1000 - -#define UBX_SERIAL_BUFFER_SIZE 256 -#define UBX_TCP_PORT 1234 - - - - - -const char UBLOX_INIT[] PROGMEM = { - - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x24, - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x2B, - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x32, - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x39, - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x04,0x40, - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x05,0x47, - - - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xDC, - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xB9, - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xC0, - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x92, - - - - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x13,0xBE, - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x14,0xC5, - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x01,0x00,0x00,0x00,0x00,0x32,0x97, - - - - - - -}; - -char UBX_name[4]; - -struct UBX_t { - const char UBX_HEADER[2] = { 0xB5, 0x62 }; - const char NAV_POSLLH_HEADER[2] = { 0x01, 0x02 }; - const char NAV_STATUS_HEADER[2] = { 0x01, 0x03 }; - const char NAV_TIME_HEADER[2] = { 0x01, 0x21 }; - - struct entry_t { - int32_t lat; - int32_t lon; - uint32_t time; - }; - - union { - entry_t values; - uint8_t bytes[sizeof(entry_t)]; - } rec_buffer; - - struct POLL_MSG { - uint8_t cls; - uint8_t id; - uint16_t zero; - }; - - struct NAV_POSLLH { - uint8_t cls; - uint8_t id; - uint16_t len; - uint32_t iTOW; - int32_t lon; - int32_t lat; - int32_t alt; - int32_t hMSL; - uint32_t hAcc; - uint32_t vAcc; - }; - - struct NAV_STATUS { - uint8_t cls; - uint8_t id; - uint16_t len; - uint32_t iTOW; - uint8_t gpsFix; - uint8_t flags; - uint8_t fixStat; - uint8_t flags2; - uint32_t ttff; - uint32_t msss; - }; - - struct NAV_TIME_UTC { - uint8_t cls; - uint8_t id; - uint16_t len; - uint32_t iTOW; - uint32_t tAcc; - int32_t nano; - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t min; - uint8_t sec; - struct { - uint8_t UTC:1; - uint8_t WKN:1; - uint8_t TOW:1; - uint8_t padding:5; - } valid; - }; - - struct CFG_RATE { - uint8_t cls; - uint8_t id; - uint16_t len; - uint16_t measRate; - uint16_t navRate; - uint16_t timeRef; - char CK[2]; - }; - - struct { - uint32_t last_iTOW; - int32_t last_alt; - uint32_t last_hAcc; - uint32_t last_vAcc; - uint8_t gpsFix; - uint8_t non_empty_loops; - uint16_t log_interval; - } state; - - struct { - uint32_t init:1; - uint32_t filter_noise:1; - uint32_t send_when_new:1; - uint32_t send_UI_only:1; - uint32_t runningNTP:1; - uint32_t forceUTCupdate:1; - uint32_t runningVPort:1; - - } mode; - - union { - NAV_POSLLH navPosllh; - NAV_STATUS navStatus; - NAV_TIME_UTC navTime; - POLL_MSG pollMsg; - CFG_RATE cfgRate; - } Message; - - uint8_t TCPbuf[UBX_SERIAL_BUFFER_SIZE]; - size_t TCPbufSize; -} UBX; - -enum UBXMsgType { - MT_NONE, - MT_NAV_POSLLH, - MT_NAV_STATUS, - MT_NAV_TIME, - MT_POLL -}; - -#ifdef USE_FLOG -FLOG *Flog = nullptr; -#endif -TasmotaSerial *UBXSerial; - -NtpServer timeServer(PortUdp); - -WiFiServer vPortServer(UBX_TCP_PORT); -WiFiClient vPortClient; - - - - - -void UBXcalcChecksum(char* CK, size_t msgSize) -{ - memset(CK, 0, 2); - for (int i = 0; i < msgSize; i++) { - CK[0] += ((char*)(&UBX.Message))[i]; - CK[1] += CK[0]; - } -} - -bool UBXcompareMsgHeader(const char* msgHeader) -{ - char* ptr = (char*)(&UBX.Message); - return ptr[0] == msgHeader[0] && ptr[1] == msgHeader[1]; -} - -void UBXinitCFG(void) -{ - for (uint32_t i = 0; i < sizeof(UBLOX_INIT); i++) { - UBXSerial->write( pgm_read_byte(UBLOX_INIT+i) ); - } - DEBUG_SENSOR_LOG(PSTR("UBX: turn off NMEA")); -} - -void UBXTriggerTele(void) -{ - mqtt_data[0] = '\0'; - if (MqttShowSensor()) { - MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); -#ifdef USE_RULES - RulesTeleperiod(); -#endif - } -} - - - -void UBXDetect(void) -{ - UBX.mode.init = 0; - if ((pin[GPIO_GPS_RX] < 99) && (pin[GPIO_GPS_TX] < 99)) { - UBXSerial = new TasmotaSerial(pin[GPIO_GPS_RX], pin[GPIO_GPS_TX], 1, 0, UBX_SERIAL_BUFFER_SIZE); - if (UBXSerial->begin(9600)) { - DEBUG_SENSOR_LOG(PSTR("UBX: started serial")); - if (UBXSerial->hardwareSerial()) { - ClaimSerial(); - DEBUG_SENSOR_LOG(PSTR("UBX: claim HW")); - } - } - } - else { - return; - } - - UBXinitCFG(); - UBX.mode.init = 1; - -#ifdef USE_FLOG - if (!Flog) { - Flog = new FLOG; - Flog->init(); - } -#endif - - UBX.state.log_interval = 10; - UBX.mode.send_UI_only = true; - UBXTriggerTele(); -} - -uint32_t UBXprocessGPS() -{ - static uint32_t fpos = 0; - static char checksum[2]; - static uint8_t currentMsgType = MT_NONE; - static size_t payloadSize = sizeof(UBX.Message); - - - uint32_t data_bytes = 0; - while ( UBXSerial->available() ) { - data_bytes++; - byte c = UBXSerial->read(); - if (UBX.mode.runningVPort){ - UBX.TCPbuf[data_bytes-1] = c; - UBX.TCPbufSize = data_bytes; - } - if ( fpos < 2 ) { - - if ( c == UBX.UBX_HEADER[fpos] ) { - fpos++; - } else { - fpos = 0; - } - } else { - - - - - - if ( (fpos-2) < payloadSize ) { - ((char*)(&UBX.Message))[fpos-2] = c; - } - fpos++; - - if ( fpos == 4 ) { - - - if ( UBXcompareMsgHeader(UBX.NAV_POSLLH_HEADER) ) { - currentMsgType = MT_NAV_POSLLH; - payloadSize = sizeof(UBX_t::NAV_POSLLH); - DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_POSLLH")); - } - else if ( UBXcompareMsgHeader(UBX.NAV_STATUS_HEADER) ) { - currentMsgType = MT_NAV_STATUS; - payloadSize = sizeof(UBX_t::NAV_STATUS); - DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_STATUS")); - } - else if ( UBXcompareMsgHeader(UBX.NAV_TIME_HEADER) ) { - currentMsgType = MT_NAV_TIME; - payloadSize = sizeof(UBX_t::NAV_TIME_UTC); - DEBUG_SENSOR_LOG(PSTR("UBX: got NAV_TIME_UTC")); - } - else { - - fpos = 0; - continue; - } - } - - if ( fpos == (payloadSize+2) ) { - - - UBXcalcChecksum(checksum, payloadSize); - } - else if ( fpos == (payloadSize+3) ) { - - - if ( c != checksum[0] ) { - - fpos = 0; - } - } - else if ( fpos == (payloadSize+4) ) { - - - fpos = 0; - if ( c == checksum[1] ) { - - return currentMsgType; - } - } - else if ( fpos > (payloadSize+4) ) { - - - fpos = 0; - } - } - } - - if (data_bytes!=0) { - UBX.state.non_empty_loops++; - DEBUG_SENSOR_LOG(PSTR("UBX: got %u bytes, non-empty-loop: %u"), data_bytes, UBX.state.non_empty_loops); - } else { - UBX.state.non_empty_loops = 0; - } - return MT_NONE; -} - - - - - -#ifdef USE_FLOG -void UBXsendHeader(void) -{ - WebServer->setContentLength(CONTENT_LENGTH_UNKNOWN); - WebServer->sendHeader(F("Content-Disposition"), F("attachment; filename=TASMOTA.gpx")); - WSSend(200, CT_STREAM, F( - "\r\n" - "\r\n" - "\r\n\r\n")); -} - -void UBXsendRecord(uint8_t *buf) -{ - char record[100]; - char stime[32]; - UBX_t::entry_t *entry = (UBX_t::entry_t*)buf; - snprintf_P(stime, sizeof(stime), GetDT(entry->time).c_str()); - char lat[12]; - char lon[12]; - dtostrfd((double)entry->lat/10000000.0f,7,lat); - dtostrfd((double)entry->lon/10000000.0f,7,lon); - snprintf_P(record, sizeof(record),PSTR("\n\t\n\n"),lat ,lon, stime); - - WebServer->sendContent_P(record); -} - -void UBXsendFooter(void) -{ - WebServer->sendContent(F("\n\n")); - WebServer->sendContent(""); - Rtc.user_time_entry = false; -} - - - -void UBXsendFile(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - Flog->startDownload(sizeof(UBX.rec_buffer),UBXsendHeader,UBXsendRecord,UBXsendFooter); -} -#endif - - - -void UBXSetRate(uint16_t interval) -{ - UBX.Message.cfgRate.cls = 0x06; - UBX.Message.cfgRate.id = 0x08; - UBX.Message.cfgRate.len = 6; - uint32_t measRate = (1000*(uint32_t)interval); - if (measRate > 0xffff) { - measRate = 0xffff; - } - UBX.Message.cfgRate.measRate = (uint16_t)measRate; - UBX.Message.cfgRate.navRate = 1; - UBX.Message.cfgRate.timeRef = 1; - UBXcalcChecksum(UBX.Message.cfgRate.CK, sizeof(UBX.Message.cfgRate)-sizeof(UBX.Message.cfgRate.CK)); - DEBUG_SENSOR_LOG(PSTR("UBX: requested interval: %u seconds measRate: %u ms"), interval, UBX.Message.cfgRate.measRate); - UBXSerial->write(UBX.UBX_HEADER[0]); - UBXSerial->write(UBX.UBX_HEADER[1]); - for (uint32_t i =0; iwrite(((uint8_t*)(&UBX.Message.cfgRate))[i]); - DEBUG_SENSOR_LOG(PSTR("UBX: cfgRate byte %u: %x"), i, ((uint8_t*)(&UBX.Message.cfgRate))[i]); - } - UBX.state.log_interval = 10*interval; -} - -void UBXSelectMode(uint16_t mode) -{ - DEBUG_SENSOR_LOG(PSTR("UBX: set mode to %u"),mode); - switch(mode){ -#ifdef USE_FLOG - case 0: - Flog->mode = 0; - break; - case 1: - Flog->mode = 1; - break; - case 2: - UBX.mode.filter_noise = true; - break; - case 3: - UBX.mode.filter_noise = false; - break; - case 4: - Flog->startRecording(true); - AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - appending")); - break; - case 5: - Flog->startRecording(false); - AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: start recording - new log")); - break; - case 6: - if(Flog->recording == true){ - Flog->stopRecording(); - } - AddLog_P(LOG_LEVEL_INFO, PSTR("UBX: stop recording")); - break; -#endif - case 7: - UBX.mode.send_when_new = 1; - break; - case 8: - UBX.mode.send_when_new = 0; - break; - case 9: - if (timeServer.beginListening()) { - UBX.mode.runningNTP = true; - } - break; - case 10: - UBX.mode.runningNTP = false; - break; - case 11: - UBX.mode.forceUTCupdate = true; - break; - case 12: - UBX.mode.forceUTCupdate = false; - break; - case 13: - Settings.latitude = UBX.rec_buffer.values.lat/10; - Settings.longitude = UBX.rec_buffer.values.lon/10; - break; - case 14: - vPortServer.begin(); - UBX.mode.runningVPort = 1; - break; - case 15: - - UBX.mode.runningVPort = 0; - break; - default: - if (mode>1000 && mode <1066) { - - UBXSetRate(mode-1000); - } - break; - } - UBX.mode.send_UI_only = true; - UBXTriggerTele(); -} - - - -bool UBXHandlePOSLLH() -{ - DEBUG_SENSOR_LOG(PSTR("UBX: iTOW: %u"),UBX.Message.navPosllh.iTOW); - if (UBX.state.gpsFix>1) { - if (UBX.mode.filter_noise) { - if ((UBX.Message.navPosllh.lat-UBX.rec_buffer.values.lat6) { - if(UBX.mode.runningVPort) return; - UBXinitCFG(); - AddLog_P(LOG_LEVEL_ERROR, PSTR("UBX: possible device-reset, will re-init")); - UBXSerial->flush(); - UBX.state.non_empty_loops = 0; - } -} - - - -void UBXLoop50msec(void) -{ - - if (UBX.mode.runningVPort){ - if(!vPortClient.connected()) { - vPortClient = vPortServer.available(); - } - while(vPortClient.available()) { - byte _newByte = vPortClient.read(); - UBXSerial->write(_newByte); - } - - if (UBX.TCPbufSize!=0){ - vPortClient.write((char*)UBX.TCPbuf, UBX.TCPbufSize); - UBX.TCPbufSize = 0; - } - } - - if(UBX.mode.runningNTP){ - timeServer.processOneRequest(Rtc.utc_time, UBX.state.last_iTOW%1000); - } -} - -void UBXLoop(void) -{ - static uint16_t counter; - static bool new_position; - - uint32_t msgType = UBXprocessGPS(); - - switch(msgType){ - case MT_NAV_POSLLH: - new_position = UBXHandlePOSLLH(); - break; - case MT_NAV_STATUS: - UBXHandleSTATUS(); - break; - case MT_NAV_TIME: - UBXHandleTIME(); - break; - default: - UBXHandleOther(); - break; - } - -#ifdef USE_FLOG - if (counter>UBX.state.log_interval) { - if (Flog->recording && new_position) { - UBX.rec_buffer.values.time = Rtc.local_time; - Flog->addToBuffer(UBX.rec_buffer.bytes, sizeof(UBX.rec_buffer.bytes)); - counter = 0; - } - } -#endif - - counter++; -} - - - - -#ifdef USE_WEBSERVER - - -#ifdef USE_FLOG -#ifdef DEBUG_TASMOTA_SENSOR - const char HTTP_SNS_FLOGVER[] PROGMEM = "{s}
{m}
{e}{s} FLOG with %u sectors: {m}%u bytes{e}" - "{s} FLOG next sector for REC: {m} %u {e}" - "{s} %u sector(s) with data at sector: {m} %u {e}"; - const char HTTP_SNS_FLOGREC[] PROGMEM = "{s} RECORDING (bytes in buffer) {m}%u{e}"; -#endif - - const char HTTP_SNS_FLOG[] PROGMEM = "{s}
{m}
{e}{s} Flash-Log {m} %s{e}"; - const char kFLOG_STATE0[] PROGMEM = "ready"; - const char kFLOG_STATE1[] PROGMEM = "recording"; - const char * kFLOG_STATE[] ={kFLOG_STATE0, kFLOG_STATE1}; - - const char HTTP_BTN_FLOG_DL[] PROGMEM = ""; - -#endif - const char HTTP_SNS_NTPSERVER[] PROGMEM = "{s} NTP server {m}active{e}"; - - const char HTTP_SNS_GPS[] PROGMEM = "{s} GPS latitude {m}%s{e}" - "{s} GPS longitude {m}%s{e}" - "{s} GPS altitude {m}%s m{e}" - "{s} GPS hor. Accuracy {m}%s m{e}" - "{s} GPS vert. Accuracy {m}%s m{e}" - "{s} GPS sat-fix status {m}%s{e}"; - - const char kGPSFix0[] PROGMEM = "no fix"; - const char kGPSFix1[] PROGMEM = "dead reckoning only"; - const char kGPSFix2[] PROGMEM = "2D-fix"; - const char kGPSFix3[] PROGMEM = "3D-fix"; - const char kGPSFix4[] PROGMEM = "GPS + dead reckoning combined"; - const char kGPSFix5[] PROGMEM = "Time only fix"; - const char * kGPSFix[] PROGMEM ={kGPSFix0, kGPSFix1, kGPSFix2, kGPSFix3, kGPSFix4, kGPSFix5}; - - - - -#endif - - - -void UBXShow(bool json) -{ - char lat[12]; - char lon[12]; - char alt[12]; - char hAcc[12]; - char vAcc[12]; - dtostrfd((double)UBX.rec_buffer.values.lat/10000000.0f,7,lat); - dtostrfd((double)UBX.rec_buffer.values.lon/10000000.0f,7,lon); - dtostrfd((double)UBX.state.last_alt/1000.0f,3,alt); - dtostrfd((double)UBX.state.last_vAcc/1000.0f,3,hAcc); - dtostrfd((double)UBX.state.last_hAcc/1000.0f,3,vAcc); - - if (json) { - ResponseAppend_P(PSTR(",\"GPS\":{")); - if (UBX.mode.send_UI_only) { - uint32_t i = UBX.state.log_interval / 10; - ResponseAppend_P(PSTR("\"fil\":%u,\"int\":%u}"), UBX.mode.filter_noise, i); - } else { - ResponseAppend_P(PSTR("\"lat\":%s,\"lon\":%s,\"alt\":%s,\"hAcc\":%s,\"vAcc\":%s}"), lat, lon, alt, hAcc, vAcc); - } -#ifdef USE_FLOG - ResponseAppend_P(PSTR(",\"FLOG\":{\"rec\":%u,\"mode\":%u,\"sec\":%u}"), Flog->recording, Flog->mode, Flog->sectors_left); -#endif - UBX.mode.send_UI_only = false; -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_GPS, lat, lon, alt, hAcc, vAcc, kGPSFix[UBX.state.gpsFix]); - -#ifdef DEBUG_TASMOTA_SENSOR -#ifdef USE_FLOG - WSContentSend_PD(HTTP_SNS_FLOGVER, Flog->num_sectors, Flog->size, Flog->current_sector, Flog->sectors_left, Flog->sector.header.physical_start_sector); - if (Flog->recording) { - WSContentSend_PD(HTTP_SNS_FLOGREC, Flog->sector.header.buf_pointer - 8); - } -#endif -#endif -#ifdef USE_FLOG - if (Flog->ready) { - WSContentSend_P(HTTP_SNS_FLOG,kFLOG_STATE[Flog->recording]); - } - if (!Flog->recording && Flog->found_saved_data) { - WSContentSend_P(HTTP_BTN_FLOG_DL); - } -#endif - if (UBX.mode.runningNTP) { - WSContentSend_P(HTTP_SNS_NTPSERVER); - } -#endif - } -} - - - - - -bool UBXCmd(void) -{ - bool serviced = true; - if (XdrvMailbox.data_len > 0) { - UBXSelectMode(XdrvMailbox.payload); - Response_P(S_JSON_UBX_COMMAND_NVALUE, XdrvMailbox.command, XdrvMailbox.payload); - } - return serviced; -} - - - - - -bool Xsns60(uint8_t function) -{ - bool result = false; - - if (FUNC_INIT == function) { - UBXDetect(); - } - - if (UBX.mode.init) { - switch (function) { - case FUNC_COMMAND_SENSOR: - if (XSNS_60 == XdrvMailbox.index) { - result = UBXCmd(); - } - break; - case FUNC_EVERY_50_MSECOND: - UBXLoop50msec(); - break; - case FUNC_EVERY_100_MSECOND: -#ifdef USE_FLOG - if (!Flog->running_download) -#endif - { - UBXLoop(); - } - break; -#ifdef USE_FLOG - case FUNC_WEB_ADD_HANDLER: - WebServer->on("/UBX", UBXsendFile); - break; -#endif - case FUNC_JSON_APPEND: - UBXShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: -#ifdef USE_FLOG - if (!Flog->running_download) -#endif - { - UBXShow(0); - } - break; -#endif - } - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_61_MI_NRF24.ino" -# 35 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_61_MI_NRF24.ino" -#ifdef USE_SPI -#ifdef USE_NRF24 -#ifdef USE_MIBLE - -#ifdef DEBUG_TASMOTA_SENSOR - #define MINRF_LOG_BUFFER(x) MINRFshowBuffer(x); -#else - #define MINRF_LOG_BUFFER(x) -#endif -# 53 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_61_MI_NRF24.ino" -#define XSNS_61 61 - -#include - -#define FLORA 1 -#define MJ_HT_V1 2 -#define LYWSD02 3 -#define LYWSD03 4 - -uint8_t kMINRFSlaveID[4][3] = { 0xC4,0x7C,0x8D, - 0x58,0x2D,0x34, - 0xE7,0x2E,0x00, - 0xA4,0xC1,0x38, - }; - -const char kMINRFSlaveType1[] PROGMEM = "Flora"; -const char kMINRFSlaveType2[] PROGMEM = "MJ_HT_V1"; -const char kMINRFSlaveType3[] PROGMEM = "LYWSD02"; -const char kMINRFSlaveType4[] PROGMEM = "LYWSD03"; -const char * kMINRFSlaveType[] PROGMEM = {kMINRFSlaveType1,kMINRFSlaveType2,kMINRFSlaveType3,kMINRFSlaveType4}; - - -const uint32_t kMINRFFloPDU[3] = {0x3eaa857d,0xef3b8730,0x71da7b46}; -const uint32_t kMINRFMJPDU[3] = {0x4760cd66,0xdbcc0cd3,0x33048df5}; -const uint32_t kMINRFL2PDU[3] = {0x3eaa057d,0xef3b0730,0x71da7646}; - -const uint32_t kMINRFL3PDU[3] = {0x4760cb78,0xdbcc0acd,0x33048beb}; - - -const uint8_t kMINRFlsfrList_A[3] = {0x4b,0x17,0x23}; -const uint8_t kMINRFlsfrList_B[3] = {0x21,0x72,0x43}; - - -#pragma pack(1) -struct MJ_HT_V1Header_t { - uint8_t padding[3]; - uint8_t mesSize; - uint8_t padding2; - uint16_t uuid; - uint16_t type; - uint8_t padding3[2]; - uint8_t counter; - uint8_t serial[6]; - uint8_t mode; - uint8_t padding5; - uint8_t effectiveDataLength; - }; - -struct FlowerHeader_t { - uint8_t padding[4]; - uint8_t padding2; - uint16_t uuid; - uint8_t mesSize; - uint8_t padding22; - uint16_t uuid2; - uint16_t type; - uint8_t padding3[2]; - uint8_t counter; - uint8_t serial[6]; - uint8_t padding4; - uint8_t mode; - }; - -union floraPacket_t { - struct { - uint16_t idWord; - uint8_t padding; - uint8_t serial[6]; - uint8_t padding4; - uint8_t mode; - uint8_t valueTen; - uint8_t effectiveDataLength; - uint16_t data; - } T; - struct { - uint16_t idWord; - uint8_t padding; - uint8_t serial[6]; - uint8_t padding4; - uint8_t mode; - uint8_t valueTen; - uint8_t effectiveDataLength; - uint32_t data:24; - } L; - struct { - uint8_t padding[3]; - uint8_t serial[6]; - uint8_t padding4; - uint8_t mode; - uint8_t valueTen; - uint8_t effectiveDataLength; - uint8_t data; - } M; - struct { - uint8_t padding[3]; - uint8_t serial[6]; - uint8_t padding4; - uint8_t mode; - uint8_t valueTen; - uint8_t effectiveDataLength; - uint16_t data; - } F; -}; - -union MJ_HT_V1Packet_t { - struct { - uint16_t idWord; - uint8_t padding; - uint8_t serial[6]; - uint8_t mode; - uint8_t valueTen; - uint8_t effectiveDataLength; - uint16_t temp; - uint16_t hum; - } TH; - struct { - uint8_t padding[3]; - uint8_t serial[6]; - uint8_t mode; - uint8_t valueTen; - uint8_t effectiveDataLength; - uint8_t battery; - } B; - -}; - -union LYWSD02Packet_t { - struct { - uint16_t idWord; - uint8_t padding; - uint8_t serial[6]; - uint8_t padding4; - uint8_t mode; - uint8_t valueTen; - uint8_t effectiveDataLength; - uint16_t data; - } TH; -}; - -struct bleAdvPacket_t { - uint8_t pduType; - uint8_t payloadSize; - uint8_t mac[6]; - union { - uint8_t payload[24]; - MJ_HT_V1Header_t header; - FlowerHeader_t flowerHeader; - struct { - uint8_t padding[21]; - uint16_t temp; - uint8_t hum_lb; - } TH; - struct { - uint8_t padding[21]; - uint16_t temp; - } T; - struct { - uint8_t padding[21]; - uint16_t hum; - } H; - struct { - uint8_t padding[21]; - uint8_t battery; - } B; - struct { - uint8_t padding[2]; - uint8_t mode; - uint16_t size; - uint16_t data; - } F_T; - struct { - uint8_t padding[2]; - uint8_t mode; - uint16_t size; - uint16_t data; - uint8_t data2; - } F_L; - struct { - uint8_t padding[2]; - uint8_t mode; - uint16_t size; - uint8_t data; - } F_M; - struct { - uint8_t padding[2]; - uint8_t mode; - uint16_t size; - uint16_t data; - } F_F; - }; -}; - -union FIFO_t{ - bleAdvPacket_t bleAdv; - floraPacket_t floraPacket; - MJ_HT_V1Packet_t MJ_HT_V1Packet; - LYWSD02Packet_t LYWSD02Packet; - uint8_t raw[32]; -}; - -#pragma pack(0) - -struct { - const uint8_t channel[3] = {37,38,39}; - const uint8_t frequency[3] = { 2,26,80}; - - uint16_t timer; - uint8_t currentChan=0; - FIFO_t buffer; - uint8_t packetMode; - -#ifdef DEBUG_TASMOTA_SENSOR - uint8_t streamBuffer[sizeof(buffer)]; - uint8_t lsfrBuffer[sizeof(buffer)]; -#endif - -} MINRF; - -struct mi_sensor_t{ - uint8_t type; - uint8_t serial[6]; - uint8_t showedUp; - float temp; - union { - struct { - float moisture; - float fertility; - uint32_t lux; - }; - struct { - float hum; - uint8_t bat; - }; - }; -}; - -std::vector MIBLEsensors; - - - - -bool MINRFinitBLE(uint8_t _mode) -{ - if (MINRF.timer%1000 == 0){ - NRF24radio.begin(pin[GPIO_SPI_CS],pin[GPIO_SPI_DC]); - NRF24radio.setAutoAck(false); - NRF24radio.setDataRate(RF24_1MBPS); - NRF24radio.disableCRC(); - NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] ); - NRF24radio.setRetries(0,0); - NRF24radio.setPALevel(RF24_PA_MIN); - NRF24radio.setAddressWidth(4); - - - NRF24radio.powerUp(); - } - if(NRF24radio.isChipConnected()){ - - MINRFchangePacketModeTo(_mode); - return true; - } - - return false; -} - -void MINRFhopChannel() -{ - MINRF.currentChan++; - if(MINRF.currentChan >= sizeof(MINRF.channel)) { - MINRF.currentChan = 0; - } - NRF24radio.setChannel( MINRF.frequency[MINRF.currentChan] ); -} - - - - - - - -bool MINRFreceivePacket(void) -{ - if(!NRF24radio.available()) { - return false; - } - while(NRF24radio.available()) { - - - NRF24radio.read( &MINRF.buffer, sizeof(MINRF.buffer) ); -#ifdef DEBUG_TASMOTA_SENSOR - memcpy(&MINRF.streamBuffer, &MINRF.buffer, sizeof(MINRF.buffer)); -#endif - MINRFswapbuf( sizeof(MINRF.buffer) ); - - - - switch (MINRF.packetMode) { - case 0: - MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), MINRF.channel[MINRF.currentChan] | 0x40); - break; - case 1: - MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); - break; - case 2: - MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); - break; - case 3: - MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_A[MINRF.currentChan]); - break; - case 4: - MINRFwhiten((uint8_t *)&MINRF.buffer, sizeof(MINRF.buffer), kMINRFlsfrList_B[MINRF.currentChan]); - break; - } - - - } - - return true; -} - -#ifdef DEBUG_TASMOTA_SENSOR -void MINRFshowBuffer(uint8_t (&buf)[32]){ - - - - - DEBUG_SENSOR_LOG(PSTR("MINRF: Buffer: %02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x " - "%02x %02x %02x %02x %02x %02x %02x %02x ") - ,buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9],buf[10],buf[11], - buf[12],buf[13],buf[14],buf[15],buf[16],buf[17],buf[18],buf[19],buf[20],buf[21],buf[22],buf[23], - buf[24],buf[25],buf[26],buf[27],buf[28],buf[29],buf[30],buf[31] - ); -} -#endif - - - - - - -void MINRFswapbuf(uint8_t len) -{ - uint8_t* buf = (uint8_t*)&MINRF.buffer; - while(len--) { - uint8_t a = *buf; - uint8_t v = 0; - if (a & 0x80) v |= 0x01; - if (a & 0x40) v |= 0x02; - if (a & 0x20) v |= 0x04; - if (a & 0x10) v |= 0x08; - if (a & 0x08) v |= 0x10; - if (a & 0x04) v |= 0x20; - if (a & 0x02) v |= 0x40; - if (a & 0x01) v |= 0x80; - *(buf++) = v; - } -} -# 420 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_61_MI_NRF24.ino" -void MINRFwhiten(uint8_t *buf, uint8_t len, uint8_t lfsr) -{ - while(len--) { - uint8_t res = 0; - - for (uint8_t i = 1; i; i <<= 1) { - if (lfsr & 0x01) { - lfsr ^= 0x88; - res |= i; - } - lfsr >>= 1; - } - *(buf++) ^= res; -#ifdef DEBUG_TASMOTA_SENSOR - MINRF.lsfrBuffer[31-len] = lfsr; -#endif - } -} - -void MINRFreverseMAC(uint8_t _mac[]){ - uint8_t _reversedMAC[6]; - for (uint8_t i=0; i<6; i++){ - _reversedMAC[5-i] = _mac[i]; - } - memcpy(_mac,_reversedMAC, sizeof(_reversedMAC)); -} - - - - - - -void MINRFchangePacketModeTo(uint8_t _mode) { - uint32_t (_nextchannel) = MINRF.currentChan+1; - if (_nextchannel>2) _nextchannel=0; - - switch(_mode){ - case 0: - NRF24radio.openReadingPipe(0,0x6B7D9171); - break; - case 1: - NRF24radio.openReadingPipe(0,kMINRFFloPDU[_nextchannel]); - break; - case 2: - NRF24radio.openReadingPipe(0,kMINRFMJPDU[_nextchannel]); - break; - case 3: - NRF24radio.openReadingPipe(0,kMINRFL2PDU[_nextchannel]); - break; - case 4: - if(kMINRFL3PDU[_nextchannel]==0xffffffff) break; - NRF24radio.openReadingPipe(0,kMINRFL3PDU[_nextchannel]); - break; - } - - MINRF.packetMode = _mode; -} -# 485 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_61_MI_NRF24.ino" -uint32_t MINRFgetSensorSlot(uint8_t (&_serial)[6], uint8_t _type){ - if(_type==0xff){ - DEBUG_SENSOR_LOG(PSTR("MINRF: will test MAC-type")); - for (uint32_t i=0;i<4;i++){ - if(memcmp(_serial,kMINRFSlaveID+i,3)==0){ - DEBUG_SENSOR_LOG(PSTR("MINRF: MAC is type %u"), i); - _type = i+1; - } - else { - DEBUG_SENSOR_LOG(PSTR("MINRF: MAC-type is unknown")); - } - } - } - if(_type==0xff) return _type; - - DEBUG_SENSOR_LOG(PSTR("MINRF: vector size %u"), MIBLEsensors.size()); - for(uint32_t i=0; i6000){ - DEBUG_SENSOR_LOG(PSTR("MINRF: check for FAKE sensors")); - MINRFpurgeFakeSensors(); - MINRF.timer=0; - } - MINRF.timer++; - - if (!MINRFreceivePacket()){ - - } - - else if(MINRF.buffer.bleAdv.header.uuid==0xfe95){ - MINRF_LOG_BUFFER(MINRF.streamBuffer); - MINRF_LOG_BUFFER(MINRF.lsfrBuffer); - MINRF_LOG_BUFFER(MINRF.buffer.raw); - DEBUG_SENSOR_LOG(PSTR("MINRF: Type: %x"), MINRF.buffer.bleAdv.header.type); - switch(MINRF.buffer.bleAdv.header.type){ - case 0x2050: - DEBUG_SENSOR_LOG(PSTR("MINRF: MJ_HT_V1 Packet")); - break; - case 0x1613:case 0x1614:case 0x1615: - DEBUG_SENSOR_LOG(PSTR("MINRF: Flora Packet")); - break; - default: - DEBUG_SENSOR_LOG(PSTR("MINRF: unknown Packet")); - break; - } - } - else if (MINRF.packetMode == FLORA){ - MINRFhandleFloraPacket(); - } - else if (MINRF.packetMode == MJ_HT_V1){ - MINRFhandleMJ_HT_V1Packet(); - } - else if (MINRF.packetMode == LYWSD02){ - MINRFhandleLYWSD02Packet(); - } - else if (MINRF.packetMode == LYWSD03){ - MINRFhandleLYWSD03Packet(); - } - - - if (MINRF.packetMode == LYWSD03){ - MINRFinitBLE(1); - } - else { - MINRFinitBLE(++MINRF.packetMode); - } - - MINRFhopChannel(); - NRF24radio.startListening(); -} - - - - -const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; -const char HTTP_MINRF_MAC[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; -const char HTTP_MINRF_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%sus/cm{e}"; -const char HTTP_MINRF_HL[] PROGMEM = "{s}
{m}
{e}"; - -void MINRFShow(bool json) -{ - if (json) { - for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { - if(MIBLEsensors.at(i).showedUp < 3){ - DEBUG_SENSOR_LOG(PSTR("MINRF: sensor not fully registered yet")); - break; - } - char slave[33]; - sprintf_P(slave,"%s-%02x%02x%02x",kMINRFSlaveType[MIBLEsensors.at(i).type-1],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]); - char temperature[33]; - dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature); - - ResponseAppend_P(PSTR(",\"%s\":{"),slave); - if(MIBLEsensors.at(i).temp!=-1000.0f){ - ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature); - } - if (MIBLEsensors.at(i).type==FLORA){ - char lux[33]; - char moisture[33]; - char fertility[33]; - dtostrfd((float)MIBLEsensors.at(i).lux, 0, lux); - dtostrfd(MIBLEsensors.at(i).moisture, 0, moisture); - dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility); - if(MIBLEsensors.at(i).lux!=0xffff){ - ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%s"), lux); - } - if(MIBLEsensors.at(i).moisture!=-1000.0f){ - ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%s"), moisture); - } - if(MIBLEsensors.at(i).fertility!=-1000.0f){ - ResponseAppend_P(PSTR(",\"Fertility\":%s"), fertility); - } - } - if (MIBLEsensors.at(i).type>FLORA){ - char humidity[33]; - dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity); - if(MIBLEsensors.at(i).hum!=-1.0f){ - ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity); - } - if(MIBLEsensors.at(i).bat!=0xff){ - ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors.at(i).bat); - } - } - ResponseAppend_P(PSTR("}")); - } -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_NRF24, NRF24type, NRF24.chipType); - for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { - if(MIBLEsensors.at(i).showedUp < 3){ - DEBUG_SENSOR_LOG(PSTR("MINRF: sensor not fully registered yet")); - break; - } - WSContentSend_PD(HTTP_MINRF_HL); - WSContentSend_PD(HTTP_MINRF_MAC, kMINRFSlaveType[MIBLEsensors.at(i).type-1], D_MAC_ADDRESS, MIBLEsensors.at(i).serial[0], MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]); - if(MIBLEsensors.at(i).temp!=-1000.0f){ - char temperature[33]; - dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature); - WSContentSend_PD(HTTP_SNS_TEMP, kMINRFSlaveType[MIBLEsensors.at(i).type-1], temperature, TempUnit()); - } - if (MIBLEsensors.at(i).type==FLORA){ - if(MIBLEsensors.at(i).lux!=0x00ffffff){ - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kMINRFSlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).lux); - } - if(MIBLEsensors.at(i).moisture!=-1000.0f){ - WSContentSend_PD(HTTP_SNS_MOISTURE, kMINRFSlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).moisture); - } - if(MIBLEsensors.at(i).fertility!=-1000.0f){ - char fertility[33]; - dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility); - WSContentSend_PD(HTTP_MINRF_FLORA_DATA, kMINRFSlaveType[MIBLEsensors.at(i).type-1], fertility); - } - } - if (MIBLEsensors.at(i).type>FLORA){ - if(MIBLEsensors.at(i).hum!=-1.0f){ - char humidity[33]; - dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity); - WSContentSend_PD(HTTP_SNS_HUM, kMINRFSlaveType[MIBLEsensors.at(i).type-1], humidity); - } - if(MIBLEsensors.at(i).bat!=0xff){ - WSContentSend_PD(HTTP_BATTERY, kMINRFSlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).bat); - } - } - } -#endif - } -} - - - - - -bool Xsns61(uint8_t function) -{ - bool result = false; - - if (NRF24.chipType) { - switch (function) { - case FUNC_INIT: - MINRFinitBLE(1); - AddLog_P2(LOG_LEVEL_INFO,PSTR("MINRF: started")); - break; - case FUNC_EVERY_50_MSECOND: - MINRF_EVERY_50_MSECOND(); - break; - case FUNC_JSON_APPEND: - MINRFShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - MINRFShow(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_62_MI_HM10.ino" -# 30 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_62_MI_HM10.ino" -#ifdef USE_HM10 - -#define XSNS_62 62 - -#include -#include - -TasmotaSerial *HM10Serial; -#define HM10_BAUDRATE 115200 - -#define HM10_MAX_TASK_NUMBER 12 -uint8_t HM10_TASK_LIST[HM10_MAX_TASK_NUMBER+1][2]; - -#define HM10_MAX_RX_BUF 512 -char HM10_RX_STRING[HM10_MAX_RX_BUF] = {0}; - -struct { - uint8_t current_task_delay; - uint8_t last_command; - uint16_t firmware; - uint32_t period; - uint32_t serialSpeed; - union { - uint32_t time; - uint8_t timebuf[4]; - }; - struct { - uint32_t init:1; - uint32_t pending_task:1; - uint32_t connected:1; - uint32_t subscribed:1; - uint32_t awaitingHT:1; - uint32_t awaitingB:1; - - } mode; - struct { - uint8_t sensor; - - } state; -} HM10; - -#pragma pack(1) -struct { - uint16_t temp; - uint8_t hum; -} LYWSD0x_HT; -#pragma pack(0) - -struct mi_sensor_t{ - uint8_t type; - uint8_t serial[6]; - uint8_t showedUp; - float temp; - union { - struct { - float moisture; - float fertility; - uint16_t lux; - }; - struct { - float hum; - uint8_t bat; - }; - }; -}; - -std::vector MIBLEsensors; - - - - - -#define D_CMND_HM10 "HM10" - -const char S_JSON_HM10_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_HM10 "%s\":%d}"; -const char S_JSON_HM10_COMMAND[] PROGMEM = "{\"" D_CMND_HM10 "%s%s\"}"; -const char kHM10_Commands[] PROGMEM = "Scan|AT|Period|Baud|Time"; - -#define FLORA 1 -#define MJ_HT_V1 2 -#define LYWSD02 3 -#define LYWSD03MMC 4 - -uint8_t kHM10SlaveID[4][3] = { 0xC4,0x7C,0x8D, - 0x58,0x2D,0x34, - 0xE7,0x2E,0x00, - 0xA4,0xC1,0x38, - }; - -const char kHM10SlaveType1[] PROGMEM = "Flora"; -const char kHM10SlaveType2[] PROGMEM = "MJ_HT_V1"; -const char kHM10SlaveType3[] PROGMEM = "LYWSD02"; -const char kHM10SlaveType4[] PROGMEM = "LYWSD03"; -const char * kHM10SlaveType[] PROGMEM = {kHM10SlaveType1,kHM10SlaveType2,kHM10SlaveType3,kHM10SlaveType4}; - - - - - -enum HM10_Commands { - CMND_HM10_DISC_SCAN, - CMND_HM10_AT, - CMND_HM10_PERIOD, - CMND_HM10_BAUD, - CMND_HM10_TIME - }; - - - - - -#define TASK_HM10_NOTASK 0 -#define TASK_HM10_ROLE1 1 -#define TASK_HM10_IMME1 2 -#define TASK_HM10_RENEW 3 -#define TASK_HM10_RESET 4 -#define TASK_HM10_DISC 5 -#define TASK_HM10_CONN 6 -#define TASK_HM10_VERSION 7 -#define TASK_HM10_NAME 8 -#define TASK_HM10_FEEDBACK 9 -#define TASK_HM10_DISCONN 10 -#define TASK_HM10_SUB_L3 11 -#define TASK_HM10_READ_HT 12 -#define TASK_HM10_FINDALLCHARS 13 -#define TASK_HM10_UN_L3 14 -#define TASK_HM10_DELAY_SUB 15 -#define TASK_HM10_READ_BT_L3 16 -#define TASK_HM10_SUB_L2 17 -#define TASK_HM10_UN_L2 18 -#define TASK_HM10_READ_BT_L2 19 -#define TASK_HM10_TIME_L2 20 - -#define TASK_HM10_DONE 99 - - - - - -void HM10_Launchtask(uint8_t task, uint8_t slot, uint8_t delay){ - HM10_TASK_LIST[slot][0] = task; - HM10_TASK_LIST[slot][1] = delay; - HM10_TASK_LIST[slot+1][0] = TASK_HM10_NOTASK; - HM10.current_task_delay = HM10_TASK_LIST[0][1]; -} - -void HM10_TaskReplaceInSlot(uint8_t task, uint8_t slot){ - HM10.last_command = HM10_TASK_LIST[slot][0]; - HM10_TASK_LIST[slot][0] = task; -} - - - - - -void HM10_Reset(void) { HM10_Launchtask(TASK_HM10_DISCONN,0,1); - HM10_Launchtask(TASK_HM10_ROLE1,1,1); - HM10_Launchtask(TASK_HM10_IMME1,2,1); - HM10_Launchtask(TASK_HM10_RESET,3,1); - HM10_Launchtask(TASK_HM10_VERSION,4,10); - HM10_Launchtask(TASK_HM10_DISC,5,50); - } - -void HM10_Discovery_Scan(void) { - HM10_Launchtask(TASK_HM10_DISCONN,0,1); - HM10_Launchtask(TASK_HM10_DISC,1,1); - } - -void HM10_Read_LYWSD03(void) { - HM10_Launchtask(TASK_HM10_CONN,0,1); - HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); - HM10_Launchtask(TASK_HM10_SUB_L3,2,20); - HM10_Launchtask(TASK_HM10_UN_L3,3,80); - HM10_Launchtask(TASK_HM10_READ_BT_L3,4,5); - HM10_Launchtask(TASK_HM10_DISCONN,5,5); - } - -void HM10_Read_LYWSD02(void) { - HM10_Launchtask(TASK_HM10_CONN,0,1); - HM10_Launchtask(TASK_HM10_FEEDBACK,1,35); - HM10_Launchtask(TASK_HM10_SUB_L2,2,20); - HM10_Launchtask(TASK_HM10_UN_L2,3,80); - HM10_Launchtask(TASK_HM10_READ_BT_L2,4,5); - HM10_Launchtask(TASK_HM10_DISCONN,5,5); - } - -void HM10_Time_LYWSD02(void) { - HM10_Launchtask(TASK_HM10_DISCONN,0,0); - HM10_Launchtask(TASK_HM10_CONN,1,5); - HM10_Launchtask(TASK_HM10_FEEDBACK,2,35); - HM10_Launchtask(TASK_HM10_TIME_L2,3,20); - HM10_Launchtask(TASK_HM10_DISCONN,4,5); - } -# 231 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_62_MI_HM10.ino" -uint32_t MIBLEgetSensorSlot(uint8_t (&_serial)[6], uint8_t _type){ - if(_type==0xff){ - DEBUG_SENSOR_LOG(PSTR("MIBLE: will test MAC-type")); - for (uint32_t i=0;i<4;i++){ - if(memcmp(_serial,kHM10SlaveID+i,3)==0){ - DEBUG_SENSOR_LOG(PSTR("MIBLE: MAC is type %u"), i); - _type = i+1; - } - else { - DEBUG_SENSOR_LOG(PSTR("MIBLE: MAC-type is unknown")); - } - } - } - if(_type==0xff) return _type; - - DEBUG_SENSOR_LOG(PSTR("MIBLE: vector size %u"), MIBLEsensors.size()); - for(uint32_t i=0; ibegin(HM10.serialSpeed)) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start serial communication fixed to 115200 baud"),D_CMND_HM10); - if (HM10Serial->hardwareSerial()) { - ClaimSerial(); - DEBUG_SENSOR_LOG(PSTR("HM10: claim HW")); - } - HM10_Reset(); - HM10.mode.pending_task = 1; - HM10.mode.init = 1; - HM10.period = Settings.tele_period; - DEBUG_SENSOR_LOG(PSTR("%s_TASK_LIST initialized, now return to main loop"),D_CMND_HM10); - } - return; -} -# 315 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_62_MI_HM10.ino" -void HM10MACStringToBytes(const char* string, uint8_t _mac[]) { - uint32_t index = 0; - while (index < 12) { - char c = string[index]; - uint32_t value = 0; - if(c >= '0' && c <= '9') - value = (c - '0'); - else if (c >= 'A' && c <= 'F') - value = (10 + (c - 'A')); - _mac[(index/2)] += value << (((index + 1) % 2) * 4); - - index++; - } - DEBUG_SENSOR_LOG(PSTR("HM10: MAC-array: %x%x%x%x%x%x"),_mac[0],_mac[1],_mac[2],_mac[3],_mac[4],_mac[5]); -} - - - - - - -void HM10ParseResponse(char *buf) { - if (!strncmp(buf,"OK",2)) { - DEBUG_SENSOR_LOG(PSTR("HM10: got OK")); - } - if (!strncmp(buf,"HMSoft",6)) { - const char* _fw = "000"; - memcpy((void *)_fw,(void *)(buf+8),3); - HM10.firmware = atoi(_fw); - DEBUG_SENSOR_LOG(PSTR("HM10: Firmware: %d"), HM10.firmware); - return; - } - char * _pos = strstr(buf, "IS0:"); - if(_pos) { - const char* _mac = "000000000000"; - memcpy((void *)_mac,(void *)(_pos+4),12); - DEBUG_SENSOR_LOG(PSTR("HM10: found Mac: %s"), _mac); - uint8_t _newMacArray[6] = {0}; - HM10MACStringToBytes(_mac, _newMacArray); - DEBUG_SENSOR_LOG(PSTR("HM10: MAC-array: %x%x%x%x%x%x"),_newMacArray[0],_newMacArray[1],_newMacArray[2],_newMacArray[3],_newMacArray[4],_newMacArray[5]); - MIBLEgetSensorSlot(_newMacArray, 0xff); - } - if (strstr(buf, "LOST")){ - HM10.mode.connected = false; - } - else { - DEBUG_SENSOR_LOG(PSTR("HM10: empty response")); - } -} - -void HM10readTempHum(char *_buf){ - DEBUG_SENSOR_LOG(PSTR("HM10: raw data: %x%x%x%x%x%x%x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); - if(_buf[0] != 0 && _buf[1] != 0){ - memcpy(&LYWSD0x_HT,(void *)_buf,3); - DEBUG_SENSOR_LOG(PSTR("HM10: Temperature * 100: %u, Humidity: %u"),LYWSD0x_HT.temp,LYWSD0x_HT.hum); - uint32_t _slot = HM10.state.sensor; - - DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); - static float _tempFloat; - _tempFloat=(float)(LYWSD0x_HT.temp)/100.0f; - if(_tempFloat<60){ - MIBLEsensors.at(_slot).temp=_tempFloat; - HM10.mode.awaitingHT = false; - HM10.current_task_delay = 0; - } - _tempFloat=(float)LYWSD0x_HT.hum; - if(_tempFloat<100){ - MIBLEsensors.at(_slot).hum = _tempFloat; - DEBUG_SENSOR_LOG(PSTR("LYWSD03: hum updated")); - } - } -} - -bool HM10readBat(char *_buf){ - DEBUG_SENSOR_LOG(PSTR("HM10: raw data: %x%x%x%x%x%x%x"),_buf[0],_buf[1],_buf[2],_buf[3],_buf[4],_buf[5],_buf[6]); - if(_buf[0] != 0){ - DEBUG_SENSOR_LOG(PSTR("HM10: Battery: %u"),_buf[0]); - uint32_t _slot = HM10.state.sensor; - DEBUG_SENSOR_LOG(PSTR("MIBLE: Sensor slot: %u"), _slot); - if(_buf[0]<101){ - MIBLEsensors.at(_slot).bat=_buf[0]; - return true; - } - } - return false; -} - - - - - -bool HM10SerialHandleFeedback(){ - bool success = false; - uint32_t i = 0; - char ret[HM10_MAX_RX_BUF] = {0}; - - - while(HM10Serial->available()) { - - if(iread(); - } - i++; - success = true; - } - if(HM10.mode.awaitingHT) { - if (HM10.mode.connected) HM10readTempHum(ret); - } - else if(HM10.mode.awaitingB) { - if (HM10.mode.connected) { - if (HM10readBat(ret)){ - HM10.mode.awaitingB = false; - HM10.current_task_delay = 0; - } - } - } - else if(success) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s response: %s"),D_CMND_HM10, (char *)ret); - HM10ParseResponse(ret); - } - else { - - } - return success; -} - - - - - -void HM10_TaskEvery100ms(){ - if (HM10.current_task_delay == 0) { - uint8_t i = 0; - bool runningTaskLoop = true; - while (runningTaskLoop) { - switch(HM10_TASK_LIST[i][0]) { - case TASK_HM10_ROLE1: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s set role to 1"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+ROLE1"); - break; - case TASK_HM10_IMME1: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s set imme to 1"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+IMME1"); - break; - case TASK_HM10_DISC: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start discovery"),D_CMND_HM10); - HM10.current_task_delay = 35; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+DISC?"); - break; - case TASK_HM10_VERSION: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read version"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+VERR?"); - break; - case TASK_HM10_NAME: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read name"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+NAME?"); - break; - case TASK_HM10_CONN: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s connect"),D_CMND_HM10); - HM10.current_task_delay = 2; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - char _con[20]; - sprintf_P(_con,"AT+CON%02x%02x%02x%02x%02x%02x",MIBLEsensors.at(HM10.state.sensor).serial[0],MIBLEsensors.at(HM10.state.sensor).serial[1],MIBLEsensors.at(HM10.state.sensor).serial[2],MIBLEsensors.at(HM10.state.sensor).serial[3],MIBLEsensors.at(HM10.state.sensor).serial[4],MIBLEsensors.at(HM10.state.sensor).serial[5]); - HM10Serial->write(_con); - HM10.mode.awaitingB = false; - HM10.mode.connected = true; - break; - case TASK_HM10_DISCONN: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s disconnect"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT"); - break; - case TASK_HM10_RESET: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s Reset Device"),D_CMND_HM10); - HM10Serial->write("AT+RESET"); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - break; - case TASK_HM10_SUB_L3: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s subscribe"),D_CMND_HM10); - HM10.current_task_delay = 25; - HM10_TaskReplaceInSlot(TASK_HM10_DELAY_SUB,i); - runningTaskLoop = false; - HM10Serial->write("AT+NOTIFY_ON0037"); - break; - case TASK_HM10_UN_L3: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s un-subscribe"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10.mode.awaitingHT = false; - HM10Serial->write("AT+NOTIFYOFF0037"); - break; - case TASK_HM10_SUB_L2: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s subscribe"),D_CMND_HM10); - HM10.current_task_delay = 25; - HM10_TaskReplaceInSlot(TASK_HM10_DELAY_SUB,i); - runningTaskLoop = false; - HM10Serial->write("AT+NOTIFY_ON003C"); - break; - case TASK_HM10_UN_L2: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s un-subscribe"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10.mode.awaitingHT = false; - HM10Serial->write("AT+NOTIFYOFF003C"); - break; - case TASK_HM10_TIME_L2: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s set time"),D_CMND_HM10); - HM10.current_task_delay = 5; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10.time = Rtc.utc_time; - HM10Serial->write("AT+SEND_DATAWR002F"); - HM10Serial->write(HM10.timebuf,4); - HM10Serial->write(Rtc.time_timezone / 60); - AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s Time-string: %x%x%x%x%x"),D_CMND_HM10, HM10.timebuf[0],HM10.timebuf[1],HM10.timebuf[2],HM10.timebuf[3],(Rtc.time_timezone /60)); - break; - case TASK_HM10_READ_HT: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read handle 0036"),D_CMND_HM10); - HM10.current_task_delay = 0; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+READDATA0036?"); - HM10.mode.awaitingHT = true; - break; - case TASK_HM10_READ_BT_L3: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read handle 003A"),D_CMND_HM10); - HM10.current_task_delay = 2; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+READDATA003A?"); - HM10.mode.awaitingB = true; - break; - case TASK_HM10_READ_BT_L2: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s read handle 0043"),D_CMND_HM10); - HM10.current_task_delay = 2; - HM10_TaskReplaceInSlot(TASK_HM10_FEEDBACK,i); - runningTaskLoop = false; - HM10Serial->write("AT+READDATA0043?"); - HM10.mode.awaitingB = true; - break; - - - - - - - - case TASK_HM10_FEEDBACK: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s get response"),D_CMND_HM10); - HM10SerialHandleFeedback(); - HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; - HM10_TASK_LIST[i][0] = TASK_HM10_DONE; - runningTaskLoop = false; - break; - case TASK_HM10_DELAY_SUB: - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("%s start reading"),D_CMND_HM10); - HM10SerialHandleFeedback(); - HM10.current_task_delay = HM10_TASK_LIST[i+1][1];; - HM10_TASK_LIST[i][0] = TASK_HM10_DONE; - HM10.mode.awaitingHT = true; - runningTaskLoop = false; - break; - case TASK_HM10_DONE: - - - if(HM10_TASK_LIST[i+1][0] == TASK_HM10_NOTASK) { - DEBUG_SENSOR_LOG(PSTR("%sno Tasks left"),D_CMND_HM10); - DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK_DONE current slot %u"),D_CMND_HM10, i); - for (uint8_t j = 0; j < HM10_MAX_TASK_NUMBER+1; j++) { - DEBUG_SENSOR_LOG(PSTR("%sHM10_TASK cleanup slot %u"),D_CMND_HM10, j); - HM10_TASK_LIST[j][0] = TASK_HM10_NOTASK; - HM10_TASK_LIST[j][1] = 0; - } - runningTaskLoop = false; - HM10.mode.pending_task = 0; - break; - } - } - i++; - } - } - else { - HM10.current_task_delay--; - } -} - - - - - - -void HM10EverySecond(){ - if(HM10.firmware == 0) return; - if(HM10.mode.pending_task == 1) return; - if (MIBLEsensors.size()==0) return; - - static uint32_t _counter = 0; - static uint32_t _nextSensorSlot = 0; - if(_counter==0) { - HM10.state.sensor = _nextSensorSlot; - _nextSensorSlot++; - if(MIBLEsensors.at(HM10.state.sensor).type==LYWSD03MMC) { - HM10.mode.pending_task = 1; - HM10_Read_LYWSD03(); - } - if(MIBLEsensors.at(HM10.state.sensor).type==LYWSD02) { - HM10.mode.pending_task = 1; - HM10_Read_LYWSD02(); - } - if (HM10.state.sensor==MIBLEsensors.size()-1) { - _nextSensorSlot= 0; - _counter++; - } - DEBUG_SENSOR_LOG(PSTR("%s active sensor now: %u"),D_CMND_HM10, HM10.state.sensor); - } - else _counter++; - if (_counter>HM10.period) _counter = 0; -} - -bool HM10Cmd(void) { - char command[CMDSZ]; - bool serviced = true; - uint8_t disp_len = strlen(D_CMND_HM10); - - if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_HM10), disp_len)) { - uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + disp_len, kHM10_Commands); - switch (command_code) { - case CMND_HM10_PERIOD: - if (XdrvMailbox.data_len > 0) { - if (command_code == CMND_HM10_PERIOD) { HM10.period = XdrvMailbox.payload; } - } - else { - if (command_code == CMND_HM10_PERIOD) XdrvMailbox.payload = HM10.period; - } - Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); - break; - case CMND_HM10_BAUD: - if (XdrvMailbox.data_len > 0) { - if (command_code == CMND_HM10_BAUD) { - HM10.serialSpeed = XdrvMailbox.payload; - HM10Serial->begin(HM10.serialSpeed); - } - } - else { - if (command_code == CMND_HM10_BAUD) XdrvMailbox.payload = HM10.serialSpeed; - } - Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); - break; - case CMND_HM10_TIME: - if (XdrvMailbox.data_len > 0) { - if(MIBLEsensors.size()>XdrvMailbox.payload){ - if(MIBLEsensors.at(XdrvMailbox.payload).type == LYWSD02){ - HM10.state.sensor = XdrvMailbox.payload; - HM10_Time_LYWSD02(); - } - } - } - Response_P(S_JSON_HM10_COMMAND_NVALUE, command, XdrvMailbox.payload); - break; - case CMND_HM10_AT: - HM10Serial->write("AT"); - if (strlen(XdrvMailbox.data)!=0) { - HM10Serial->write("+"); - HM10Serial->write(XdrvMailbox.data); - Response_P(S_JSON_HM10_COMMAND, ":AT+",XdrvMailbox.data); - } - else Response_P(S_JSON_HM10_COMMAND, ":AT",XdrvMailbox.data); - break; - case CMND_HM10_DISC_SCAN: - if (command_code == CMND_HM10_DISC_SCAN) { HM10_Discovery_Scan(); } - Response_P(S_JSON_HM10_COMMAND, command, ""); - break; - default: - - serviced = false; - break; - } - } else { - return false; - } - return serviced; -} - - - - - - -const char HTTP_HM10[] PROGMEM = "{s}HM10" " Firmware " "{m}%u{e}"; -const char HTTP_HM10_SERIAL[] PROGMEM = "{s}%s %s{m}%02x:%02x:%02x:%02x:%02x:%02x%{e}"; -const char HTTP_BATTERY[] PROGMEM = "{s}%s" " Battery" "{m}%u%%{e}"; -const char HTTP_HM10_FLORA_DATA[] PROGMEM = "{s}%s" " Fertility" "{m}%sus/cm{e}"; -const char HTTP_HM10_HL[] PROGMEM = "{s}
{m}
{e}"; - -void HM10Show(bool json) -{ - if (json) { - for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { - char slave[33]; - sprintf_P(slave,"%s-%02x%02x%02x",kHM10SlaveType[MIBLEsensors.at(i).type-1],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]); - char temperature[33]; - dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature); - - ResponseAppend_P(PSTR(",\"%s\":{"),slave); - if(MIBLEsensors.at(i).temp!=-1000.0f){ - ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%s"), temperature); - } - if (MIBLEsensors.at(i).type==FLORA){ - char lux[33]; - char moisture[33]; - char fertility[33]; - dtostrfd((float)MIBLEsensors.at(i).lux, 0, lux); - dtostrfd(MIBLEsensors.at(i).moisture, 0, moisture); - dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility); - if(MIBLEsensors.at(i).lux!=0xffff){ - ResponseAppend_P(PSTR(",\"" D_JSON_ILLUMINANCE "\":%s"), lux); - } - if(MIBLEsensors.at(i).moisture!=-1000.0f){ - ResponseAppend_P(PSTR(",\"" D_JSON_MOISTURE "\":%s"), moisture); - } - if(MIBLEsensors.at(i).fertility!=-1000.0f){ - ResponseAppend_P(PSTR(",\"Fertility\":%s"), fertility); - } - } - if (MIBLEsensors.at(i).type>FLORA){ - char humidity[33]; - dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity); - if(MIBLEsensors.at(i).hum!=-1.0f){ - ResponseAppend_P(PSTR(",\"" D_JSON_HUMIDITY "\":%s"), humidity); - } - if(MIBLEsensors.at(i).bat!=0xff){ - ResponseAppend_P(PSTR(",\"Battery\":%u"), MIBLEsensors.at(i).bat); - } - } - ResponseAppend_P(PSTR("}")); - } -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_HM10, HM10.firmware); - for (uint32_t i = 0; i < MIBLEsensors.size(); i++) { - WSContentSend_PD(HTTP_HM10_HL); - WSContentSend_PD(HTTP_HM10_SERIAL, kHM10SlaveType[MIBLEsensors.at(i).type-1], D_MAC_ADDRESS, MIBLEsensors.at(i).serial[0], MIBLEsensors.at(i).serial[1],MIBLEsensors.at(i).serial[2],MIBLEsensors.at(i).serial[3],MIBLEsensors.at(i).serial[4],MIBLEsensors.at(i).serial[5]); - if(MIBLEsensors.at(i).temp!=-1000.0f){ - char temperature[33]; - dtostrfd(MIBLEsensors.at(i).temp, Settings.flag2.temperature_resolution, temperature); - WSContentSend_PD(HTTP_SNS_TEMP, kHM10SlaveType[MIBLEsensors.at(i).type-1], temperature, TempUnit()); - } - if (MIBLEsensors.at(i).type==FLORA){ - if(MIBLEsensors.at(i).lux!=0xffff){ - WSContentSend_PD(HTTP_SNS_ILLUMINANCE, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).lux); - } - if(MIBLEsensors.at(i).moisture!=-1000.0f){ - WSContentSend_PD(HTTP_SNS_MOISTURE, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).moisture); - } - if(MIBLEsensors.at(i).fertility!=-1000.0f){ - char fertility[33]; - dtostrfd(MIBLEsensors.at(i).fertility, 0, fertility); - WSContentSend_PD(HTTP_HM10_FLORA_DATA, kHM10SlaveType[MIBLEsensors.at(i).type-1], fertility); - } - } - if (MIBLEsensors.at(i).type>FLORA){ - if(MIBLEsensors.at(i).hum!=-1.0f){ - char humidity[33]; - dtostrfd(MIBLEsensors.at(i).hum, Settings.flag2.humidity_resolution, humidity); - WSContentSend_PD(HTTP_SNS_HUM, kHM10SlaveType[MIBLEsensors.at(i).type-1], humidity); - } - if(MIBLEsensors.at(i).bat!=0xff){ - WSContentSend_PD(HTTP_BATTERY, kHM10SlaveType[MIBLEsensors.at(i).type-1], MIBLEsensors.at(i).bat); - } - } - } -#endif - } -} - - - - - -bool Xsns62(uint8_t function) -{ - bool result = false; - - if ((pin[GPIO_HM10_RX] < 99) && (pin[GPIO_HM10_TX] < 99)) { - switch (function) { - case FUNC_INIT: - HM10SerialInit(); - break; - case FUNC_EVERY_50_MSECOND: - HM10SerialHandleFeedback(); - break; - case FUNC_EVERY_100_MSECOND: - if (HM10_TASK_LIST[0][0] != TASK_HM10_NOTASK) { - HM10_TaskEvery100ms(); - } - break; - case FUNC_EVERY_SECOND: - HM10EverySecond(); - break; - case FUNC_COMMAND: - result = HM10Cmd(); - break; - case FUNC_JSON_APPEND: - HM10Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - HM10Show(0); - break; -#endif - } - } - return result; -} -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_64_aht10.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_64_aht10.ino" -#ifdef USE_I2C -#ifdef USE_AHT10 - - - - - - -#define XSNS_64 64 -#define XI2C_43 43 - -#define AHT10_ADDR 0x38 - -unsigned char eSensorCalibrateCmd[3] = {0xE1, 0x08, 0x00}; -unsigned char eSensorNormalCmd[3] = {0xA8, 0x00, 0x00}; -unsigned char eSensorMeasureCmd[3] = {0xAC, 0x33, 0x00}; -unsigned char eSensorResetCmd = 0xBA; - -struct AHT10 { - float humidity = NAN; - float temperature = NAN; - uint8_t valid = 0; - uint8_t count = 0; - char name[6] = "AHT10"; -} AHT10; - -bool begin() -{ - Wire.begin(AHT10_ADDR); - Wire.beginTransmission(AHT10_ADDR); - Wire.write(eSensorCalibrateCmd, 3); - Wire.endTransmission(); - delay(500); - - if((readStatus() & 0x68) == 0x08) - return true; - else - { - return false; - } -} - -bool AHT10Read(void) -{ - unsigned long result, temp[6]; - - if (AHT10.valid) { AHT10.valid--; } - - Wire.beginTransmission(AHT10_ADDR); - Wire.write(eSensorMeasureCmd, 3); - Wire.endTransmission(); - delay(100); - - Wire.requestFrom(AHT10_ADDR, 6); - for(unsigned char i = 0; Wire.available() > 0; i++) - { - temp[i] = Wire.read(); - } - - AHT10.humidity = (((temp[1] << 16) | (temp[2] << 8) | temp[3]) >> 4)* 100 / 1048576; - AHT10.temperature = ((200 * (((temp[3] & 0x0F) << 16) | (temp[4] << 8) | temp[5])) / 1048576) - 50; - - if (isnan(AHT10.temperature) || isnan(AHT10.humidity)) { return false; } - - AHT10.valid = SENSOR_MAX_MISS; - return true; -} - - -unsigned char readStatus(void) -{ - unsigned char result = 0; - - Wire.requestFrom(AHT10_ADDR, 1); - result = Wire.read(); - return result; -} - -void AHT10Detect(void) -{ - if (I2cActive(AHT10_ADDR)) - { - return; - } - - if (begin()) - { - I2cSetActiveFound(AHT10_ADDR, AHT10.name); - AHT10.count = 1; - } -} - -void AHT10EverySecond(void) -{ - if (uptime &1) { - - if (!AHT10Read()) { - AddLogMissed(AHT10.name, AHT10.valid); - } - } -} - -void AHT10Show(bool json) -{ - if (AHT10.valid) { - char temperature[33]; - dtostrfd(AHT10.temperature, Settings.flag2.temperature_resolution, temperature); - char humidity[33]; - dtostrfd(AHT10.humidity, Settings.flag2.humidity_resolution, humidity); - - if (json) { - ResponseAppend_P(JSON_SNS_TEMPHUM, AHT10.name, temperature, humidity); -#ifdef USE_DOMOTICZ - if ((0 == tele_period)) { - DomoticzTempHumSensor(temperature, humidity); - } -#endif -#ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, AHT10.temperature); - KnxSensor(KNX_HUMIDITY, AHT10.humidity); - } -#endif -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_TEMP, AHT10.name, temperature, TempUnit()); - WSContentSend_PD(HTTP_SNS_HUM, AHT10.name, humidity); -#endif - } - } -} - - - - - -bool Xsns64(uint8_t function) -{ - if (!I2cEnabled(XI2C_43)) { return false; } - - bool result = false; - - if (FUNC_INIT == function) { - AHT10Detect(); - } - else if (AHT10.count) { - switch (function) { - case FUNC_EVERY_SECOND: - AHT10EverySecond(); - break; - case FUNC_JSON_APPEND: - AHT10Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - AHT10Show(0); - break; -#endif - } - } - return result; -} - -#endif -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_91_prometheus.ino" -# 22 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_91_prometheus.ino" -#ifdef USE_PROMETHEUS - - - - -#define XSNS_91 91 - -void HandleMetrics(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - - AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, PSTR("Prometheus")); - - WSContentBegin(200, CT_PLAIN); - - - char parameter[FLOATSZ]; - - if (global_temperature != 9999) { - dtostrfd(global_temperature, Settings.flag2.temperature_resolution, parameter); - WSContentSend_P(PSTR("# TYPE global_temperature gauge\nglobal_temperature %s\n"), parameter); - } - if (global_humidity != 0) { - dtostrfd(global_humidity, Settings.flag2.humidity_resolution, parameter); - WSContentSend_P(PSTR("# TYPE global_humidity gauge\nglobal_humidity %s\n"), parameter); - } - if (global_pressure != 0) { - dtostrfd(global_pressure, Settings.flag2.pressure_resolution, parameter); - WSContentSend_P(PSTR("# TYPE global_pressure gauge\nglobal_pressure %s\n"), parameter); - } - -#ifdef USE_ENERGY_SENSOR - dtostrfd(Energy.voltage[0], Settings.flag2.voltage_resolution, parameter); - WSContentSend_P(PSTR("# TYPE voltage gauge\nvoltage %s\n"), parameter); - dtostrfd(Energy.current[0], Settings.flag2.current_resolution, parameter); - WSContentSend_P(PSTR("# TYPE current gauge\ncurrent %s\n"), parameter); - dtostrfd(Energy.active_power[0], Settings.flag2.wattage_resolution, parameter); - WSContentSend_P(PSTR("# TYPE active_power gauge\nactive_power %s\n"), parameter); - dtostrfd(Energy.daily, Settings.flag2.energy_resolution, parameter); - WSContentSend_P(PSTR("# TYPE energy_daily gauge\nenergy_daily %s\n"), parameter); - dtostrfd(Energy.total, Settings.flag2.energy_resolution, parameter); - WSContentSend_P(PSTR("# TYPE energy_total counter\nenergy_total %s\n"), parameter); -#endif -# 80 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_91_prometheus.ino" - WSContentEnd(); -} - - - - - -bool Xsns91(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_WEB_ADD_HANDLER: - WebServer->on("/metrics", HandleMetrics); - break; - } - return result; -} - -#endif -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_interface.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xsns_interface.ino" -#ifdef XFUNC_PTR_IN_ROM -bool (* const xsns_func_ptr[])(uint8_t) PROGMEM = { -#else -bool (* const xsns_func_ptr[])(uint8_t) = { -#endif - -#ifdef XSNS_01 - &Xsns01, -#endif - -#ifdef XSNS_02 - &Xsns02, -#endif - -#ifdef XSNS_03 - &Xsns03, -#endif - -#ifdef XSNS_04 - &Xsns04, -#endif - -#ifdef XSNS_05 - &Xsns05, -#endif - -#ifdef XSNS_06 - &Xsns06, -#endif - -#ifdef XSNS_07 - &Xsns07, -#endif - -#ifdef XSNS_08 - &Xsns08, -#endif - -#ifdef XSNS_09 - &Xsns09, -#endif - -#ifdef XSNS_10 - &Xsns10, -#endif - -#ifdef XSNS_11 - &Xsns11, -#endif - -#ifdef XSNS_12 - &Xsns12, -#endif - -#ifdef XSNS_13 - &Xsns13, -#endif - -#ifdef XSNS_14 - &Xsns14, -#endif - -#ifdef XSNS_15 - &Xsns15, -#endif - -#ifdef XSNS_16 - &Xsns16, -#endif - -#ifdef XSNS_17 - &Xsns17, -#endif - -#ifdef XSNS_18 - &Xsns18, -#endif - -#ifdef XSNS_19 - &Xsns19, -#endif - -#ifdef XSNS_20 - &Xsns20, -#endif - -#ifdef XSNS_21 - &Xsns21, -#endif - -#ifdef XSNS_22 - &Xsns22, -#endif - -#ifdef XSNS_23 - &Xsns23, -#endif - -#ifdef XSNS_24 - &Xsns24, -#endif - -#ifdef XSNS_25 - &Xsns25, -#endif - -#ifdef XSNS_26 - &Xsns26, -#endif - -#ifdef XSNS_27 - &Xsns27, -#endif - -#ifdef XSNS_28 - &Xsns28, -#endif - -#ifdef XSNS_29 - &Xsns29, -#endif - -#ifdef XSNS_30 - &Xsns30, -#endif - -#ifdef XSNS_31 - &Xsns31, -#endif - -#ifdef XSNS_32 - &Xsns32, -#endif - -#ifdef XSNS_33 - &Xsns33, -#endif - -#ifdef XSNS_34 - &Xsns34, -#endif - -#ifdef XSNS_35 - &Xsns35, -#endif - -#ifdef XSNS_36 - &Xsns36, -#endif - -#ifdef XSNS_37 - &Xsns37, -#endif - -#ifdef XSNS_38 - &Xsns38, -#endif - -#ifdef XSNS_39 - &Xsns39, -#endif - -#ifdef XSNS_40 - &Xsns40, -#endif - -#ifdef XSNS_41 - &Xsns41, -#endif - -#ifdef XSNS_42 - &Xsns42, -#endif - -#ifdef XSNS_43 - &Xsns43, -#endif - -#ifdef XSNS_44 - &Xsns44, -#endif - -#ifdef XSNS_45 - &Xsns45, -#endif - -#ifdef XSNS_46 - &Xsns46, -#endif - -#ifdef XSNS_47 - &Xsns47, -#endif - -#ifdef XSNS_48 - &Xsns48, -#endif - -#ifdef XSNS_49 - &Xsns49, -#endif - -#ifdef XSNS_50 - &Xsns50, -#endif - -#ifdef XSNS_51 - &Xsns51, -#endif - -#ifdef XSNS_52 - &Xsns52, -#endif - -#ifdef XSNS_53 - &Xsns53, -#endif - -#ifdef XSNS_54 - &Xsns54, -#endif - -#ifdef XSNS_55 - &Xsns55, -#endif - -#ifdef XSNS_56 - &Xsns56, -#endif - -#ifdef XSNS_57 - &Xsns57, -#endif - -#ifdef XSNS_58 - &Xsns58, -#endif - -#ifdef XSNS_59 - &Xsns59, -#endif - -#ifdef XSNS_60 - &Xsns60, -#endif - -#ifdef XSNS_61 - &Xsns61, -#endif - -#ifdef XSNS_62 - &Xsns62, -#endif - -#ifdef XSNS_63 - &Xsns63, -#endif - -#ifdef XSNS_64 - &Xsns64, -#endif - -#ifdef XSNS_65 - &Xsns65, -#endif - -#ifdef XSNS_66 - &Xsns66, -#endif - -#ifdef XSNS_67 - &Xsns67, -#endif - -#ifdef XSNS_68 - &Xsns68, -#endif - -#ifdef XSNS_69 - &Xsns69, -#endif - -#ifdef XSNS_70 - &Xsns70, -#endif - -#ifdef XSNS_71 - &Xsns71, -#endif - -#ifdef XSNS_72 - &Xsns72, -#endif - -#ifdef XSNS_73 - &Xsns73, -#endif - -#ifdef XSNS_74 - &Xsns74, -#endif - -#ifdef XSNS_75 - &Xsns75, -#endif - -#ifdef XSNS_76 - &Xsns76, -#endif - -#ifdef XSNS_77 - &Xsns77, -#endif - -#ifdef XSNS_78 - &Xsns78, -#endif - -#ifdef XSNS_79 - &Xsns79, -#endif - -#ifdef XSNS_80 - &Xsns80, -#endif - -#ifdef XSNS_81 - &Xsns81, -#endif - -#ifdef XSNS_82 - &Xsns82, -#endif - -#ifdef XSNS_83 - &Xsns83, -#endif - -#ifdef XSNS_84 - &Xsns84, -#endif - -#ifdef XSNS_85 - &Xsns85, -#endif - -#ifdef XSNS_86 - &Xsns86, -#endif - -#ifdef XSNS_87 - &Xsns87, -#endif - -#ifdef XSNS_88 - &Xsns88, -#endif - -#ifdef XSNS_89 - &Xsns89, -#endif - -#ifdef XSNS_90 - &Xsns90, -#endif - -#ifdef XSNS_91 - &Xsns91, -#endif - -#ifdef XSNS_92 - &Xsns92, -#endif - -#ifdef XSNS_93 - &Xsns93, -#endif - -#ifdef XSNS_94 - &Xsns94, -#endif - -#ifdef XSNS_95 - &Xsns95, -#endif - -#ifdef XSNS_96 - &Xsns96, -#endif - -#ifdef XSNS_97 - &Xsns97, -#endif - -#ifdef XSNS_98 - &Xsns98, -#endif - -#ifdef XSNS_99 - &Xsns99 -#endif -}; - -const uint8_t xsns_present = sizeof(xsns_func_ptr) / sizeof(xsns_func_ptr[0]); - - - - - -#ifdef XFUNC_PTR_IN_ROM -const uint8_t kXsnsList[] PROGMEM = { -#else -const uint8_t kXsnsList[] = { -#endif - -#ifdef XSNS_01 - XSNS_01, -#endif - -#ifdef XSNS_02 - XSNS_02, -#endif - -#ifdef XSNS_03 - XSNS_03, -#endif - -#ifdef XSNS_04 - XSNS_04, -#endif - -#ifdef XSNS_05 - XSNS_05, -#endif - -#ifdef XSNS_06 - XSNS_06, -#endif - -#ifdef XSNS_07 - XSNS_07, -#endif - -#ifdef XSNS_08 - XSNS_08, -#endif - -#ifdef XSNS_09 - XSNS_09, -#endif - -#ifdef XSNS_10 - XSNS_10, -#endif - -#ifdef XSNS_11 - XSNS_11, -#endif - -#ifdef XSNS_12 - XSNS_12, -#endif - -#ifdef XSNS_13 - XSNS_13, -#endif - -#ifdef XSNS_14 - XSNS_14, -#endif - -#ifdef XSNS_15 - XSNS_15, -#endif - -#ifdef XSNS_16 - XSNS_16, -#endif - -#ifdef XSNS_17 - XSNS_17, -#endif - -#ifdef XSNS_18 - XSNS_18, -#endif - -#ifdef XSNS_19 - XSNS_19, -#endif - -#ifdef XSNS_20 - XSNS_20, -#endif - -#ifdef XSNS_21 - XSNS_21, -#endif - -#ifdef XSNS_22 - XSNS_22, -#endif - -#ifdef XSNS_23 - XSNS_23, -#endif - -#ifdef XSNS_24 - XSNS_24, -#endif - -#ifdef XSNS_25 - XSNS_25, -#endif - -#ifdef XSNS_26 - XSNS_26, -#endif - -#ifdef XSNS_27 - XSNS_27, -#endif - -#ifdef XSNS_28 - XSNS_28, -#endif - -#ifdef XSNS_29 - XSNS_29, -#endif - -#ifdef XSNS_30 - XSNS_30, -#endif - -#ifdef XSNS_31 - XSNS_31, -#endif - -#ifdef XSNS_32 - XSNS_32, -#endif - -#ifdef XSNS_33 - XSNS_33, -#endif - -#ifdef XSNS_34 - XSNS_34, -#endif - -#ifdef XSNS_35 - XSNS_35, -#endif - -#ifdef XSNS_36 - XSNS_36, -#endif - -#ifdef XSNS_37 - XSNS_37, -#endif - -#ifdef XSNS_38 - XSNS_38, -#endif - -#ifdef XSNS_39 - XSNS_39, -#endif - -#ifdef XSNS_40 - XSNS_40, -#endif - -#ifdef XSNS_41 - XSNS_41, -#endif - -#ifdef XSNS_42 - XSNS_42, -#endif - -#ifdef XSNS_43 - XSNS_43, -#endif - -#ifdef XSNS_44 - XSNS_44, -#endif - -#ifdef XSNS_45 - XSNS_45, -#endif - -#ifdef XSNS_46 - XSNS_46, -#endif - -#ifdef XSNS_47 - XSNS_47, -#endif - -#ifdef XSNS_48 - XSNS_48, -#endif - -#ifdef XSNS_49 - XSNS_49, -#endif - -#ifdef XSNS_50 - XSNS_50, -#endif - -#ifdef XSNS_51 - XSNS_51, -#endif - -#ifdef XSNS_52 - XSNS_52, -#endif - -#ifdef XSNS_53 - XSNS_53, -#endif - -#ifdef XSNS_54 - XSNS_54, -#endif - -#ifdef XSNS_55 - XSNS_55, -#endif - -#ifdef XSNS_56 - XSNS_56, -#endif - -#ifdef XSNS_57 - XSNS_57, -#endif - -#ifdef XSNS_58 - XSNS_58, -#endif - -#ifdef XSNS_59 - XSNS_59, -#endif - -#ifdef XSNS_60 - XSNS_60, -#endif - -#ifdef XSNS_61 - XSNS_61, -#endif - -#ifdef XSNS_62 - XSNS_62, -#endif - -#ifdef XSNS_63 - XSNS_63, -#endif - -#ifdef XSNS_64 - XSNS_64, -#endif - -#ifdef XSNS_65 - XSNS_65, -#endif - -#ifdef XSNS_66 - XSNS_66, -#endif - -#ifdef XSNS_67 - XSNS_67, -#endif - -#ifdef XSNS_68 - XSNS_68, -#endif - -#ifdef XSNS_69 - XSNS_69, -#endif - -#ifdef XSNS_70 - XSNS_70, -#endif - -#ifdef XSNS_71 - XSNS_71, -#endif - -#ifdef XSNS_72 - XSNS_72, -#endif - -#ifdef XSNS_73 - XSNS_73, -#endif - -#ifdef XSNS_74 - XSNS_74, -#endif - -#ifdef XSNS_75 - XSNS_75, -#endif - -#ifdef XSNS_76 - XSNS_76, -#endif - -#ifdef XSNS_77 - XSNS_77, -#endif - -#ifdef XSNS_78 - XSNS_78, -#endif - -#ifdef XSNS_79 - XSNS_79, -#endif - -#ifdef XSNS_80 - XSNS_80, -#endif - -#ifdef XSNS_81 - XSNS_81, -#endif - -#ifdef XSNS_82 - XSNS_82, -#endif - -#ifdef XSNS_83 - XSNS_83, -#endif - -#ifdef XSNS_84 - XSNS_84, -#endif - -#ifdef XSNS_85 - XSNS_85, -#endif - -#ifdef XSNS_86 - XSNS_86, -#endif - -#ifdef XSNS_87 - XSNS_87, -#endif - -#ifdef XSNS_88 - XSNS_88, -#endif - -#ifdef XSNS_89 - XSNS_89, -#endif - -#ifdef XSNS_90 - XSNS_90, -#endif - -#ifdef XSNS_91 - XSNS_91, -#endif - -#ifdef XSNS_92 - XSNS_92, -#endif - -#ifdef XSNS_93 - XSNS_93, -#endif - -#ifdef XSNS_94 - XSNS_94, -#endif - -#ifdef XSNS_95 - XSNS_95, -#endif - -#ifdef XSNS_96 - XSNS_96, -#endif - -#ifdef XSNS_97 - XSNS_97, -#endif - -#ifdef XSNS_98 - XSNS_98, -#endif - -#ifdef XSNS_99 - XSNS_99 -#endif -}; - - - -bool XsnsEnabled(uint32_t sns_index) -{ - if (sns_index < sizeof(kXsnsList)) { -#ifdef XFUNC_PTR_IN_ROM - uint32_t index = pgm_read_byte(kXsnsList + sns_index); -#else - uint32_t index = kXsnsList[sns_index]; -#endif - return bitRead(Settings.sensors[index / 32], index % 32); - } - return true; -} - -void XsnsSensorState(void) -{ - ResponseAppend_P(PSTR("\"")); - for (uint32_t i = 0; i < sizeof(kXsnsList); i++) { -#ifdef XFUNC_PTR_IN_ROM - uint32_t sensorid = pgm_read_byte(kXsnsList + i); -#else - uint32_t sensorid = kXsnsList[i]; -#endif - bool disabled = false; - if (sensorid < MAX_XSNS_DRIVERS) { - disabled = !bitRead(Settings.sensors[sensorid / 32], sensorid % 32); - } - ResponseAppend_P(PSTR("%s%s%d"), (i) ? "," : "", (disabled) ? "!" : "", sensorid); - } - ResponseAppend_P(PSTR("\"")); -} - - - - - -bool XsnsNextCall(uint8_t Function, uint8_t &xsns_index) -{ - xsns_index++; - if (xsns_index == xsns_present) { xsns_index = 0; } - -#ifndef USE_DEBUG_DRIVER - if (FUNC_WEB_SENSOR == Function) { -#endif - uint32_t max_disabled = xsns_present; - while (!XsnsEnabled(xsns_index) && max_disabled--) { - xsns_index++; - if (xsns_index == xsns_present) { xsns_index = 0; } - } -#ifndef USE_DEBUG_DRIVER - } -#endif - - return xsns_func_ptr[xsns_index](Function); -} - -bool XsnsCall(uint8_t Function) -{ - bool result = false; - - DEBUG_TRACE_LOG(PSTR("SNS: %d"), Function); - -#ifdef PROFILE_XSNS_EVERY_SECOND - uint32_t profile_start_millis = millis(); -#endif - - for (uint32_t x = 0; x < xsns_present; x++) { -#ifdef USE_DEBUG_DRIVER - if (XsnsEnabled(x)) { -#endif - - if ((FUNC_WEB_SENSOR == Function) && !XsnsEnabled(x)) { continue; } - -#ifdef PROFILE_XSNS_SENSOR_EVERY_SECOND - uint32_t profile_start_millis = millis(); -#endif - result = xsns_func_ptr[x](Function); - -#ifdef PROFILE_XSNS_SENSOR_EVERY_SECOND - uint32_t profile_millis = millis() - profile_start_millis; - if (profile_millis) { - if (FUNC_EVERY_SECOND == Function) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PRF: At %08u XsnsCall %d to Sensor %d took %u mS"), uptime, Function, x, profile_millis); - } - } -#endif - - if (result && ((FUNC_COMMAND == Function) || - (FUNC_PIN_STATE == Function) || - (FUNC_COMMAND_SENSOR == Function) - )) { - break; - } -#ifdef USE_DEBUG_DRIVER - } -#endif - } - -#ifdef PROFILE_XSNS_EVERY_SECOND - uint32_t profile_millis = millis() - profile_start_millis; - if (profile_millis) { - if (FUNC_EVERY_SECOND == Function) { - AddLog_P2(LOG_LEVEL_DEBUG, PSTR("PRF: At %08u XsnsCall %d took %u mS"), uptime, Function, profile_millis); - } - } -#endif - - return result; -} -# 1 "C:/shared/sonoff/Git/Tasmota/tasmota/xx2c_interface.ino" -# 20 "C:/shared/sonoff/Git/Tasmota/tasmota/xx2c_interface.ino" -#ifdef USE_I2C - -#ifdef XFUNC_PTR_IN_ROM -const uint8_t kI2cList[] PROGMEM = { -#else -const uint8_t kI2cList[] = { -#endif - -#ifdef XI2C_01 - XI2C_01, -#endif - -#ifdef XI2C_02 - XI2C_02, -#endif - -#ifdef XI2C_03 - XI2C_03, -#endif - -#ifdef XI2C_04 - XI2C_04, -#endif - -#ifdef XI2C_05 - XI2C_05, -#endif - -#ifdef XI2C_06 - XI2C_06, -#endif - -#ifdef XI2C_07 - XI2C_07, -#endif - -#ifdef XI2C_08 - XI2C_08, -#endif - -#ifdef XI2C_09 - XI2C_09, -#endif - -#ifdef XI2C_10 - XI2C_10, -#endif - -#ifdef XI2C_11 - XI2C_11, -#endif - -#ifdef XI2C_12 - XI2C_12, -#endif - -#ifdef XI2C_13 - XI2C_13, -#endif - -#ifdef XI2C_14 - XI2C_14, -#endif - -#ifdef XI2C_15 - XI2C_15, -#endif - -#ifdef XI2C_16 - XI2C_16, -#endif - -#ifdef XI2C_17 - XI2C_17, -#endif - -#ifdef XI2C_18 - XI2C_18, -#endif - -#ifdef XI2C_19 - XI2C_19, -#endif - -#ifdef XI2C_20 - XI2C_20, -#endif - -#ifdef XI2C_21 - XI2C_21, -#endif - -#ifdef XI2C_22 - XI2C_22, -#endif - -#ifdef XI2C_23 - XI2C_23, -#endif - -#ifdef XI2C_24 - XI2C_24, -#endif - -#ifdef XI2C_25 - XI2C_25, -#endif - -#ifdef XI2C_26 - XI2C_26, -#endif - -#ifdef XI2C_27 - XI2C_27, -#endif - -#ifdef XI2C_28 - XI2C_28, -#endif - -#ifdef XI2C_29 - XI2C_29, -#endif - -#ifdef XI2C_30 - XI2C_30, -#endif - -#ifdef XI2C_31 - XI2C_31, -#endif - -#ifdef XI2C_32 - XI2C_32, -#endif - -#ifdef XI2C_33 - XI2C_33, -#endif - -#ifdef XI2C_34 - XI2C_34, -#endif - -#ifdef XI2C_35 - XI2C_35, -#endif - -#ifdef XI2C_36 - XI2C_36, -#endif - -#ifdef XI2C_37 - XI2C_37, -#endif - -#ifdef XI2C_38 - XI2C_38, -#endif - -#ifdef XI2C_39 - XI2C_39, -#endif - -#ifdef XI2C_40 - XI2C_40, -#endif - -#ifdef XI2C_41 - XI2C_41, -#endif - -#ifdef XI2C_42 - XI2C_42, -#endif - -#ifdef XI2C_43 - XI2C_43, -#endif - -#ifdef XI2C_44 - XI2C_44, -#endif - -#ifdef XI2C_45 - XI2C_45, -#endif - -#ifdef XI2C_46 - XI2C_46, -#endif - -#ifdef XI2C_47 - XI2C_47, -#endif - -#ifdef XI2C_48 - XI2C_48, -#endif - -#ifdef XI2C_49 - XI2C_49, -#endif - -#ifdef XI2C_50 - XI2C_50, -#endif - -#ifdef XI2C_51 - XI2C_51, -#endif - -#ifdef XI2C_52 - XI2C_52, -#endif - -#ifdef XI2C_53 - XI2C_53, -#endif - -#ifdef XI2C_54 - XI2C_54, -#endif - -#ifdef XI2C_55 - XI2C_55, -#endif - -#ifdef XI2C_56 - XI2C_56, -#endif - -#ifdef XI2C_57 - XI2C_57, -#endif - -#ifdef XI2C_58 - XI2C_58, -#endif - -#ifdef XI2C_59 - XI2C_59, -#endif - -#ifdef XI2C_60 - XI2C_60, -#endif - -#ifdef XI2C_61 - XI2C_61, -#endif - -#ifdef XI2C_62 - XI2C_62, -#endif - -#ifdef XI2C_63 - XI2C_63, -#endif - -#ifdef XI2C_64 - XI2C_64, -#endif - -#ifdef XI2C_65 - XI2C_65, -#endif - -#ifdef XI2C_66 - XI2C_66, -#endif - -#ifdef XI2C_67 - XI2C_67, -#endif - -#ifdef XI2C_68 - XI2C_68, -#endif - -#ifdef XI2C_69 - XI2C_69, -#endif - -#ifdef XI2C_70 - XI2C_70, -#endif - -#ifdef XI2C_71 - XI2C_71, -#endif - -#ifdef XI2C_72 - XI2C_72, -#endif - -#ifdef XI2C_73 - XI2C_73, -#endif - -#ifdef XI2C_74 - XI2C_74, -#endif - -#ifdef XI2C_75 - XI2C_75, -#endif - -#ifdef XI2C_76 - XI2C_76, -#endif - -#ifdef XI2C_77 - XI2C_77, -#endif - -#ifdef XI2C_78 - XI2C_78, -#endif - -#ifdef XI2C_79 - XI2C_79, -#endif - -#ifdef XI2C_80 - XI2C_80, -#endif - -#ifdef XI2C_81 - XI2C_81, -#endif - -#ifdef XI2C_82 - XI2C_82, -#endif - -#ifdef XI2C_83 - XI2C_83, -#endif - -#ifdef XI2C_84 - XI2C_84, -#endif - -#ifdef XI2C_85 - XI2C_85, -#endif - -#ifdef XI2C_86 - XI2C_86, -#endif - -#ifdef XI2C_87 - XI2C_87, -#endif - -#ifdef XI2C_88 - XI2C_88, -#endif - -#ifdef XI2C_89 - XI2C_89, -#endif - -#ifdef XI2C_90 - XI2C_90, -#endif - -#ifdef XI2C_91 - XI2C_91, -#endif - -#ifdef XI2C_92 - XI2C_92, -#endif - -#ifdef XI2C_93 - XI2C_93, -#endif - -#ifdef XI2C_94 - XI2C_94, -#endif - -#ifdef XI2C_95 - XI2C_95, -#endif - -#ifdef XI2C_96 - XI2C_96 -#endif -}; - - - -bool I2cEnabled(uint32_t i2c_index) -{ - return (i2c_flg && bitRead(Settings.i2c_drivers[i2c_index / 32], i2c_index % 32)); -} - -void I2cDriverState(void) -{ - ResponseAppend_P(PSTR("\"")); - for (uint32_t i = 0; i < sizeof(kI2cList); i++) { -#ifdef XFUNC_PTR_IN_ROM - uint32_t i2c_driver_id = pgm_read_byte(kI2cList + i); -#else - uint32_t i2c_driver_id = kI2cList[i]; -#endif - bool disabled = false; - if (i2c_driver_id < MAX_I2C_DRIVERS) { - disabled = !bitRead(Settings.i2c_drivers[i2c_driver_id / 32], i2c_driver_id % 32); - } - ResponseAppend_P(PSTR("%s%s%d"), (i) ? "," : "", (disabled) ? "!" : "", i2c_driver_id); - } - ResponseAppend_P(PSTR("\"")); -} - -#endif \ No newline at end of file diff --git a/tasmota/xsns_64_aht10.ino b/tasmota/xsns_63_aht1x.ino similarity index 94% rename from tasmota/xsns_64_aht10.ino rename to tasmota/xsns_63_aht1x.ino index 95d5a3e2b..59a02a7de 100644 --- a/tasmota/xsns_64_aht10.ino +++ b/tasmota/xsns_63_aht1x.ino @@ -1,7 +1,7 @@ /* - xsns_64_AHT10.ino - AHT10 I2C temperature and humidity sensor support for Tasmota + xsns_63_AHT1x.ino - AHT10 I2C temperature and humidity sensor support for Tasmota - Copyright (C) 2020 M. Wagner + Copyright (C) 2020 Martin Wagner 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 @@ -18,14 +18,14 @@ */ #ifdef USE_I2C -#ifdef USE_AHT10 +#ifdef USE_AHT1x /*********************************************************************************************\ - * AHT10 - Temperature and Humidity + * AHT10/15 - Temperature and Humidity * * I2C Address: 0x38 \*********************************************************************************************/ -#define XSNS_64 64 +#define XSNS_63 63 #define XI2C_43 43 // See I2CDEVICES.md #define AHT10_ADDR 0x38 @@ -40,7 +40,7 @@ struct AHT10 { float temperature = NAN; uint8_t valid = 0; uint8_t count = 0; - char name[6] = "AHT10"; + char name[9] = "AHT10/15"; } AHT10; @@ -60,6 +60,7 @@ bool AHT10Read(void) { temp[i] = Wire.read(); } + result_h = ((temp[1] << 16) | (temp[2] << 8) | temp[3]) >> 4; result_t = ((temp[3] & 0x0F) << 16) | (temp[4] << 8) | temp[5]; @@ -160,7 +161,7 @@ void AHT10Show(bool json) * Interface \*********************************************************************************************/ -bool Xsns64(uint8_t function) +bool Xsns63(uint8_t function) { if (!I2cEnabled(XI2C_43)) { return false; } From 5c4864499401af9a84a3c90aec43d53904398971 Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Wed, 19 Feb 2020 10:23:56 +0100 Subject: [PATCH 10/13] typo change --- tasmota/my_user_config.h | 2 +- tasmota/xsns_63_aht1x.ino | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 7f7fa3a45..d641ac761 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -486,7 +486,7 @@ // #define USE_HIH6 // [I2cDriver36] Enable Honeywell HIH Humidity and Temperature sensor (I2C address 0x27) (+0k6) // #define USE_DHT12 // [I2cDriver41] Enable DHT12 humidity and temperature sensor (I2C address 0x5C) (+0k7 code) // #define USE_DS1624 // [I2cDriver42] Enable DS1624, DS1621 temperature sensor (I2C addresses 0x48 - 0x4F) (+1k2 code) - #define USE_AHT1x // [I2cDriver43] Enable AHT10/15 humidity and temperature sensor (I2C address 0x38) (+0k8 code) +// #define USE_AHT1x // [I2cDriver43] Enable AHT10/15 humidity and temperature sensor (I2C address 0x38) (+0k8 code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 diff --git a/tasmota/xsns_63_aht1x.ino b/tasmota/xsns_63_aht1x.ino index 59a02a7de..4407fb625 100644 --- a/tasmota/xsns_63_aht1x.ino +++ b/tasmota/xsns_63_aht1x.ino @@ -56,7 +56,7 @@ bool AHT10Read(void) delay(100); Wire.requestFrom(AHT10_ADDR, 6); - for(unsigned char i = 0; Wire.available() > 0; i++) + for(uint8_t i = 0; Wire.available() > 0; i++) { temp[i] = Wire.read(); } From fbd2216d094778f28198965d49a7b2a47f32e676 Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Wed, 19 Feb 2020 14:06:35 +0100 Subject: [PATCH 11/13] Update xsns_63_aht1x.ino Sensor Name changement --- tasmota/xsns_63_aht1x.ino | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tasmota/xsns_63_aht1x.ino b/tasmota/xsns_63_aht1x.ino index 4407fb625..815f3a30a 100644 --- a/tasmota/xsns_63_aht1x.ino +++ b/tasmota/xsns_63_aht1x.ino @@ -40,13 +40,13 @@ struct AHT10 { float temperature = NAN; uint8_t valid = 0; uint8_t count = 0; - char name[9] = "AHT10/15"; + char name[6] = "AHT1x"; } AHT10; bool AHT10Read(void) { - unsigned long result_t, result_h, temp[6]; + unsigned long result_t, result_h, data[6]; if (AHT10.valid) { AHT10.valid--; } @@ -58,11 +58,11 @@ bool AHT10Read(void) Wire.requestFrom(AHT10_ADDR, 6); for(uint8_t i = 0; Wire.available() > 0; i++) { - temp[i] = Wire.read(); + data[i] = Wire.read(); } - result_h = ((temp[1] << 16) | (temp[2] << 8) | temp[3]) >> 4; - result_t = ((temp[3] & 0x0F) << 16) | (temp[4] << 8) | temp[5]; + result_h = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4; + result_t = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]; AHT10.humidity = result_h * 100 / 1048576; AHT10.temperature = ((200 * result_t) / 1048576) - 50; From 84a2da2ee27c480747668583242bfb1a2051ec36 Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Wed, 19 Feb 2020 14:24:37 +0100 Subject: [PATCH 12/13] Update support_features.ino --- tasmota/support_features.ino | 1 - 1 file changed, 1 deletion(-) diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 648235f12..85f5a77ea 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -516,7 +516,6 @@ void GetFeatures(void) #ifdef USE_AHT1x feature5 |= 0x10000000; // xsns_63_aht1x.ino #endif -// feature5 |= 0x10000000; // feature5 |= 0x20000000; // feature5 |= 0x40000000; // feature5 |= 0x80000000; From f7d4d5ddb18870e3b3687bb1e62f388acc92cec3 Mon Sep 17 00:00:00 2001 From: device111 <48546979+device111@users.noreply.github.com> Date: Wed, 19 Feb 2020 19:49:09 +0100 Subject: [PATCH 13/13] Ad release note --- RELEASENOTES.md | 1 + tasmota/xsns_63_aht1x.ino | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0962f6617..cf7184534 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -103,3 +103,4 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add support for FiF LE-01MR energy meter by saper-2 (#7584) - Add new DHT driver. The old driver can still be used using define USE_DHT_OLD (#7468) - Add another new DHT driver based on ESPEasy. The old driver can still be used using define USE_DHT_OLD. The previous new driver can be used with define USE_DHT_V2 (#7717) +- Add initial support for Sensors AHT10 and AHT15 by Martin Wagner (#7596) \ No newline at end of file diff --git a/tasmota/xsns_63_aht1x.ino b/tasmota/xsns_63_aht1x.ino index 815f3a30a..b3b17e0e4 100644 --- a/tasmota/xsns_63_aht1x.ino +++ b/tasmota/xsns_63_aht1x.ino @@ -50,22 +50,22 @@ bool AHT10Read(void) if (AHT10.valid) { AHT10.valid--; } - Wire.beginTransmission(AHT10_ADDR); - Wire.write(eSensorMeasureCmd, 3); - Wire.endTransmission(); - delay(100); + Wire.beginTransmission(AHT10_ADDR); + Wire.write(eSensorMeasureCmd, 3); + Wire.endTransmission(); + delay(100); - Wire.requestFrom(AHT10_ADDR, 6); - for(uint8_t i = 0; Wire.available() > 0; i++) - { - data[i] = Wire.read(); - } + Wire.requestFrom(AHT10_ADDR, 6); + for(uint8_t i = 0; Wire.available() > 0; i++) + { + data[i] = Wire.read(); + } - result_h = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4; - result_t = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]; + result_h = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4; + result_t = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]; - AHT10.humidity = result_h * 100 / 1048576; - AHT10.temperature = ((200 * result_t) / 1048576) - 50; + AHT10.humidity = result_h * 100 / 1048576; + AHT10.temperature = ((200 * result_t) / 1048576) - 50; if (isnan(AHT10.temperature) || isnan(AHT10.humidity)) { return false; }