diff --git a/include/tasmota_options.h b/include/tasmota_options.h index 840daf379..ebfc69019 100644 --- a/include/tasmota_options.h +++ b/include/tasmota_options.h @@ -21,6 +21,6 @@ #define __TASMOTA_INCLUDE__ #include "../tasmota/my_user_config.h" -#include "../tasmota/tasmota_configurations.h" +#include "../tasmota/include/tasmota_configurations.h" #endif // __TASMOTA_INCLUDE__ \ No newline at end of file diff --git a/lib/libesp32/berry/default/berry_conf.h b/lib/libesp32/berry/default/berry_conf.h index 84255936b..c70af7945 100644 --- a/lib/libesp32/berry/default/berry_conf.h +++ b/lib/libesp32/berry/default/berry_conf.h @@ -12,7 +12,7 @@ #ifdef COMPILE_BERRY_LIB #include "my_user_config.h" - #include "tasmota_configurations.h" + #include "include/tasmota_configurations.h" #endif /* Macro: BE_DEBUG diff --git a/pio-tools/pre_source_dir.py b/pio-tools/pre_source_dir.py index 19198f394..660f5df0a 100644 --- a/pio-tools/pre_source_dir.py +++ b/pio-tools/pre_source_dir.py @@ -6,7 +6,7 @@ Import("env") def FindInoNodes(env): src_dir = glob.escape(env.subst("$PROJECT_SRC_DIR")) return env.Glob(os.path.join(src_dir, "*.ino")) + env.Glob( - os.path.join(src_dir, "ino_*", "*.ino") + os.path.join(src_dir, "tasmota_*", "*.ino") ) env.AddMethod(FindInoNodes) diff --git a/tasmota/berry/berry_tasmota_config.h b/tasmota/berry/berry_tasmota_config.h index d0ebf89fc..b3f4f2c17 100644 --- a/tasmota/berry/berry_tasmota_config.h +++ b/tasmota/berry/berry_tasmota_config.h @@ -1,4 +1,4 @@ // Configuration #include "my_user_config.h" -#include "tasmota_configurations.h" +#include "include/tasmota_configurations.h" diff --git a/tasmota/i18n.h b/tasmota/include/i18n.h similarity index 100% rename from tasmota/i18n.h rename to tasmota/include/i18n.h diff --git a/tasmota/local_ca_data_sample.h b/tasmota/include/local_ca_data_sample.h similarity index 99% rename from tasmota/local_ca_data_sample.h rename to tasmota/include/local_ca_data_sample.h index 160e362ad..93e286344 100644 --- a/tasmota/local_ca_data_sample.h +++ b/tasmota/include/local_ca_data_sample.h @@ -37,15 +37,15 @@ Be sure to generate both files: local_ca_data.h, and local_ca_descriptor.h // // this is what the result will look like, except there will be // a lot of data bytes defined in the first three arrays -// -static const unsigned char PROGMEM TA0_DN[] = { - // variable number of bytes go here (typically 100-140 or so) for the DN -}; - -static const unsigned char PROGMEM TA0_RSA_N[] = { - // 256 bytes go here for the public key modulus -}; - -static const unsigned char PROGMEM TA0_RSA_E[] = { - // 3 bytes go here for the public key exponent -}; +// +static const unsigned char PROGMEM TA0_DN[] = { + // variable number of bytes go here (typically 100-140 or so) for the DN +}; + +static const unsigned char PROGMEM TA0_RSA_N[] = { + // 256 bytes go here for the public key modulus +}; + +static const unsigned char PROGMEM TA0_RSA_E[] = { + // 3 bytes go here for the public key exponent +}; diff --git a/tasmota/local_ca_descriptor_sample.h b/tasmota/include/local_ca_descriptor_sample.h similarity index 99% rename from tasmota/local_ca_descriptor_sample.h rename to tasmota/include/local_ca_descriptor_sample.h index 3d4481981..3b9ee2cb3 100644 --- a/tasmota/local_ca_descriptor_sample.h +++ b/tasmota/include/local_ca_descriptor_sample.h @@ -37,14 +37,14 @@ Be sure to generate both files: local_ca_data.h, and local_ca_descriptor.h // // this is what the result will look like // - { - { (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, - } } - } - } + { + { (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, + } } + } + } diff --git a/tasmota/sendemail_ESP8266.h b/tasmota/include/sendemail_ESP8266.h similarity index 100% rename from tasmota/sendemail_ESP8266.h rename to tasmota/include/sendemail_ESP8266.h diff --git a/tasmota/tasmota.h b/tasmota/include/tasmota.h similarity index 100% rename from tasmota/tasmota.h rename to tasmota/include/tasmota.h diff --git a/tasmota/tasmota_compat.h b/tasmota/include/tasmota_compat.h similarity index 100% rename from tasmota/tasmota_compat.h rename to tasmota/include/tasmota_compat.h diff --git a/tasmota/tasmota_configurations.h b/tasmota/include/tasmota_configurations.h similarity index 100% rename from tasmota/tasmota_configurations.h rename to tasmota/include/tasmota_configurations.h diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/include/tasmota_configurations_ESP32.h similarity index 100% rename from tasmota/tasmota_configurations_ESP32.h rename to tasmota/include/tasmota_configurations_ESP32.h diff --git a/tasmota/tasmota_globals.h b/tasmota/include/tasmota_globals.h similarity index 99% rename from tasmota/tasmota_globals.h rename to tasmota/include/tasmota_globals.h index 284e7f314..1909d2c66 100644 --- a/tasmota/tasmota_globals.h +++ b/tasmota/include/tasmota_globals.h @@ -63,7 +63,7 @@ String EthernetMacAddress(void); * Preconfigured configurations \*********************************************************************************************/ -#include "tasmota_configurations.h" // Preconfigured configurations +#include "include/tasmota_configurations.h" // Preconfigured configurations /*********************************************************************************************\ * ESP8266 specific parameters diff --git a/tasmota/tasmota_template.h b/tasmota/include/tasmota_template.h similarity index 99% rename from tasmota/tasmota_template.h rename to tasmota/include/tasmota_template.h index ca9c2732a..80cfa3fe8 100644 --- a/tasmota/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -1239,7 +1239,7 @@ typedef struct MYTMPLT { //******************************************************************************************** #ifdef ESP8266 -#include "tasmota_template_legacy.h" +#include "include/tasmota_template_legacy.h" /********************************************************************************************\ * ESP8266 Module Templates diff --git a/tasmota/tasmota_template_legacy.h b/tasmota/include/tasmota_template_legacy.h similarity index 100% rename from tasmota/tasmota_template_legacy.h rename to tasmota/include/tasmota_template_legacy.h diff --git a/tasmota/settings.h b/tasmota/include/tasmota_types.h similarity index 96% rename from tasmota/settings.h rename to tasmota/include/tasmota_types.h index b39455ddf..443f7db18 100644 --- a/tasmota/settings.h +++ b/tasmota/include/tasmota_types.h @@ -1,5 +1,5 @@ /* - settings.h - setting variables for Tasmota + tasmota_types.h - setting variables for Tasmota Copyright (C) 2021 Theo Arends @@ -17,10 +17,8 @@ along with this program. If not, see . */ -#ifndef _SETTINGS_H_ -#define _SETTINGS_H_ - -const uint8_t PARAM8_SIZE = 18; // Number of param bytes (SetOption) +#ifndef _TASMOTA_TYPES_H_ +#define _TASMOTA_TYPES_H_ // Bitfield to be used for any SetOption0 .. SetOption31 persistent single bit typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... @@ -490,6 +488,7 @@ typedef union { const uint32_t settings_text_size = 699; // Settings->text_pool[size] = Settings->display_model (2D2) - Settings->text_pool (017) const uint8_t MAX_TUYA_FUNCTIONS = 16; +const uint8_t PARAM8_SIZE = 18; // Number of param bytes (SetOption) typedef struct { uint16_t cfg_holder; // 000 v6 header @@ -867,70 +866,6 @@ typedef struct { static_assert(sizeof(TSettings) == 4096, "TSettings Size is not correct"); -typedef struct { - uint16_t valid; // 280 (RTC memory offset 100 - sizeof(RTCRBT)) - uint8_t fast_reboot_count; // 282 - uint8_t free_003[1]; // 283 -} TRtcReboot; -TRtcReboot RtcReboot; -#ifdef ESP32 -RTC_NOINIT_ATTR TRtcReboot RtcDataReboot; -#endif // ESP32 - -typedef struct { - uint16_t valid; // 290 (RTC memory offset 100) - uint8_t oswatch_blocked_loop; // 292 - uint8_t ota_loader; // 293 - uint32_t energy_kWhtoday; // 294 - uint32_t energy_kWhtotal; // 298 - volatile uint32_t pulse_counter[MAX_COUNTERS]; // 29C - See #9521 why volatile - power_t power; // 2AC - EnergyUsage energy_usage; // 2B0 - uint32_t nextwakeup; // 2C8 - uint32_t baudrate; // 2CC - uint32_t ultradeepsleep; // 2D0 - uint16_t deepsleep_slip; // 2D4 - uint8_t improv_state; // 2D6 - - uint8_t free_2d7[1]; // 2D7 - - int32_t energy_kWhtoday_ph[3]; // 2D8 - int32_t energy_kWhtotal_ph[3]; // 2E4 - int32_t energy_kWhexport_ph[3]; // 2F0 - - uint8_t free_2fc[4]; // 2FC -} TRtcSettings; -TRtcSettings RtcSettings; -#ifdef ESP32 -RTC_NOINIT_ATTR TRtcSettings RtcDataSettings; -#endif // ESP32 - -struct TIME_T { - uint8_t second; - uint8_t minute; - uint8_t hour; - uint8_t day_of_week; // sunday is day 1 - uint8_t day_of_month; - uint8_t month; - char name_of_month[4]; - uint16_t day_of_year; - uint16_t year; - uint32_t days; - uint32_t valid; -} RtcTime; - -struct XDRVMAILBOX { - bool grpflg; - bool usridx; - uint16_t command_code; - uint32_t index; - uint32_t data_len; - int32_t payload; - char *topic; - char *data; - char *command; -} XdrvMailbox; - typedef union { // Restricted by MISRA-C Rule 18.4 but so useful... uint16_t data; // Allow bit manipulation struct { @@ -974,4 +909,4 @@ typedef union { #endif #endif -#endif // _SETTINGS_H_ +#endif // _TASMOTA_TYPES_H_ diff --git a/tasmota/tasmota_version.h b/tasmota/include/tasmota_version.h similarity index 94% rename from tasmota/tasmota_version.h rename to tasmota/include/tasmota_version.h index f4b32b051..8543e8fc8 100644 --- a/tasmota/tasmota_version.h +++ b/tasmota/include/tasmota_version.h @@ -20,6 +20,6 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x0B010003; // 11.1.0.3 +const uint32_t VERSION = 0x0B010004; // 11.1.0.4 #endif // _TASMOTA_VERSION_H_ diff --git a/tasmota/xdrv_53_projector_ctrl.h b/tasmota/include/xdrv_53_projector_ctrl.h similarity index 100% rename from tasmota/xdrv_53_projector_ctrl.h rename to tasmota/include/xdrv_53_projector_ctrl.h diff --git a/tasmota/xsns_62_esp32_mi.h b/tasmota/include/xsns_62_esp32_mi.h similarity index 100% rename from tasmota/xsns_62_esp32_mi.h rename to tasmota/include/xsns_62_esp32_mi.h diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index d15cfc001..3232fccec 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -21,16 +21,16 @@ #ifndef ESP32_STAGE // ESP32 Stage has no core_version.h file. Disable include via PlatformIO Option #include // Arduino_Esp8266 version information (ARDUINO_ESP8266_RELEASE and ARDUINO_ESP8266_RELEASE_2_7_1) #endif // ESP32_STAGE -#include "tasmota_compat.h" -#include "tasmota_version.h" // Tasmota version information -#include "tasmota.h" // Enumeration used in my_user_config.h +#include "include/tasmota_compat.h" +#include "include/tasmota_version.h" // Tasmota version information +#include "include/tasmota.h" // Enumeration used in my_user_config.h #include "my_user_config.h" // Fixed user configurable options #ifdef USE_TLS #include // We need to include before "tasmota_globals.h" to take precedence over the BearSSL version in Arduino #endif // USE_TLS -#include "tasmota_globals.h" // Function prototypes and global configuration -#include "i18n.h" // Language support configured by my_user_config.h -#include "tasmota_template.h" // Hardware configuration +#include "include/tasmota_globals.h" // Function prototypes and global configuration +#include "include/i18n.h" // Language support configured by my_user_config.h +#include "include/tasmota_template.h" // Hardware configuration // Libraries #include // Ota @@ -83,7 +83,7 @@ #endif // USE_UFILESYS // Structs -#include "settings.h" +#include "include/tasmota_types.h" #ifdef CONFIG_IDF_TARGET_ESP32 #include "soc/efuse_reg.h" @@ -95,6 +95,70 @@ const uint32_t VERSION_MARKER[] PROGMEM = { 0x5AA55AA5, 0xFFFFFFFF, 0xA55AA55A }; +typedef struct { + uint16_t valid; // 280 (RTC memory offset 100 - sizeof(RTCRBT)) + uint8_t fast_reboot_count; // 282 + uint8_t free_003[1]; // 283 +} TRtcReboot; +TRtcReboot RtcReboot; +#ifdef ESP32 +RTC_NOINIT_ATTR TRtcReboot RtcDataReboot; +#endif // ESP32 + +typedef struct { + uint16_t valid; // 290 (RTC memory offset 100) + uint8_t oswatch_blocked_loop; // 292 + uint8_t ota_loader; // 293 + uint32_t energy_kWhtoday; // 294 + uint32_t energy_kWhtotal; // 298 + volatile uint32_t pulse_counter[MAX_COUNTERS]; // 29C - See #9521 why volatile + power_t power; // 2AC + EnergyUsage energy_usage; // 2B0 + uint32_t nextwakeup; // 2C8 + uint32_t baudrate; // 2CC + uint32_t ultradeepsleep; // 2D0 + uint16_t deepsleep_slip; // 2D4 + uint8_t improv_state; // 2D6 + + uint8_t free_2d7[1]; // 2D7 + + int32_t energy_kWhtoday_ph[3]; // 2D8 + int32_t energy_kWhtotal_ph[3]; // 2E4 + int32_t energy_kWhexport_ph[3]; // 2F0 + + uint8_t free_2fc[4]; // 2FC +} TRtcSettings; +TRtcSettings RtcSettings; +#ifdef ESP32 +RTC_NOINIT_ATTR TRtcSettings RtcDataSettings; +#endif // ESP32 + +struct TIME_T { + uint8_t second; + uint8_t minute; + uint8_t hour; + uint8_t day_of_week; // sunday is day 1 + uint8_t day_of_month; + uint8_t month; + char name_of_month[4]; + uint16_t day_of_year; + uint16_t year; + uint32_t days; + uint32_t valid; +} RtcTime; + +struct XDRVMAILBOX { + bool grpflg; + bool usridx; + uint16_t command_code; + uint32_t index; + uint32_t data_len; + int32_t payload; + char *topic; + char *data; + char *command; +} XdrvMailbox; + WiFiUDP PortUdp; // UDP Syslog and Alexa #ifdef ESP32 diff --git a/tasmota/homekit.c b/tasmota/tasmota_support/homekit.c old mode 100755 new mode 100644 similarity index 100% rename from tasmota/homekit.c rename to tasmota/tasmota_support/homekit.c diff --git a/tasmota/sendemail_ESP32.ino b/tasmota/tasmota_support/sendemail_ESP32.ino similarity index 100% rename from tasmota/sendemail_ESP32.ino rename to tasmota/tasmota_support/sendemail_ESP32.ino diff --git a/tasmota/sendemail_ESP8266.ino b/tasmota/tasmota_support/sendemail_ESP8266.ino similarity index 99% rename from tasmota/sendemail_ESP8266.ino rename to tasmota/tasmota_support/sendemail_ESP8266.ino index 4abc897fc..508ca8131 100644 --- a/tasmota/sendemail_ESP8266.ino +++ b/tasmota/tasmota_support/sendemail_ESP8266.ino @@ -1,6 +1,6 @@ #ifdef ESP8266 #ifdef USE_SENDMAIL -#include "sendemail_ESP8266.h" +#include "include/sendemail_ESP8266.h" // enable serial debugging //#define DEBUG_EMAIL_PORT diff --git a/tasmota/settings.ino b/tasmota/tasmota_support/settings.ino similarity index 100% rename from tasmota/settings.ino rename to tasmota/tasmota_support/settings.ino diff --git a/tasmota/support.ino b/tasmota/tasmota_support/support.ino similarity index 100% rename from tasmota/support.ino rename to tasmota/tasmota_support/support.ino diff --git a/tasmota/support_button.ino b/tasmota/tasmota_support/support_button.ino similarity index 100% rename from tasmota/support_button.ino rename to tasmota/tasmota_support/support_button.ino diff --git a/tasmota/support_command.ino b/tasmota/tasmota_support/support_command.ino similarity index 100% rename from tasmota/support_command.ino rename to tasmota/tasmota_support/support_command.ino diff --git a/tasmota/support_cores.ino b/tasmota/tasmota_support/support_cores.ino similarity index 100% rename from tasmota/support_cores.ino rename to tasmota/tasmota_support/support_cores.ino diff --git a/tasmota/support_crash_recorder.ino b/tasmota/tasmota_support/support_crash_recorder.ino similarity index 100% rename from tasmota/support_crash_recorder.ino rename to tasmota/tasmota_support/support_crash_recorder.ino diff --git a/tasmota/support_device_groups.ino b/tasmota/tasmota_support/support_device_groups.ino similarity index 100% rename from tasmota/support_device_groups.ino rename to tasmota/tasmota_support/support_device_groups.ino diff --git a/tasmota/support_eeprom.ino b/tasmota/tasmota_support/support_eeprom.ino similarity index 100% rename from tasmota/support_eeprom.ino rename to tasmota/tasmota_support/support_eeprom.ino diff --git a/tasmota/support_esp.ino b/tasmota/tasmota_support/support_esp.ino similarity index 100% rename from tasmota/support_esp.ino rename to tasmota/tasmota_support/support_esp.ino diff --git a/tasmota/support_esptool.ino b/tasmota/tasmota_support/support_esptool.ino similarity index 100% rename from tasmota/support_esptool.ino rename to tasmota/tasmota_support/support_esptool.ino diff --git a/tasmota/support_features.ino b/tasmota/tasmota_support/support_features.ino similarity index 100% rename from tasmota/support_features.ino rename to tasmota/tasmota_support/support_features.ino diff --git a/tasmota/support_flash_log.ino b/tasmota/tasmota_support/support_flash_log.ino similarity index 97% rename from tasmota/support_flash_log.ino rename to tasmota/tasmota_support/support_flash_log.ino index 58a8c2dc0..c28528a7f 100644 --- a/tasmota/support_flash_log.ino +++ b/tasmota/tasmota_support/support_flash_log.ino @@ -1,430 +1,430 @@ -/* - support_flash_log.ino - log to flash support for Sonoff-Tasmota - - Copyright (C) 2021 Theo Arends & Christian Baars - - 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 . - - -------------------------------------------------------------------------------------------- - Version Date Action Description - -------------------------------------------------------------------------------------------- - - - --- - 1.0.0.0 20190923 started - further development by Christian Baars - https://github.com/Staars/Sonoff-Tasmota - forked - from arendst/tasmota - https://github.com/arendst/Sonoff-Tasmota - base - code base from arendst and - written from scratch - -*/ - -/********************************************************************************************\ -| * Generic helper class to log arbitrary data to the OTA-partition -| * Working principle: Add preferrable small chunks of data to the sector buffer, which will -| * be written to FLASH when full automatically. The next sector will be -| * erased and is the anchor point for downloading and state configuration -| * after reboot. -\*********************************************************************************************/ - -#ifdef USE_FLOG -#ifdef ESP8266 - -class FLOG - -#define MAGIC_WORD_FL 0x464c //F, L - -{ - -struct header_t{ - uint16_t magic_word; // FL - uint16_t padding; // leave something for the future - uint32_t physical_start_sector:10; //first used sector of the current FLOG - uint32_t number:10; // number of this sector, starting with 0 for the first sector - uint32_t buf_pointer:12; //internal pointer to the next free position in the buffer = first empty byte when reading - }; // should be 4-byte-aligned - -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; // size of OTA-partition - uint32_t start; // start position of OTA-partition in bytes - uint32_t end; // end position of OTA-partition in bytes - uint16_t num_sectors; // calculated number of sectors with a size of 4096 bytes - - uint16_t first_erased_sector; // this will be our new start - uint16_t current_sector; // always point to next sector, where data from the buffer will be written to - - uint16_t bytes_left; // byte per buffer (of sector size 4096 bytes - 8 byte header size) - uint16_t sectors_left; // number of saved sectors for download - - uint8_t mode = 0; // 0 - write once on all sectors, then stop, 1 - write infinitely through the sectors - bool found_saved_data = false; // possible saved data has been found - bool ready = false; // the FLOG is initialized - bool running_download = false; // a download operation is running - bool recording = false; // ready for recording - - union sector_t{ - uint32_t dword_buffer[FLASH_SECTOR_SIZE/4]; - uint8_t byte_buffer[FLASH_SECTOR_SIZE]; - header_t header; // should be 4-byte-aligned - } sector; // the global buffer of 4096 bytes, used for reading and writing - - void init(void); - void addToBuffer(uint8_t src[], uint32_t size); - void startRecording(bool append); - void stopRecording(void); - - typedef void (*CallbackNoArgs) (); // simple typedef for a callback - typedef void (*CallbackWithArgs) (uint8_t *_record); // typedef for a callback with one argument - - void startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter); -}; - -extern "C" uint32_t _SPIFFS_start; // we make shure later, that only one of the two is really used ... -extern "C" uint32_t _FS_start; // ... depending on core-sdk-version - -/** - * @brief Will examine the start and end of the OTA-partition. Then the sector size will be computed, saved data should be found and the initial state will be configured. - */ -void FLOG::init(void) -{ -DEBUG_SENSOR_LOG(PSTR("FLOG: init ...")); -size = ESP.getSketchSize(); -// round one sector up -start = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); -end = (uint32_t)&_FS_start - 0x40200000; -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; // start with sector 0, could be first run or after crash -} -_searchSaves(); -_initBuffer(); -ready = true; -} - -/********************************************************************************************\ -| * -| * private helper functions -| * -\*********************************************************************************************/ - -/** - * @brief Read a sector into the global buffer - * - * @param one_sector as an uint8_t - */ -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); -} -/** - * @brief Erase the given sector og the OTA-partition - * - * @param one_sector as an uint8_t - */ -void FLOG::_eraseSector(uint8_t one_sector){ // Erase sector of FLOG/OTA - DEBUG_SENSOR_LOG(PSTR("FLOG: erasing sector number: %u" ), one_sector); - ESP.flashEraseSector((start/FLASH_SECTOR_SIZE)+one_sector); -} -/** - * @brief Write the global buffer to the given sector - * - * @param one_sector as an uint8_t - */ -void FLOG::_writeSector(uint8_t one_sector){ // Write sector of FLOG/OTA - 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); -} -/** - * @brief Clear the global buffer, but leave the header intact - * - */ -void FLOG::_clearBuffer(){ //not the header - 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); - // _showBuffer(); -} -/** - * @brief Write global buffer to FLASH and set the current sector to the next valid position, maybe to 0 - * - */ -void FLOG::_saveBufferToSector(){ // save buffer to already erased(!) sector, erase next sector, clear buffer, increment number - DEBUG_SENSOR_LOG(PSTR("FLOG: write buffer to current sector: %u" ),current_sector); - _writeSector(current_sector); - if(current_sector == num_sectors){ // 1 MB means ~110 sectors in OTA-partition, if we reach this, start a again - current_sector = 0; - } - else{ - current_sector++; - } - _eraseSector(current_sector); // we always erase the next sector, to find out were we are after restart - _clearBuffer(); - sector.header.number++; - DEBUG_SENSOR_LOG(PSTR("FLOG: new sector header number: %u" ),sector.header.number); -} - -/** - * @brief Typically after restart find the first erased sector as a starting point for further operations - * - */ -void FLOG::_findFirstErasedSector(){ - for (uint32_t i = 0; i3){ - break; - } - } -} - -/** - * @brief pass a data entry/record as uint8_t array with its size - * - * @param src uint8_t array - * @param size uint32_t size of the array - */ -void FLOG::addToBuffer(uint8_t src[], uint32_t size){ - if(mode == 0){ - if(sector.header.number == num_sectors && !ready){ - return; // we ignore additional calls and are done, TODO: maybe use meaningful return values - } - } - if((FLASH_SECTOR_SIZE-sector.header.buf_pointer-sizeof(sector.header))>size){ - // DEBUG_SENSOR_LOG(PSTR("FLOG: enough space left in buffer: %u"), FLASH_SECTOR_SIZE - sector.header.buf_pointer - sizeof(sector.header)); - // DEBUG_SENSOR_LOG(PSTR("FLOG: current buf_pointer: %u, size of added: %u"), sector.header.buf_pointer, size); - - memcpy(sector.byte_buffer + sector.header.buf_pointer, src, size); - sector.header.buf_pointer+=size; // this is the next free spot - // DEBUG_SENSOR_LOG(PSTR("FLOG: current buf_pointer: %u"), sector.header.buf_pointer); - } - 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++; - // but now save the data to the fresh buffer - 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; // this is the next free spot - } - } -} - -/** - * @brief shows that it is ready to accept recording - * - * @param append - if true append to current log, else start a new log - */ -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; // nothing to append to, we silently start a new log - } - if(append){ - sector.header.number = _saved_header.number+1; // continue with the next number - sector.header.physical_start_sector = _saved_header.physical_start_sector; // keep the old start sector - } - else{ //new log, old data is lost - sector.header.physical_start_sector = (uint16_t)first_erased_sector; - found_saved_data = false; - sectors_left = 0; - } - } - -/** - * @brief stop recording including saving current buffer to FLASH - * - */ -void FLOG::stopRecording(void){ - _saveBufferToSector(); - _findFirstErasedSector(); - _searchSaves(); - _initBuffer(); - recording = false; - found_saved_data = true; - } - -/** - * @brief Will start a downloads, needs the correct implementation of 3 callback functions - * - * @param size: size of the data entry/record in bytes, i.e. sizeof(myStruct) - * @param sendHeader: should implement at least something like: - * @example Webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); // This is very likely unknown!! - * Webserver->sendHeader(F("Content-Disposition"), F("attachment; filename=myfile.txt")); - * @param sendRecord: will receive the memory address as "uint8_t* addr" and should consume the current entry/record - * @example myStruct_t *entry = (myStruct_t*)addr; - * Then make useful Strings and send it, i.e.: Webserver->sendContent_P(myString); - * @param sendFooter: finish the download, should implement at least: - * @example Webserver->sendContent(""); - */ - 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; - // Callback 1: Create the header incl. file name, content length (probably unknown!!) and additional header stuff - sendHeader(); - - while(sectors_left){ - DEBUG_SENSOR_LOG(PSTR("FLOG: next sector: %u"), next_sector); - //initially we have the first sector already loaded, so we do it at the bottom - uint32_t k = sizeof(sector.header); - while(bytes_left){ - // DEBUG_SENSOR_LOG(PSTR("FLOG: DL %u %u"), Flog->sector.byte_buffer[k],Flog->sector.byte_buffer[k+1]); - uint8_t *_record_start = (uint8_t*)§or.byte_buffer[k]; // this is basically the start address of the current record/entry of the Log - // Callback 2: send the pointer for consuming the next record/entry and doing something useful to create a file - sendRecord(_record_start); - if(k%128 == 0){ // give control to the system every x iteration, TODO: This will fail, when record/entry-size is not 8 - // DEBUG_SENSOR_LOG(PSTR("FLOG: now loop(), %u bytes left"), Flog->bytes_left); - OsWatchLoop(); - delay(TasmotaGlobal.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(TasmotaGlobal.sleep); - } - running_download = false; - // Callback 3: create a footer or simply finish the download with an empty payload - sendFooter(); - // refresh settings for another download - _searchSaves(); - _initBuffer(); - } - - #endif // ESP8266 +/* + support_flash_log.ino - log to flash support for Sonoff-Tasmota + + Copyright (C) 2021 Theo Arends & Christian Baars + + 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 . + + -------------------------------------------------------------------------------------------- + Version Date Action Description + -------------------------------------------------------------------------------------------- + + + --- + 1.0.0.0 20190923 started - further development by Christian Baars - https://github.com/Staars/Sonoff-Tasmota + forked - from arendst/tasmota - https://github.com/arendst/Sonoff-Tasmota + base - code base from arendst and - written from scratch + +*/ + +/********************************************************************************************\ +| * Generic helper class to log arbitrary data to the OTA-partition +| * Working principle: Add preferrable small chunks of data to the sector buffer, which will +| * be written to FLASH when full automatically. The next sector will be +| * erased and is the anchor point for downloading and state configuration +| * after reboot. +\*********************************************************************************************/ + +#ifdef USE_FLOG +#ifdef ESP8266 + +class FLOG + +#define MAGIC_WORD_FL 0x464c //F, L + +{ + +struct header_t{ + uint16_t magic_word; // FL + uint16_t padding; // leave something for the future + uint32_t physical_start_sector:10; //first used sector of the current FLOG + uint32_t number:10; // number of this sector, starting with 0 for the first sector + uint32_t buf_pointer:12; //internal pointer to the next free position in the buffer = first empty byte when reading + }; // should be 4-byte-aligned + +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; // size of OTA-partition + uint32_t start; // start position of OTA-partition in bytes + uint32_t end; // end position of OTA-partition in bytes + uint16_t num_sectors; // calculated number of sectors with a size of 4096 bytes + + uint16_t first_erased_sector; // this will be our new start + uint16_t current_sector; // always point to next sector, where data from the buffer will be written to + + uint16_t bytes_left; // byte per buffer (of sector size 4096 bytes - 8 byte header size) + uint16_t sectors_left; // number of saved sectors for download + + uint8_t mode = 0; // 0 - write once on all sectors, then stop, 1 - write infinitely through the sectors + bool found_saved_data = false; // possible saved data has been found + bool ready = false; // the FLOG is initialized + bool running_download = false; // a download operation is running + bool recording = false; // ready for recording + + union sector_t{ + uint32_t dword_buffer[FLASH_SECTOR_SIZE/4]; + uint8_t byte_buffer[FLASH_SECTOR_SIZE]; + header_t header; // should be 4-byte-aligned + } sector; // the global buffer of 4096 bytes, used for reading and writing + + void init(void); + void addToBuffer(uint8_t src[], uint32_t size); + void startRecording(bool append); + void stopRecording(void); + + typedef void (*CallbackNoArgs) (); // simple typedef for a callback + typedef void (*CallbackWithArgs) (uint8_t *_record); // typedef for a callback with one argument + + void startDownload(size_t size, CallbackNoArgs sendHeader, CallbackWithArgs sendRecord, CallbackNoArgs sendFooter); +}; + +extern "C" uint32_t _SPIFFS_start; // we make shure later, that only one of the two is really used ... +extern "C" uint32_t _FS_start; // ... depending on core-sdk-version + +/** + * @brief Will examine the start and end of the OTA-partition. Then the sector size will be computed, saved data should be found and the initial state will be configured. + */ +void FLOG::init(void) +{ +DEBUG_SENSOR_LOG(PSTR("FLOG: init ...")); +size = ESP.getSketchSize(); +// round one sector up +start = (size + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); +end = (uint32_t)&_FS_start - 0x40200000; +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; // start with sector 0, could be first run or after crash +} +_searchSaves(); +_initBuffer(); +ready = true; +} + +/********************************************************************************************\ +| * +| * private helper functions +| * +\*********************************************************************************************/ + +/** + * @brief Read a sector into the global buffer + * + * @param one_sector as an uint8_t + */ +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); +} +/** + * @brief Erase the given sector og the OTA-partition + * + * @param one_sector as an uint8_t + */ +void FLOG::_eraseSector(uint8_t one_sector){ // Erase sector of FLOG/OTA + DEBUG_SENSOR_LOG(PSTR("FLOG: erasing sector number: %u" ), one_sector); + ESP.flashEraseSector((start/FLASH_SECTOR_SIZE)+one_sector); +} +/** + * @brief Write the global buffer to the given sector + * + * @param one_sector as an uint8_t + */ +void FLOG::_writeSector(uint8_t one_sector){ // Write sector of FLOG/OTA + 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); +} +/** + * @brief Clear the global buffer, but leave the header intact + * + */ +void FLOG::_clearBuffer(){ //not the header + 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); + // _showBuffer(); +} +/** + * @brief Write global buffer to FLASH and set the current sector to the next valid position, maybe to 0 + * + */ +void FLOG::_saveBufferToSector(){ // save buffer to already erased(!) sector, erase next sector, clear buffer, increment number + DEBUG_SENSOR_LOG(PSTR("FLOG: write buffer to current sector: %u" ),current_sector); + _writeSector(current_sector); + if(current_sector == num_sectors){ // 1 MB means ~110 sectors in OTA-partition, if we reach this, start a again + current_sector = 0; + } + else{ + current_sector++; + } + _eraseSector(current_sector); // we always erase the next sector, to find out were we are after restart + _clearBuffer(); + sector.header.number++; + DEBUG_SENSOR_LOG(PSTR("FLOG: new sector header number: %u" ),sector.header.number); +} + +/** + * @brief Typically after restart find the first erased sector as a starting point for further operations + * + */ +void FLOG::_findFirstErasedSector(){ + for (uint32_t i = 0; i3){ + break; + } + } +} + +/** + * @brief pass a data entry/record as uint8_t array with its size + * + * @param src uint8_t array + * @param size uint32_t size of the array + */ +void FLOG::addToBuffer(uint8_t src[], uint32_t size){ + if(mode == 0){ + if(sector.header.number == num_sectors && !ready){ + return; // we ignore additional calls and are done, TODO: maybe use meaningful return values + } + } + if((FLASH_SECTOR_SIZE-sector.header.buf_pointer-sizeof(sector.header))>size){ + // DEBUG_SENSOR_LOG(PSTR("FLOG: enough space left in buffer: %u"), FLASH_SECTOR_SIZE - sector.header.buf_pointer - sizeof(sector.header)); + // DEBUG_SENSOR_LOG(PSTR("FLOG: current buf_pointer: %u, size of added: %u"), sector.header.buf_pointer, size); + + memcpy(sector.byte_buffer + sector.header.buf_pointer, src, size); + sector.header.buf_pointer+=size; // this is the next free spot + // DEBUG_SENSOR_LOG(PSTR("FLOG: current buf_pointer: %u"), sector.header.buf_pointer); + } + 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++; + // but now save the data to the fresh buffer + 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; // this is the next free spot + } + } +} + +/** + * @brief shows that it is ready to accept recording + * + * @param append - if true append to current log, else start a new log + */ +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; // nothing to append to, we silently start a new log + } + if(append){ + sector.header.number = _saved_header.number+1; // continue with the next number + sector.header.physical_start_sector = _saved_header.physical_start_sector; // keep the old start sector + } + else{ //new log, old data is lost + sector.header.physical_start_sector = (uint16_t)first_erased_sector; + found_saved_data = false; + sectors_left = 0; + } + } + +/** + * @brief stop recording including saving current buffer to FLASH + * + */ +void FLOG::stopRecording(void){ + _saveBufferToSector(); + _findFirstErasedSector(); + _searchSaves(); + _initBuffer(); + recording = false; + found_saved_data = true; + } + +/** + * @brief Will start a downloads, needs the correct implementation of 3 callback functions + * + * @param size: size of the data entry/record in bytes, i.e. sizeof(myStruct) + * @param sendHeader: should implement at least something like: + * @example Webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); // This is very likely unknown!! + * Webserver->sendHeader(F("Content-Disposition"), F("attachment; filename=myfile.txt")); + * @param sendRecord: will receive the memory address as "uint8_t* addr" and should consume the current entry/record + * @example myStruct_t *entry = (myStruct_t*)addr; + * Then make useful Strings and send it, i.e.: Webserver->sendContent_P(myString); + * @param sendFooter: finish the download, should implement at least: + * @example Webserver->sendContent(""); + */ + 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; + // Callback 1: Create the header incl. file name, content length (probably unknown!!) and additional header stuff + sendHeader(); + + while(sectors_left){ + DEBUG_SENSOR_LOG(PSTR("FLOG: next sector: %u"), next_sector); + //initially we have the first sector already loaded, so we do it at the bottom + uint32_t k = sizeof(sector.header); + while(bytes_left){ + // DEBUG_SENSOR_LOG(PSTR("FLOG: DL %u %u"), Flog->sector.byte_buffer[k],Flog->sector.byte_buffer[k+1]); + uint8_t *_record_start = (uint8_t*)§or.byte_buffer[k]; // this is basically the start address of the current record/entry of the Log + // Callback 2: send the pointer for consuming the next record/entry and doing something useful to create a file + sendRecord(_record_start); + if(k%128 == 0){ // give control to the system every x iteration, TODO: This will fail, when record/entry-size is not 8 + // DEBUG_SENSOR_LOG(PSTR("FLOG: now loop(), %u bytes left"), Flog->bytes_left); + OsWatchLoop(); + delay(TasmotaGlobal.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(TasmotaGlobal.sleep); + } + running_download = false; + // Callback 3: create a footer or simply finish the download with an empty payload + sendFooter(); + // refresh settings for another download + _searchSaves(); + _initBuffer(); + } + + #endif // ESP8266 #endif // USE_FLOG \ No newline at end of file diff --git a/tasmota/support_float.ino b/tasmota/tasmota_support/support_float.ino similarity index 100% rename from tasmota/support_float.ino rename to tasmota/tasmota_support/support_float.ino diff --git a/tasmota/support_jpeg.ino b/tasmota/tasmota_support/support_jpeg.ino similarity index 100% rename from tasmota/support_jpeg.ino rename to tasmota/tasmota_support/support_jpeg.ino diff --git a/tasmota/support_network.ino b/tasmota/tasmota_support/support_network.ino similarity index 100% rename from tasmota/support_network.ino rename to tasmota/tasmota_support/support_network.ino diff --git a/tasmota/support_profiling.ino b/tasmota/tasmota_support/support_profiling.ino similarity index 100% rename from tasmota/support_profiling.ino rename to tasmota/tasmota_support/support_profiling.ino diff --git a/tasmota/support_pwm.ino b/tasmota/tasmota_support/support_pwm.ino similarity index 100% rename from tasmota/support_pwm.ino rename to tasmota/tasmota_support/support_pwm.ino diff --git a/tasmota/support_rotary.ino b/tasmota/tasmota_support/support_rotary.ino similarity index 100% rename from tasmota/support_rotary.ino rename to tasmota/tasmota_support/support_rotary.ino diff --git a/tasmota/support_rtc.ino b/tasmota/tasmota_support/support_rtc.ino similarity index 100% rename from tasmota/support_rtc.ino rename to tasmota/tasmota_support/support_rtc.ino diff --git a/tasmota/support_statistics.ino b/tasmota/tasmota_support/support_statistics.ino similarity index 100% rename from tasmota/support_statistics.ino rename to tasmota/tasmota_support/support_statistics.ino diff --git a/tasmota/support_switch.ino b/tasmota/tasmota_support/support_switch.ino similarity index 100% rename from tasmota/support_switch.ino rename to tasmota/tasmota_support/support_switch.ino diff --git a/tasmota/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino similarity index 100% rename from tasmota/support_tasmota.ino rename to tasmota/tasmota_support/support_tasmota.ino diff --git a/tasmota/support_udp.ino b/tasmota/tasmota_support/support_udp.ino similarity index 100% rename from tasmota/support_udp.ino rename to tasmota/tasmota_support/support_udp.ino diff --git a/tasmota/support_wifi.ino b/tasmota/tasmota_support/support_wifi.ino similarity index 100% rename from tasmota/support_wifi.ino rename to tasmota/tasmota_support/support_wifi.ino diff --git a/tasmota/tasmota_ca.ino b/tasmota/tasmota_support/tasmota_ca.ino similarity index 98% rename from tasmota/tasmota_ca.ino rename to tasmota/tasmota_support/tasmota_ca.ino index 007685863..c34e4ba12 100644 --- a/tasmota/tasmota_ca.ino +++ b/tasmota/tasmota_support/tasmota_ca.ino @@ -20,17 +20,17 @@ // // Certificates are stored in flash (PROGMEM) to avoid consuming valuable RAM. // -// To save space in flash, the Let's Encrypt and Amazon AWS certificates may be -// individually omitted if TLS is enabled, but the certificates are not used. -// This is typically the case when a locally generated root certificate is used +// To save space in flash, the Let's Encrypt and Amazon AWS certificates may be +// individually omitted if TLS is enabled, but the certificates are not used. +// This is typically the case when a locally generated root certificate is used // for TLS authentication. -// -// To omit these certificates, define one or both of the +// +// To omit these certificates, define one or both of the // OMIT_LETS_ENCRYPT_CERT and/or OMIT_AWS_CERT macros in user_config_override.h. // // To include a locally generated root certificate, define the // INCLUDE_LOCAL_CERT macro in user_config_override.h. -// +// // See the files tasmota/local_ca_data_sample.h and tasmota/local_ca_descriptor_sample.h // in the Tasmota source for instructions for generating them from a local root // certificate in .crt format. @@ -145,7 +145,7 @@ static const unsigned char PROGMEM AmazonRootCA1_RSA_E[] = { #endif #if defined(INCLUDE_LOCAL_CERT) -#include +#include #endif // // ========== cumulative CA ================= diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_webserver.ino similarity index 100% rename from tasmota/xdrv_01_webserver.ino rename to tasmota/tasmota_xdrv_driver/xdrv_01_webserver.ino diff --git a/tasmota/xdrv_02_1_mqtt_file.ino b/tasmota/tasmota_xdrv_driver/xdrv_02_1_mqtt_file.ino similarity index 100% rename from tasmota/xdrv_02_1_mqtt_file.ino rename to tasmota/tasmota_xdrv_driver/xdrv_02_1_mqtt_file.ino diff --git a/tasmota/xdrv_02_9_mqtt.ino b/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino similarity index 100% rename from tasmota/xdrv_02_9_mqtt.ino rename to tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino diff --git a/tasmota/xdrv_03_energy.ino b/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino similarity index 100% rename from tasmota/xdrv_03_energy.ino rename to tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino diff --git a/tasmota/xdrv_04_light.ino b/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino similarity index 100% rename from tasmota/xdrv_04_light.ino rename to tasmota/tasmota_xdrv_driver/xdrv_04_light.ino diff --git a/tasmota/xdrv_04_light_utils.ino b/tasmota/tasmota_xdrv_driver/xdrv_04_light_utils.ino similarity index 100% rename from tasmota/xdrv_04_light_utils.ino rename to tasmota/tasmota_xdrv_driver/xdrv_04_light_utils.ino diff --git a/tasmota/xdrv_05_irremote.ino b/tasmota/tasmota_xdrv_driver/xdrv_05_irremote.ino similarity index 100% rename from tasmota/xdrv_05_irremote.ino rename to tasmota/tasmota_xdrv_driver/xdrv_05_irremote.ino diff --git a/tasmota/xdrv_05_irremote_full.ino b/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino similarity index 100% rename from tasmota/xdrv_05_irremote_full.ino rename to tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino diff --git a/tasmota/xdrv_06_snfbridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_06_snfbridge.ino similarity index 100% rename from tasmota/xdrv_06_snfbridge.ino rename to tasmota/tasmota_xdrv_driver/xdrv_06_snfbridge.ino diff --git a/tasmota/xdrv_07_domoticz.ino b/tasmota/tasmota_xdrv_driver/xdrv_07_domoticz.ino similarity index 100% rename from tasmota/xdrv_07_domoticz.ino rename to tasmota/tasmota_xdrv_driver/xdrv_07_domoticz.ino diff --git a/tasmota/xdrv_08_serial_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_08_serial_bridge.ino similarity index 100% rename from tasmota/xdrv_08_serial_bridge.ino rename to tasmota/tasmota_xdrv_driver/xdrv_08_serial_bridge.ino diff --git a/tasmota/xdrv_09_timers.ino b/tasmota/tasmota_xdrv_driver/xdrv_09_timers.ino similarity index 100% rename from tasmota/xdrv_09_timers.ino rename to tasmota/tasmota_xdrv_driver/xdrv_09_timers.ino diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino similarity index 100% rename from tasmota/xdrv_10_rules.ino rename to tasmota/tasmota_xdrv_driver/xdrv_10_rules.ino diff --git a/tasmota/xdrv_10_scripter.ino b/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino old mode 100755 new mode 100644 similarity index 100% rename from tasmota/xdrv_10_scripter.ino rename to tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino diff --git a/tasmota/xdrv_11_knx.ino b/tasmota/tasmota_xdrv_driver/xdrv_11_knx.ino similarity index 100% rename from tasmota/xdrv_11_knx.ino rename to tasmota/tasmota_xdrv_driver/xdrv_11_knx.ino diff --git a/tasmota/xdrv_12_discovery.ino b/tasmota/tasmota_xdrv_driver/xdrv_12_discovery.ino similarity index 100% rename from tasmota/xdrv_12_discovery.ino rename to tasmota/tasmota_xdrv_driver/xdrv_12_discovery.ino diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/tasmota_xdrv_driver/xdrv_12_home_assistant.ino similarity index 100% rename from tasmota/xdrv_12_home_assistant.ino rename to tasmota/tasmota_xdrv_driver/xdrv_12_home_assistant.ino diff --git a/tasmota/xdrv_13_display.ino b/tasmota/tasmota_xdrv_driver/xdrv_13_display.ino old mode 100755 new mode 100644 similarity index 100% rename from tasmota/xdrv_13_display.ino rename to tasmota/tasmota_xdrv_driver/xdrv_13_display.ino diff --git a/tasmota/xdrv_14_mp3.ino b/tasmota/tasmota_xdrv_driver/xdrv_14_mp3.ino similarity index 100% rename from tasmota/xdrv_14_mp3.ino rename to tasmota/tasmota_xdrv_driver/xdrv_14_mp3.ino diff --git a/tasmota/xdrv_15_pca9685.ino b/tasmota/tasmota_xdrv_driver/xdrv_15_pca9685.ino similarity index 100% rename from tasmota/xdrv_15_pca9685.ino rename to tasmota/tasmota_xdrv_driver/xdrv_15_pca9685.ino diff --git a/tasmota/xdrv_16_tuyamcu.ino b/tasmota/tasmota_xdrv_driver/xdrv_16_tuyamcu.ino similarity index 100% rename from tasmota/xdrv_16_tuyamcu.ino rename to tasmota/tasmota_xdrv_driver/xdrv_16_tuyamcu.ino diff --git a/tasmota/xdrv_17_rcswitch.ino b/tasmota/tasmota_xdrv_driver/xdrv_17_rcswitch.ino similarity index 100% rename from tasmota/xdrv_17_rcswitch.ino rename to tasmota/tasmota_xdrv_driver/xdrv_17_rcswitch.ino diff --git a/tasmota/xdrv_18_armtronix_dimmers.ino b/tasmota/tasmota_xdrv_driver/xdrv_18_armtronix_dimmers.ino similarity index 100% rename from tasmota/xdrv_18_armtronix_dimmers.ino rename to tasmota/tasmota_xdrv_driver/xdrv_18_armtronix_dimmers.ino diff --git a/tasmota/xdrv_19_ps16dz_dimmer.ino b/tasmota/tasmota_xdrv_driver/xdrv_19_ps16dz_dimmer.ino similarity index 100% rename from tasmota/xdrv_19_ps16dz_dimmer.ino rename to tasmota/tasmota_xdrv_driver/xdrv_19_ps16dz_dimmer.ino diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/tasmota_xdrv_driver/xdrv_20_hue.ino similarity index 100% rename from tasmota/xdrv_20_hue.ino rename to tasmota/tasmota_xdrv_driver/xdrv_20_hue.ino diff --git a/tasmota/xdrv_21_wemo.ino b/tasmota/tasmota_xdrv_driver/xdrv_21_wemo.ino similarity index 100% rename from tasmota/xdrv_21_wemo.ino rename to tasmota/tasmota_xdrv_driver/xdrv_21_wemo.ino diff --git a/tasmota/xdrv_21_wemo_multi.ino b/tasmota/tasmota_xdrv_driver/xdrv_21_wemo_multi.ino similarity index 100% rename from tasmota/xdrv_21_wemo_multi.ino rename to tasmota/tasmota_xdrv_driver/xdrv_21_wemo_multi.ino diff --git a/tasmota/xdrv_22_sonoff_ifan.ino b/tasmota/tasmota_xdrv_driver/xdrv_22_sonoff_ifan.ino similarity index 100% rename from tasmota/xdrv_22_sonoff_ifan.ino rename to tasmota/tasmota_xdrv_driver/xdrv_22_sonoff_ifan.ino diff --git a/tasmota/xdrv_23_zigbee_0_constants.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_0_constants.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_0_constants.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_0_constants.ino diff --git a/tasmota/xdrv_23_zigbee_1_headers.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1_headers.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_1_headers.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1_headers.ino diff --git a/tasmota/xdrv_23_zigbee_1z_libs.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1z_libs.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_1z_libs.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1z_libs.ino diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_2_devices.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino diff --git a/tasmota/xdrv_23_zigbee_2a_devices_impl.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_2a_devices_impl.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino diff --git a/tasmota/xdrv_23_zigbee_4a_nano_fs.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4a_nano_fs.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_4a_nano_fs.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4a_nano_fs.ino diff --git a/tasmota/xdrv_23_zigbee_4b_data.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4b_data.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_4b_data.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4b_data.ino diff --git a/tasmota/xdrv_23_zigbee_4c_devices.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4c_devices.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_4c_devices.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4c_devices.ino diff --git a/tasmota/xdrv_23_zigbee_5__constants.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5__constants.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_5__constants.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5__constants.ino diff --git a/tasmota/xdrv_23_zigbee_5_converters.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_converters.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_5_converters.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_converters.ino diff --git a/tasmota/xdrv_23_zigbee_6_5_hue.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_5_hue.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_6_5_hue.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_5_hue.ino diff --git a/tasmota/xdrv_23_zigbee_6_commands.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_commands.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_6_commands.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_commands.ino diff --git a/tasmota/xdrv_23_zigbee_7_0_statemachine.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_0_statemachine.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_7_0_statemachine.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_0_statemachine.ino diff --git a/tasmota/xdrv_23_zigbee_7_5_map.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_5_map.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_7_5_map.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_5_map.ino diff --git a/tasmota/xdrv_23_zigbee_8_parsers.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_8_parsers.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino diff --git a/tasmota/xdrv_23_zigbee_9_serial.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_9_serial.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_9_serial.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_9_serial.ino diff --git a/tasmota/xdrv_23_zigbee_9a_upload.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_9a_upload.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_9a_upload.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_9a_upload.ino diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino similarity index 100% rename from tasmota/xdrv_23_zigbee_A_impl.ino rename to tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino diff --git a/tasmota/xdrv_24_buzzer.ino b/tasmota/tasmota_xdrv_driver/xdrv_24_buzzer.ino similarity index 100% rename from tasmota/xdrv_24_buzzer.ino rename to tasmota/tasmota_xdrv_driver/xdrv_24_buzzer.ino diff --git a/tasmota/xdrv_25_A4988_Stepper.ino b/tasmota/tasmota_xdrv_driver/xdrv_25_A4988_Stepper.ino similarity index 100% rename from tasmota/xdrv_25_A4988_Stepper.ino rename to tasmota/tasmota_xdrv_driver/xdrv_25_A4988_Stepper.ino diff --git a/tasmota/xdrv_26_ariluxrf.ino b/tasmota/tasmota_xdrv_driver/xdrv_26_ariluxrf.ino similarity index 100% rename from tasmota/xdrv_26_ariluxrf.ino rename to tasmota/tasmota_xdrv_driver/xdrv_26_ariluxrf.ino diff --git a/tasmota/xdrv_27_shutter.ino b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino similarity index 100% rename from tasmota/xdrv_27_shutter.ino rename to tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino diff --git a/tasmota/xdrv_28_pcf8574.ino b/tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574.ino similarity index 100% rename from tasmota/xdrv_28_pcf8574.ino rename to tasmota/tasmota_xdrv_driver/xdrv_28_pcf8574.ino diff --git a/tasmota/xdrv_29_deepsleep.ino b/tasmota/tasmota_xdrv_driver/xdrv_29_deepsleep.ino similarity index 100% rename from tasmota/xdrv_29_deepsleep.ino rename to tasmota/tasmota_xdrv_driver/xdrv_29_deepsleep.ino diff --git a/tasmota/xdrv_30_exs_dimmer.ino b/tasmota/tasmota_xdrv_driver/xdrv_30_exs_dimmer.ino similarity index 100% rename from tasmota/xdrv_30_exs_dimmer.ino rename to tasmota/tasmota_xdrv_driver/xdrv_30_exs_dimmer.ino diff --git a/tasmota/xdrv_31_tasmota_client.ino b/tasmota/tasmota_xdrv_driver/xdrv_31_tasmota_client.ino similarity index 100% rename from tasmota/xdrv_31_tasmota_client.ino rename to tasmota/tasmota_xdrv_driver/xdrv_31_tasmota_client.ino diff --git a/tasmota/xdrv_32_hotplug.ino b/tasmota/tasmota_xdrv_driver/xdrv_32_hotplug.ino similarity index 100% rename from tasmota/xdrv_32_hotplug.ino rename to tasmota/tasmota_xdrv_driver/xdrv_32_hotplug.ino diff --git a/tasmota/xdrv_33_nrf24l01.ino b/tasmota/tasmota_xdrv_driver/xdrv_33_nrf24l01.ino similarity index 100% rename from tasmota/xdrv_33_nrf24l01.ino rename to tasmota/tasmota_xdrv_driver/xdrv_33_nrf24l01.ino diff --git a/tasmota/xdrv_34_wemos_motor_v1.ino b/tasmota/tasmota_xdrv_driver/xdrv_34_wemos_motor_v1.ino similarity index 100% rename from tasmota/xdrv_34_wemos_motor_v1.ino rename to tasmota/tasmota_xdrv_driver/xdrv_34_wemos_motor_v1.ino diff --git a/tasmota/xdrv_35_pwm_dimmer.ino b/tasmota/tasmota_xdrv_driver/xdrv_35_pwm_dimmer.ino similarity index 100% rename from tasmota/xdrv_35_pwm_dimmer.ino rename to tasmota/tasmota_xdrv_driver/xdrv_35_pwm_dimmer.ino diff --git a/tasmota/xdrv_36_keeloq.ino b/tasmota/tasmota_xdrv_driver/xdrv_36_keeloq.ino similarity index 100% rename from tasmota/xdrv_36_keeloq.ino rename to tasmota/tasmota_xdrv_driver/xdrv_36_keeloq.ino diff --git a/tasmota/xdrv_37_sonoff_d1.ino b/tasmota/tasmota_xdrv_driver/xdrv_37_sonoff_d1.ino similarity index 100% rename from tasmota/xdrv_37_sonoff_d1.ino rename to tasmota/tasmota_xdrv_driver/xdrv_37_sonoff_d1.ino diff --git a/tasmota/xdrv_38_ping.ino b/tasmota/tasmota_xdrv_driver/xdrv_38_ping.ino similarity index 100% rename from tasmota/xdrv_38_ping.ino rename to tasmota/tasmota_xdrv_driver/xdrv_38_ping.ino diff --git a/tasmota/xdrv_39_thermostat.ino b/tasmota/tasmota_xdrv_driver/xdrv_39_thermostat.ino similarity index 100% rename from tasmota/xdrv_39_thermostat.ino rename to tasmota/tasmota_xdrv_driver/xdrv_39_thermostat.ino diff --git a/tasmota/xdrv_40_telegram.ino b/tasmota/tasmota_xdrv_driver/xdrv_40_telegram.ino similarity index 100% rename from tasmota/xdrv_40_telegram.ino rename to tasmota/tasmota_xdrv_driver/xdrv_40_telegram.ino diff --git a/tasmota/xdrv_41_tcp_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_41_tcp_bridge.ino similarity index 100% rename from tasmota/xdrv_41_tcp_bridge.ino rename to tasmota/tasmota_xdrv_driver/xdrv_41_tcp_bridge.ino diff --git a/tasmota/xdrv_42_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino similarity index 100% rename from tasmota/xdrv_42_i2s_audio.ino rename to tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino diff --git a/tasmota/xdrv_43_mlx90640.ino b/tasmota/tasmota_xdrv_driver/xdrv_43_mlx90640.ino similarity index 100% rename from tasmota/xdrv_43_mlx90640.ino rename to tasmota/tasmota_xdrv_driver/xdrv_43_mlx90640.ino diff --git a/tasmota/xdrv_44_miel_hvac.ino b/tasmota/tasmota_xdrv_driver/xdrv_44_miel_hvac.ino similarity index 100% rename from tasmota/xdrv_44_miel_hvac.ino rename to tasmota/tasmota_xdrv_driver/xdrv_44_miel_hvac.ino diff --git a/tasmota/xdrv_45_shelly_dimmer.ino b/tasmota/tasmota_xdrv_driver/xdrv_45_shelly_dimmer.ino similarity index 100% rename from tasmota/xdrv_45_shelly_dimmer.ino rename to tasmota/tasmota_xdrv_driver/xdrv_45_shelly_dimmer.ino diff --git a/tasmota/xdrv_46_ccloader.ino b/tasmota/tasmota_xdrv_driver/xdrv_46_ccloader.ino similarity index 100% rename from tasmota/xdrv_46_ccloader.ino rename to tasmota/tasmota_xdrv_driver/xdrv_46_ccloader.ino diff --git a/tasmota/xdrv_47_ftc532.ino b/tasmota/tasmota_xdrv_driver/xdrv_47_ftc532.ino similarity index 100% rename from tasmota/xdrv_47_ftc532.ino rename to tasmota/tasmota_xdrv_driver/xdrv_47_ftc532.ino diff --git a/tasmota/xdrv_48_timeprop.ino b/tasmota/tasmota_xdrv_driver/xdrv_48_timeprop.ino similarity index 100% rename from tasmota/xdrv_48_timeprop.ino rename to tasmota/tasmota_xdrv_driver/xdrv_48_timeprop.ino diff --git a/tasmota/xdrv_49_pid.ino b/tasmota/tasmota_xdrv_driver/xdrv_49_pid.ino similarity index 100% rename from tasmota/xdrv_49_pid.ino rename to tasmota/tasmota_xdrv_driver/xdrv_49_pid.ino diff --git a/tasmota/xdrv_50_filesystem.ino b/tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino similarity index 100% rename from tasmota/xdrv_50_filesystem.ino rename to tasmota/tasmota_xdrv_driver/xdrv_50_filesystem.ino diff --git a/tasmota/xdrv_51_bs814a2.ino b/tasmota/tasmota_xdrv_driver/xdrv_51_bs814a2.ino similarity index 100% rename from tasmota/xdrv_51_bs814a2.ino rename to tasmota/tasmota_xdrv_driver/xdrv_51_bs814a2.ino diff --git a/tasmota/xdrv_52_0_berry_struct.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_0_berry_struct.ino similarity index 100% rename from tasmota/xdrv_52_0_berry_struct.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_0_berry_struct.ino diff --git a/tasmota/xdrv_52_1_berry_native.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_1_berry_native.ino similarity index 100% rename from tasmota/xdrv_52_1_berry_native.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_1_berry_native.ino diff --git a/tasmota/xdrv_52_3_berry_MI32.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_MI32.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_MI32.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_MI32.ino diff --git a/tasmota/xdrv_52_3_berry_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_audio.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_audio.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_audio.ino diff --git a/tasmota/xdrv_52_3_berry_crypto.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_crypto.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_crypto.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_crypto.ino diff --git a/tasmota/xdrv_52_3_berry_display.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_display.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_display.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_display.ino diff --git a/tasmota/xdrv_52_3_berry_flash.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_flash.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_flash.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_flash.ino diff --git a/tasmota/xdrv_52_3_berry_gpio.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_gpio.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino diff --git a/tasmota/xdrv_52_3_berry_hue.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_hue.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_hue.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_hue.ino diff --git a/tasmota/xdrv_52_3_berry_leds.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_leds.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino diff --git a/tasmota/xdrv_52_3_berry_light.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_light.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_light.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_light.ino diff --git a/tasmota/xdrv_52_3_berry_light_state.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_light_state.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_light_state.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_light_state.ino diff --git a/tasmota/xdrv_52_3_berry_lvgl.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_lvgl.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_lvgl.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_lvgl.ino diff --git a/tasmota/xdrv_52_3_berry_mqtt.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_mqtt.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_mqtt.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_mqtt.ino diff --git a/tasmota/xdrv_52_3_berry_onewire.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_onewire.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_onewire.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_onewire.ino diff --git a/tasmota/xdrv_52_3_berry_serial.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_serial.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_serial.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_serial.ino diff --git a/tasmota/xdrv_52_3_berry_tasmota.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_tasmota.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino diff --git a/tasmota/xdrv_52_3_berry_tasmota_global.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota_global.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_tasmota_global.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota_global.ino diff --git a/tasmota/xdrv_52_3_berry_webclient.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_webclient.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_webclient.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_webclient.ino diff --git a/tasmota/xdrv_52_3_berry_webserver.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_webserver.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_webserver.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_webserver.ino diff --git a/tasmota/xdrv_52_3_berry_wire.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_wire.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_wire.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_wire.ino diff --git a/tasmota/xdrv_52_3_berry_zigbee.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_zigbee.ino similarity index 100% rename from tasmota/xdrv_52_3_berry_zigbee.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_zigbee.ino diff --git a/tasmota/xdrv_52_7_berry_embedded.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_7_berry_embedded.ino similarity index 100% rename from tasmota/xdrv_52_7_berry_embedded.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_7_berry_embedded.ino diff --git a/tasmota/xdrv_52_9_berry.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_9_berry.ino similarity index 100% rename from tasmota/xdrv_52_9_berry.ino rename to tasmota/tasmota_xdrv_driver/xdrv_52_9_berry.ino diff --git a/tasmota/xdrv_53_projector_ctrl.ino b/tasmota/tasmota_xdrv_driver/xdrv_53_projector_ctrl.ino similarity index 99% rename from tasmota/xdrv_53_projector_ctrl.ino rename to tasmota/tasmota_xdrv_driver/xdrv_53_projector_ctrl.ino index 1fd8f8446..be0af2e5a 100644 --- a/tasmota/xdrv_53_projector_ctrl.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_53_projector_ctrl.ino @@ -72,7 +72,7 @@ struct projector_ctrl_command_info_s { const uint8_t fail_value_bytes; } __packed; -#include "xdrv_53_projector_ctrl.h" +#include "include/xdrv_53_projector_ctrl.h" struct projector_ctrl_softc_s { TasmotaSerial *sc_serial; diff --git a/tasmota/xdrv_54_lvgl.ino b/tasmota/tasmota_xdrv_driver/xdrv_54_lvgl.ino similarity index 100% rename from tasmota/xdrv_54_lvgl.ino rename to tasmota/tasmota_xdrv_driver/xdrv_54_lvgl.ino diff --git a/tasmota/xdrv_55_touch.ino b/tasmota/tasmota_xdrv_driver/xdrv_55_touch.ino similarity index 100% rename from tasmota/xdrv_55_touch.ino rename to tasmota/tasmota_xdrv_driver/xdrv_55_touch.ino diff --git a/tasmota/xdrv_56_rtc_chips.ino b/tasmota/tasmota_xdrv_driver/xdrv_56_rtc_chips.ino similarity index 100% rename from tasmota/xdrv_56_rtc_chips.ino rename to tasmota/tasmota_xdrv_driver/xdrv_56_rtc_chips.ino diff --git a/tasmota/xdrv_57_1_tasmesh_support.ino b/tasmota/tasmota_xdrv_driver/xdrv_57_1_tasmesh_support.ino similarity index 100% rename from tasmota/xdrv_57_1_tasmesh_support.ino rename to tasmota/tasmota_xdrv_driver/xdrv_57_1_tasmesh_support.ino diff --git a/tasmota/xdrv_57_9_tasmesh.ino b/tasmota/tasmota_xdrv_driver/xdrv_57_9_tasmesh.ino similarity index 100% rename from tasmota/xdrv_57_9_tasmesh.ino rename to tasmota/tasmota_xdrv_driver/xdrv_57_9_tasmesh.ino diff --git a/tasmota/xdrv_58_range_extender.ino b/tasmota/tasmota_xdrv_driver/xdrv_58_range_extender.ino similarity index 100% rename from tasmota/xdrv_58_range_extender.ino rename to tasmota/tasmota_xdrv_driver/xdrv_58_range_extender.ino diff --git a/tasmota/xdrv_59_influxdb.ino b/tasmota/tasmota_xdrv_driver/xdrv_59_influxdb.ino similarity index 100% rename from tasmota/xdrv_59_influxdb.ino rename to tasmota/tasmota_xdrv_driver/xdrv_59_influxdb.ino diff --git a/tasmota/xdrv_60_shift595.ino b/tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino similarity index 100% rename from tasmota/xdrv_60_shift595.ino rename to tasmota/tasmota_xdrv_driver/xdrv_60_shift595.ino diff --git a/tasmota/xdrv_61_ds3502.ino b/tasmota/tasmota_xdrv_driver/xdrv_61_ds3502.ino similarity index 100% rename from tasmota/xdrv_61_ds3502.ino rename to tasmota/tasmota_xdrv_driver/xdrv_61_ds3502.ino diff --git a/tasmota/xdrv_62_improv.ino b/tasmota/tasmota_xdrv_driver/xdrv_62_improv.ino similarity index 100% rename from tasmota/xdrv_62_improv.ino rename to tasmota/tasmota_xdrv_driver/xdrv_62_improv.ino diff --git a/tasmota/xdrv_79_esp32_ble.ino b/tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino similarity index 100% rename from tasmota/xdrv_79_esp32_ble.ino rename to tasmota/tasmota_xdrv_driver/xdrv_79_esp32_ble.ino diff --git a/tasmota/xdrv_81_esp32_webcam.ino b/tasmota/tasmota_xdrv_driver/xdrv_81_esp32_webcam.ino similarity index 100% rename from tasmota/xdrv_81_esp32_webcam.ino rename to tasmota/tasmota_xdrv_driver/xdrv_81_esp32_webcam.ino diff --git a/tasmota/xdrv_82_esp32_ethernet.ino b/tasmota/tasmota_xdrv_driver/xdrv_82_esp32_ethernet.ino similarity index 100% rename from tasmota/xdrv_82_esp32_ethernet.ino rename to tasmota/tasmota_xdrv_driver/xdrv_82_esp32_ethernet.ino diff --git a/tasmota/xdrv_83_esp32_watch.ino b/tasmota/tasmota_xdrv_driver/xdrv_83_esp32_watch.ino similarity index 100% rename from tasmota/xdrv_83_esp32_watch.ino rename to tasmota/tasmota_xdrv_driver/xdrv_83_esp32_watch.ino diff --git a/tasmota/xdrv_85_esp32_ble_eq3_trv.ino b/tasmota/tasmota_xdrv_driver/xdrv_85_esp32_ble_eq3_trv.ino similarity index 100% rename from tasmota/xdrv_85_esp32_ble_eq3_trv.ino rename to tasmota/tasmota_xdrv_driver/xdrv_85_esp32_ble_eq3_trv.ino diff --git a/tasmota/xdrv_86_esp32_sonoff_spm.ino b/tasmota/tasmota_xdrv_driver/xdrv_86_esp32_sonoff_spm.ino similarity index 100% rename from tasmota/xdrv_86_esp32_sonoff_spm.ino rename to tasmota/tasmota_xdrv_driver/xdrv_86_esp32_sonoff_spm.ino diff --git a/tasmota/xdrv_98_file_settings_demo.ino b/tasmota/tasmota_xdrv_driver/xdrv_98_file_settings_demo.ino similarity index 100% rename from tasmota/xdrv_98_file_settings_demo.ino rename to tasmota/tasmota_xdrv_driver/xdrv_98_file_settings_demo.ino diff --git a/tasmota/xdrv_99_debug.ino b/tasmota/tasmota_xdrv_driver/xdrv_99_debug.ino similarity index 100% rename from tasmota/xdrv_99_debug.ino rename to tasmota/tasmota_xdrv_driver/xdrv_99_debug.ino diff --git a/tasmota/xdrv_interface.ino b/tasmota/tasmota_xdrv_driver/xdrv_interface.ino similarity index 100% rename from tasmota/xdrv_interface.ino rename to tasmota/tasmota_xdrv_driver/xdrv_interface.ino diff --git a/tasmota/xdsp_01_lcd.ino b/tasmota/tasmota_xdsp_display/xdsp_01_lcd.ino similarity index 100% rename from tasmota/xdsp_01_lcd.ino rename to tasmota/tasmota_xdsp_display/xdsp_01_lcd.ino diff --git a/tasmota/xdsp_02_ssd1306.ino b/tasmota/tasmota_xdsp_display/xdsp_02_ssd1306.ino similarity index 100% rename from tasmota/xdsp_02_ssd1306.ino rename to tasmota/tasmota_xdsp_display/xdsp_02_ssd1306.ino diff --git a/tasmota/xdsp_03_matrix.ino b/tasmota/tasmota_xdsp_display/xdsp_03_matrix.ino similarity index 100% rename from tasmota/xdsp_03_matrix.ino rename to tasmota/tasmota_xdsp_display/xdsp_03_matrix.ino diff --git a/tasmota/xdsp_04_ili9341.ino b/tasmota/tasmota_xdsp_display/xdsp_04_ili9341.ino old mode 100755 new mode 100644 similarity index 100% rename from tasmota/xdsp_04_ili9341.ino rename to tasmota/tasmota_xdsp_display/xdsp_04_ili9341.ino diff --git a/tasmota/xdsp_05_epaper_29.ino b/tasmota/tasmota_xdsp_display/xdsp_05_epaper_29.ino similarity index 100% rename from tasmota/xdsp_05_epaper_29.ino rename to tasmota/tasmota_xdsp_display/xdsp_05_epaper_29.ino diff --git a/tasmota/xdsp_06_epaper_42.ino b/tasmota/tasmota_xdsp_display/xdsp_06_epaper_42.ino similarity index 100% rename from tasmota/xdsp_06_epaper_42.ino rename to tasmota/tasmota_xdsp_display/xdsp_06_epaper_42.ino diff --git a/tasmota/xdsp_07_sh1106.ino b/tasmota/tasmota_xdsp_display/xdsp_07_sh1106.ino similarity index 100% rename from tasmota/xdsp_07_sh1106.ino rename to tasmota/tasmota_xdsp_display/xdsp_07_sh1106.ino diff --git a/tasmota/xdsp_09_SSD1351.ino b/tasmota/tasmota_xdsp_display/xdsp_09_SSD1351.ino similarity index 100% rename from tasmota/xdsp_09_SSD1351.ino rename to tasmota/tasmota_xdsp_display/xdsp_09_SSD1351.ino diff --git a/tasmota/xdsp_10_RA8876.ino b/tasmota/tasmota_xdsp_display/xdsp_10_RA8876.ino similarity index 100% rename from tasmota/xdsp_10_RA8876.ino rename to tasmota/tasmota_xdsp_display/xdsp_10_RA8876.ino diff --git a/tasmota/xdsp_11_sevenseg.ino b/tasmota/tasmota_xdsp_display/xdsp_11_sevenseg.ino similarity index 100% rename from tasmota/xdsp_11_sevenseg.ino rename to tasmota/tasmota_xdsp_display/xdsp_11_sevenseg.ino diff --git a/tasmota/xdsp_12_ST7789.ino b/tasmota/tasmota_xdsp_display/xdsp_12_ST7789.ino similarity index 100% rename from tasmota/xdsp_12_ST7789.ino rename to tasmota/tasmota_xdsp_display/xdsp_12_ST7789.ino diff --git a/tasmota/xdsp_14_SSD1331.ino b/tasmota/tasmota_xdsp_display/xdsp_14_SSD1331.ino similarity index 100% rename from tasmota/xdsp_14_SSD1331.ino rename to tasmota/tasmota_xdsp_display/xdsp_14_SSD1331.ino diff --git a/tasmota/xdsp_15_tm1637.ino b/tasmota/tasmota_xdsp_display/xdsp_15_tm1637.ino similarity index 100% rename from tasmota/xdsp_15_tm1637.ino rename to tasmota/tasmota_xdsp_display/xdsp_15_tm1637.ino diff --git a/tasmota/xdsp_16_esp32_epaper_47.ino b/tasmota/tasmota_xdsp_display/xdsp_16_esp32_epaper_47.ino similarity index 100% rename from tasmota/xdsp_16_esp32_epaper_47.ino rename to tasmota/tasmota_xdsp_display/xdsp_16_esp32_epaper_47.ino diff --git a/tasmota/xdsp_17_universal.ino b/tasmota/tasmota_xdsp_display/xdsp_17_universal.ino similarity index 100% rename from tasmota/xdsp_17_universal.ino rename to tasmota/tasmota_xdsp_display/xdsp_17_universal.ino diff --git a/tasmota/xdsp_18_berry_display.ino b/tasmota/tasmota_xdsp_display/xdsp_18_berry_display.ino similarity index 100% rename from tasmota/xdsp_18_berry_display.ino rename to tasmota/tasmota_xdsp_display/xdsp_18_berry_display.ino diff --git a/tasmota/xdsp_19_max7219_matrix.ino b/tasmota/tasmota_xdsp_display/xdsp_19_max7219_matrix.ino similarity index 100% rename from tasmota/xdsp_19_max7219_matrix.ino rename to tasmota/tasmota_xdsp_display/xdsp_19_max7219_matrix.ino diff --git a/tasmota/xdsp_interface.ino b/tasmota/tasmota_xdsp_display/xdsp_interface.ino similarity index 100% rename from tasmota/xdsp_interface.ino rename to tasmota/tasmota_xdsp_display/xdsp_interface.ino diff --git a/tasmota/xlgt_01_ws2812.ino b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino similarity index 100% rename from tasmota/xlgt_01_ws2812.ino rename to tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino diff --git a/tasmota/xlgt_02_my92x1.ino b/tasmota/tasmota_xlgt_light/xlgt_02_my92x1.ino similarity index 100% rename from tasmota/xlgt_02_my92x1.ino rename to tasmota/tasmota_xlgt_light/xlgt_02_my92x1.ino diff --git a/tasmota/xlgt_03_sm16716.ino b/tasmota/tasmota_xlgt_light/xlgt_03_sm16716.ino similarity index 100% rename from tasmota/xlgt_03_sm16716.ino rename to tasmota/tasmota_xlgt_light/xlgt_03_sm16716.ino diff --git a/tasmota/xlgt_04_sm2135.ino b/tasmota/tasmota_xlgt_light/xlgt_04_sm2135.ino similarity index 100% rename from tasmota/xlgt_04_sm2135.ino rename to tasmota/tasmota_xlgt_light/xlgt_04_sm2135.ino diff --git a/tasmota/xlgt_05_sonoff_l1.ino b/tasmota/tasmota_xlgt_light/xlgt_05_sonoff_l1.ino similarity index 100% rename from tasmota/xlgt_05_sonoff_l1.ino rename to tasmota/tasmota_xlgt_light/xlgt_05_sonoff_l1.ino diff --git a/tasmota/xlgt_06_electriq_moodl.ino b/tasmota/tasmota_xlgt_light/xlgt_06_electriq_moodl.ino similarity index 100% rename from tasmota/xlgt_06_electriq_moodl.ino rename to tasmota/tasmota_xlgt_light/xlgt_06_electriq_moodl.ino diff --git a/tasmota/xlgt_07_lsc_mcsl.ino b/tasmota/tasmota_xlgt_light/xlgt_07_lsc_mcsl.ino similarity index 100% rename from tasmota/xlgt_07_lsc_mcsl.ino rename to tasmota/tasmota_xlgt_light/xlgt_07_lsc_mcsl.ino diff --git a/tasmota/xlgt_08_bp5758d.ino b/tasmota/tasmota_xlgt_light/xlgt_08_bp5758d.ino similarity index 100% rename from tasmota/xlgt_08_bp5758d.ino rename to tasmota/tasmota_xlgt_light/xlgt_08_bp5758d.ino diff --git a/tasmota/xlgt_interface.ino b/tasmota/tasmota_xlgt_light/xlgt_interface.ino similarity index 100% rename from tasmota/xlgt_interface.ino rename to tasmota/tasmota_xlgt_light/xlgt_interface.ino diff --git a/tasmota/xnrg_01_hlw8012.ino b/tasmota/tasmota_xnrg_energy/xnrg_01_hlw8012.ino similarity index 100% rename from tasmota/xnrg_01_hlw8012.ino rename to tasmota/tasmota_xnrg_energy/xnrg_01_hlw8012.ino diff --git a/tasmota/xnrg_02_cse7766.ino b/tasmota/tasmota_xnrg_energy/xnrg_02_cse7766.ino similarity index 100% rename from tasmota/xnrg_02_cse7766.ino rename to tasmota/tasmota_xnrg_energy/xnrg_02_cse7766.ino diff --git a/tasmota/xnrg_03_pzem004t.ino b/tasmota/tasmota_xnrg_energy/xnrg_03_pzem004t.ino similarity index 100% rename from tasmota/xnrg_03_pzem004t.ino rename to tasmota/tasmota_xnrg_energy/xnrg_03_pzem004t.ino diff --git a/tasmota/xnrg_04_mcp39f501.ino b/tasmota/tasmota_xnrg_energy/xnrg_04_mcp39f501.ino similarity index 100% rename from tasmota/xnrg_04_mcp39f501.ino rename to tasmota/tasmota_xnrg_energy/xnrg_04_mcp39f501.ino diff --git a/tasmota/xnrg_05_pzem_ac.ino b/tasmota/tasmota_xnrg_energy/xnrg_05_pzem_ac.ino similarity index 100% rename from tasmota/xnrg_05_pzem_ac.ino rename to tasmota/tasmota_xnrg_energy/xnrg_05_pzem_ac.ino diff --git a/tasmota/xnrg_06_pzem_dc.ino b/tasmota/tasmota_xnrg_energy/xnrg_06_pzem_dc.ino similarity index 100% rename from tasmota/xnrg_06_pzem_dc.ino rename to tasmota/tasmota_xnrg_energy/xnrg_06_pzem_dc.ino diff --git a/tasmota/xnrg_07_ade7953.ino b/tasmota/tasmota_xnrg_energy/xnrg_07_ade7953.ino similarity index 100% rename from tasmota/xnrg_07_ade7953.ino rename to tasmota/tasmota_xnrg_energy/xnrg_07_ade7953.ino diff --git a/tasmota/xnrg_08_sdm120.ino b/tasmota/tasmota_xnrg_energy/xnrg_08_sdm120.ino similarity index 100% rename from tasmota/xnrg_08_sdm120.ino rename to tasmota/tasmota_xnrg_energy/xnrg_08_sdm120.ino diff --git a/tasmota/xnrg_09_dds2382.ino b/tasmota/tasmota_xnrg_energy/xnrg_09_dds2382.ino similarity index 100% rename from tasmota/xnrg_09_dds2382.ino rename to tasmota/tasmota_xnrg_energy/xnrg_09_dds2382.ino diff --git a/tasmota/xnrg_10_sdm630.ino b/tasmota/tasmota_xnrg_energy/xnrg_10_sdm630.ino similarity index 100% rename from tasmota/xnrg_10_sdm630.ino rename to tasmota/tasmota_xnrg_energy/xnrg_10_sdm630.ino diff --git a/tasmota/xnrg_11_ddsu666.ino b/tasmota/tasmota_xnrg_energy/xnrg_11_ddsu666.ino similarity index 100% rename from tasmota/xnrg_11_ddsu666.ino rename to tasmota/tasmota_xnrg_energy/xnrg_11_ddsu666.ino diff --git a/tasmota/xnrg_12_solaxX1.ino b/tasmota/tasmota_xnrg_energy/xnrg_12_solaxX1.ino similarity index 97% rename from tasmota/xnrg_12_solaxX1.ino rename to tasmota/tasmota_xnrg_energy/xnrg_12_solaxX1.ino index 6bad53255..f454b1c06 100644 --- a/tasmota/xnrg_12_solaxX1.ino +++ b/tasmota/tasmota_xnrg_energy/xnrg_12_solaxX1.ino @@ -1,615 +1,615 @@ -/* - xnrg_12_solaxX1.ino - Solax X1 inverter RS485 support for Tasmota - - Copyright (C) 2021 by Pablo Zerón - Copyright (C) 2022 by Stefan Wershoven - - 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_ENERGY_SENSOR -#ifdef USE_SOLAX_X1 -/*********************************************************************************************\ - * Solax X1 Inverter -\*********************************************************************************************/ - -#define XNRG_12 12 - -#ifndef SOLAXX1_SPEED -#define SOLAXX1_SPEED 9600 // default solax rs485 speed -#endif - -#define SOLAXX1_READCONFIG // enable to read inverters config; disable to save codespace (3k1) - -#define INVERTER_ADDRESS 0x0A - -#define D_SOLAX_X1 "SolaxX1" - -#include - -const char kSolaxMode[] PROGMEM = - D_OFF "|" D_SOLAX_MODE_0 "|" D_SOLAX_MODE_1 "|" D_SOLAX_MODE_2 "|" D_SOLAX_MODE_3 "|" D_SOLAX_MODE_4 "|" - D_SOLAX_MODE_5 "|" D_SOLAX_MODE_6; - -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; - -#ifdef SOLAXX1_READCONFIG -const char kSolaxSafetyType[] PROGMEM = - "VDE0126|ARN4105|AS4777_AU|G98/1|C10/11|OVE/ONORME8001|EN50438_NL|EN50438_DK|CEB|CEI021|NRS097_2_1|" - "VDE0126_Gr_Is|UTE_C15_712|IEC61727|G99/1|VDE0126_Gr_Co|France_VFR2014|C15_712_is_50|C15_712_is_60|" - "AS4777_NZ|RD1699|Chile|EN50438_Ireland|Philippines|Czech_PPDS|Czech_50438"; -#endif // SOLAXX1_READCONFIG - -union { - uint32_t ErrMessage; - struct { - //BYTE0 - uint8_t TzProtectFault:1;//0 - uint8_t MainsLostFault:1;//1 - uint8_t GridVoltFault:1;//2 - uint8_t GridFreqFault:1;//3 - uint8_t PLLLostFault:1;//4 - uint8_t BusVoltFault:1;//5 - uint8_t ErrBit06:1;//6 - uint8_t OciFault:1;//7 - //BYTE1 - uint8_t Dci_OCP_Fault:1;//8 - uint8_t ResidualCurrentFault:1;//9 - uint8_t PvVoltFault:1;//10 - uint8_t Ac10Mins_Voltage_Fault:1;//11 - uint8_t IsolationFault:1;//12 - uint8_t TemperatureOverFault:1;//13 - uint8_t FanFault:1;//14 - uint8_t ErrBit15:1;//15 - //BYTE2 - uint8_t SpiCommsFault:1;//16 - uint8_t SciCommsFault:1;//17 - uint8_t ErrBit18:1;//18 - uint8_t InputConfigFault:1;//19 - uint8_t EepromFault:1;//20 - uint8_t RelayFault:1;//21 - uint8_t SampleConsistenceFault:1;//22 - uint8_t ResidualCurrent_DeviceFault:1;//23 - //BYTE3 - uint8_t ErrBit24:1;//24 - uint8_t ErrBit25:1;//25 - uint8_t ErrBit26:1;//26 - uint8_t ErrBit27:1;//27 - uint8_t ErrBit28:1;//28 - uint8_t DCI_DeviceFault:1;//29 - uint8_t OtherDeviceFault:1;//30 - uint8_t ErrBit31:1;//31 - }; -} solaxX1_ErrCode; - -struct SOLAXX1_LIVEDATA { - int16_t temperature = 0; - float energy_today = 0; - float dc1_voltage = 0; - float dc2_voltage = 0; - float dc1_current = 0; - float dc2_current = 0; - uint32_t runtime_total = 0; - float dc1_power = 0; - float dc2_power = 0; - int16_t runMode = 0; - uint32_t errorCode = 0; - uint8_t SerialNumber[16] = {0x6e, 0x2f, 0x61}; // "n/a" -} solaxX1; - -struct SOLAXX1_GLOBALDATA { - bool AddressAssigned = true; - uint8_t SendRetry_count = 20; - uint8_t QueryData_count = 0; - uint8_t QueryID_count = 240; - bool Command_QueryID = false;; - bool Command_QueryConfig = false; -} solaxX1_global; - -struct SOLAXX1_SENDDATA { - 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 Payload[16] = {0}; -} solaxX1_SendData; - -TasmotaSerial *solaxX1Serial; - -/*********************************************************************************************/ - -void solaxX1_RS485Send(void) -{ - uint8_t message[30]; - memcpy(message, solaxX1_SendData.Header, 2); - memcpy(message + 2, solaxX1_SendData.Source, 2); - memcpy(message + 4, solaxX1_SendData.Destination, 2); - memcpy(message + 6, solaxX1_SendData.ControlCode, 1); - memcpy(message + 7, solaxX1_SendData.FunctionCode, 1); - memcpy(message + 8, solaxX1_SendData.DataLength, 1); - memcpy(message + 9, solaxX1_SendData.Payload, sizeof(solaxX1_SendData.Payload)); - uint16_t crc = solaxX1_calculateCRC(message, 9 + solaxX1_SendData.DataLength[0]); // calculate out crc bytes - while (solaxX1Serial->available() > 0) { // read serial if any old data is available - solaxX1Serial->read(); - } - if (PinUsed(GPIO_SOLAXX1_RTS)) { - digitalWrite(Pin(GPIO_SOLAXX1_RTS), HIGH); - } - solaxX1Serial->flush(); - solaxX1Serial->write(message, 9 + solaxX1_SendData.DataLength[0]); - solaxX1Serial->write(highByte(crc)); - solaxX1Serial->write(lowByte(crc)); - solaxX1Serial->flush(); - if (PinUsed(GPIO_SOLAXX1_RTS)) { - digitalWrite(Pin(GPIO_SOLAXX1_RTS), LOW); - } - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, 9 + solaxX1_SendData.DataLength[0]); -} - -bool solaxX1_RS485Receive(uint8_t *ReadBuffer) -{ - uint8_t len = 0; - while (solaxX1Serial->available() > 0) { - ReadBuffer[len++] = (uint8_t)solaxX1Serial->read(); - } - AddLogBuffer(LOG_LEVEL_DEBUG_MORE, ReadBuffer, len); - uint16_t crc = solaxX1_calculateCRC(ReadBuffer, len - 2); // calculate out crc bytes - return !(ReadBuffer[len - 1] == lowByte(crc) && ReadBuffer[len - 2] == highByte(crc)); -} - -uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) -{ - uint8_t i; - uint16_t wChkSum = 0; - for (i = 0; i < bLen; i++) { - wChkSum = wChkSum + bExternTxPackage[i]; - } - return wChkSum; -} - -void solaxX1_ExtractText(uint8_t *DataIn, uint8_t *DataOut, uint8_t Begin, uint8_t End) -{ - uint8_t i; - for (i = Begin; i <= End; i++) { - DataOut[i - Begin] = DataIn[i]; - } - DataOut[End - Begin + 1] = 0; -} - -void solaxX1_QueryOfflineInverters(void) -{ - solaxX1_SendData.Source[0] = 0x01; - solaxX1_SendData.Destination[0] = 0x00; - solaxX1_SendData.Destination[1] = 0x00; - solaxX1_SendData.ControlCode[0] = 0x10; - solaxX1_SendData.FunctionCode[0] = 0x00; - solaxX1_SendData.DataLength[0] = 0x00; - solaxX1_RS485Send(); -} - -void solaxX1_SendInverterAddress(void) -{ - solaxX1_SendData.Source[0] = 0x00; - solaxX1_SendData.Destination[0] = 0x00; - solaxX1_SendData.Destination[1] = 0x00; - solaxX1_SendData.ControlCode[0] = 0x10; - solaxX1_SendData.FunctionCode[0] = 0x01; - solaxX1_SendData.DataLength[0] = 0x0F; - solaxX1_SendData.Payload[14] = INVERTER_ADDRESS; // Inverter Address, It must be unique in case of more inverters in the same rs485 net. - solaxX1_RS485Send(); -} - -void solaxX1_QueryLiveData(void) -{ - solaxX1_SendData.Source[0] = 0x01; - solaxX1_SendData.Destination[0] = 0x00; - solaxX1_SendData.Destination[1] = INVERTER_ADDRESS; - solaxX1_SendData.ControlCode[0] = 0x11; - solaxX1_SendData.FunctionCode[0] = 0x02; - solaxX1_SendData.DataLength[0] = 0x00; - solaxX1_RS485Send(); -} - -void solaxX1_QueryIDData(void) -{ - solaxX1_SendData.Source[0] = 0x01; - solaxX1_SendData.Destination[0] = 0x00; - solaxX1_SendData.Destination[1] = INVERTER_ADDRESS; - solaxX1_SendData.ControlCode[0] = 0x11; - solaxX1_SendData.FunctionCode[0] = 0x03; - solaxX1_SendData.DataLength[0] = 0x00; - solaxX1_RS485Send(); -} - -void solaxX1_QueryConfigData(void) -{ - solaxX1_SendData.Source[0] = 0x01; - solaxX1_SendData.Destination[0] = 0x00; - solaxX1_SendData.Destination[1] = INVERTER_ADDRESS; - solaxX1_SendData.ControlCode[0] = 0x11; - solaxX1_SendData.FunctionCode[0] = 0x04; - solaxX1_SendData.DataLength[0] = 0x00; - solaxX1_RS485Send(); -} - -uint8_t solaxX1_ParseErrorCode(uint32_t code){ - solaxX1_ErrCode.ErrMessage = code; - if (code == 0) return 0; - if (solaxX1_ErrCode.MainsLostFault) return 1; - if (solaxX1_ErrCode.GridVoltFault) return 2; - if (solaxX1_ErrCode.GridFreqFault) return 3; - if (solaxX1_ErrCode.PvVoltFault) return 4; - if (solaxX1_ErrCode.IsolationFault) return 5; - if (solaxX1_ErrCode.TemperatureOverFault) return 6; - if (solaxX1_ErrCode.FanFault) return 7; - if (solaxX1_ErrCode.OtherDeviceFault) return 8; - return 0; -} - -/*********************************************************************************************/ - -void solaxX1_250MSecond(void) // Every 250 milliseconds -{ - uint8_t DataRead[80] = {0}; - uint8_t TempData[16] = {0}; - char TempDataChar[16]; - float TempFloat; - - if (solaxX1Serial->available()) { - if (solaxX1_RS485Receive(DataRead)) { // CRC-error -> no further action - DEBUG_SENSOR_LOG(PSTR("SX1: Data response CRC error")); - return; - } - - solaxX1_global.SendRetry_count = 20; // Inverter is responding - - if (DataRead[0] != 0xAA || DataRead[1] != 0x55) { // Check for header - DEBUG_SENSOR_LOG(PSTR("SX1: Check for header failed")); - return; - } - - if (DataRead[6] == 0x11 && DataRead[7] == 0x82) { // received "Response for query (live data)" - Energy.data_valid[0] = 0; - solaxX1.temperature = (DataRead[9] << 8) | DataRead[10]; // Temperature - solaxX1.energy_today = ((DataRead[11] << 8) | DataRead[12]) * 0.1f; // Energy Today - solaxX1.dc1_voltage = ((DataRead[13] << 8) | DataRead[14]) * 0.1f; // PV1 Voltage - solaxX1.dc2_voltage = ((DataRead[15] << 8) | DataRead[16]) * 0.1f; // PV2 Voltage - solaxX1.dc1_current = ((DataRead[17] << 8) | DataRead[18]) * 0.1f; // PV1 Current - solaxX1.dc2_current = ((DataRead[19] << 8) | DataRead[20]) * 0.1f; // PV2 Current - Energy.current[0] = ((DataRead[21] << 8) | DataRead[22]) * 0.1f; // AC Current - Energy.voltage[0] = ((DataRead[23] << 8) | DataRead[24]) * 0.1f; // AC Voltage - Energy.frequency[0] = ((DataRead[25] << 8) | DataRead[26]) * 0.01f; // AC Frequency - Energy.active_power[0] = ((DataRead[27] << 8) | DataRead[28]); // AC Power - //temporal = (float)((DataRead[29] << 8) | DataRead[30]) * 0.1f; // Not Used - Energy.import_active[0] = ((DataRead[31] << 24) | (DataRead[32] << 16) | (DataRead[33] << 8) | DataRead[34]) * 0.1f; // Energy Total - solaxX1.runtime_total = (DataRead[35] << 24) | (DataRead[36] << 16) | (DataRead[37] << 8) | DataRead[38]; // Work Time Total - solaxX1.runMode = (DataRead[39] << 8) | DataRead[40]; // Work mode - //temporal = (float)((DataRead[41] << 8) | DataRead[42]); // Grid voltage fault value 0.1V - //temporal = (float)((DataRead[43] << 8) | DataRead[44]); // Gird frequency fault value 0.01Hz - //temporal = (float)((DataRead[45] << 8) | DataRead[46]); // Dc injection fault value 1mA - //temporal = (float)((DataRead[47] << 8) | DataRead[48]); // Temperature fault value - //temporal = (float)((DataRead[49] << 8) | DataRead[50]); // Pv1 voltage fault value 0.1V - //temporal = (float)((DataRead[51] << 8) | DataRead[52]); // Pv2 voltage fault value 0.1V - //temporal = (float)((DataRead[53] << 8) | DataRead[54]); // GFC fault value - solaxX1.errorCode = (DataRead[58] << 24) | (DataRead[57] << 16) | (DataRead[56] << 8) | DataRead[55]; // Error Code - solaxX1.dc1_power = solaxX1.dc1_voltage * solaxX1.dc1_current; - solaxX1.dc2_power = solaxX1.dc2_voltage * solaxX1.dc2_current; - EnergyUpdateTotal(); // 484.708 kWh - DEBUG_SENSOR_LOG(PSTR("SX1: received live data")); - return; - } // end received "Response for query (live data)" - - if (DataRead[6] == 0x11 && DataRead[7] == 0x83) { // received "Response for query (ID data)" - solaxX1_ExtractText(DataRead, solaxX1.SerialNumber, 49, 62); // extract "real" serial number - if (solaxX1_global.Command_QueryID) { - AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter phases: %d"),DataRead[9]); // number of phases - solaxX1_ExtractText(DataRead, TempData, 10, 15); // extract rated bus power (my be empty) - AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter rated bus power: %s"),(char*)TempData); - solaxX1_ExtractText(DataRead, TempData, 16, 20); // extract firmware version - AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter firmware version: %s"),(char*)TempData); - solaxX1_ExtractText(DataRead, TempData, 21, 34); // extract module name (my be empty) - AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter module name: %s"),(char*)TempData); - solaxX1_ExtractText(DataRead, TempData, 35, 48); // extract factory name - AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter factory name: %s"),(char*)TempData); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter serial number: %s"),(char*)solaxX1.SerialNumber); - solaxX1_ExtractText(DataRead, TempData, 63, 66); // extract rated bus voltage - AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter rated bus voltage: %s"),(char*)TempData); - solaxX1_global.Command_QueryID = false; - } else { - AddLog(LOG_LEVEL_DEBUG, PSTR("SX1: Inverter serial number: %s"),(char*)solaxX1.SerialNumber); - } - DEBUG_SENSOR_LOG(PSTR("SX1: received ID data")); - return; - } // end received "Response for query (ID data)" - -#ifdef SOLAXX1_READCONFIG - if (DataRead[6] == 0x11 && DataRead[7] == 0x84) { // received "Response for query (config data)" - if (solaxX1_global.Command_QueryConfig) { - // This values are displayed as they were received from the inverter. They are not interpreted in any way. - TempFloat = ((DataRead[9] << 8) | DataRead[10]) * 0.1f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wVpvStart: %1_f V (Inverter launch voltage threshold)"), &TempFloat); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wTimeStart: %d sec (launch wait time)"), (DataRead[11] << 8) | DataRead[12]); - TempFloat = ((DataRead[13] << 8) | DataRead[14]) * 0.1f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wVacMinProtect: %1_f V (allowed minimum grid voltage)"), &TempFloat); - TempFloat = ((DataRead[15] << 8) | DataRead[16]) * 0.1f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wVacMaxProtect: %1_f V (allowed maximum grid voltage)"), &TempFloat); - TempFloat = ((DataRead[17] << 8) | DataRead[18]) * 0.01f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wFacMinProtect: %2_f Hz (allowed minimum grid frequency)"), &TempFloat); - TempFloat = ((DataRead[19] << 8) | DataRead[20]) * 0.01f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wFacMaxProtect: %2_f Hz (allowed maximum grid frequency)"), &TempFloat); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wDciLimits: %d mA (DC component limits)"), (DataRead[21] << 8) | DataRead[22]); - TempFloat = ((DataRead[23] << 8) | DataRead[24]) * 0.1f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wGrid10MinAvgProtect: %1_f V (10 minutes over voltage protect)"), &TempFloat); - TempFloat = ((DataRead[25] << 8) | DataRead[26]) * 0.1f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wVacMinSlowProtect: %1_f V (grid undervoltage protect value)"), &TempFloat); - TempFloat = ((DataRead[27] << 8) | DataRead[28]) * 0.1f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wVacMaxSlowProtect: %1_f V (grid overvoltage protect value)"), &TempFloat); - TempFloat = ((DataRead[29] << 8) | DataRead[30]) * 0.01f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wFacMinSlowProtect: %2_f Hz (grid underfrequency protect value)"), &TempFloat); - TempFloat = ((DataRead[31] << 8) | DataRead[32]) * 0.01f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wFacMaxSlowProtect: %2_f Hz (grid overfrequency protect value)"), &TempFloat); - GetTextIndexed(TempDataChar, sizeof(TempDataChar), (DataRead[33] << 8) | DataRead[34], kSolaxSafetyType); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wSafety: %d ≙ %s"), (DataRead[33] << 8) | DataRead[34], TempDataChar); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wPowerfactor_mode: %d"), DataRead[35]); - TempFloat = DataRead[36] * 0.01f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wPowerfactor_data: %2_f"), &TempFloat); - TempFloat = DataRead[37] * 0.01f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wUpperLimit: %2_f (overexcite limits)"), &TempFloat); - TempFloat = DataRead[38] * 0.01f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wLowerLimit: %2_f (underexcite limits)"), &TempFloat); - TempFloat = DataRead[39] * 0.01f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wPowerLow: %2_f (power ratio change upper limits)"), &TempFloat); - TempFloat = DataRead[40] * 0.01f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wPowerUp: %2_f (power ratio change lower limits)"), &TempFloat); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: Qpower_set: %d"), (DataRead[41] << 8) | DataRead[42]); - TempFloat = ((DataRead[43] << 8) | DataRead[44]) * 0.01f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: WFreqSetPoint: %2_f Hz (Over Frequency drop output setpoint)"), &TempFloat); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: WFreqDroopRate: %d %% (drop output slope)"), (DataRead[45] << 8) | DataRead[46]); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: QuVupRate: %d %% (Q(U) curve up set point)"), (DataRead[47] << 8) | DataRead[48]); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: QuVlowRate: %d %% (Q(U) curve low set point)"), (DataRead[49] << 8) | DataRead[50]); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: WPowerLimitsPercent: %d %%"), (DataRead[51] << 8) | DataRead[52]); - TempFloat = ((DataRead[53] << 8) | DataRead[54]) * 0.01f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: WWgra: %2_f %%"), &TempFloat); - TempFloat = ((DataRead[55] << 8) | DataRead[56]) * 0.1f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wWv2: %1_f V"), &TempFloat); - TempFloat = ((DataRead[57] << 8) | DataRead[58]) * 0.1f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wWv3: %1_f V"), &TempFloat); - TempFloat = ((DataRead[59] << 8) | DataRead[60]) * 0.1f; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wWv4: %1_f V"), &TempFloat); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wQurangeV1: %d %%"), (DataRead[61] << 8) | DataRead[62]); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: wQurangeV4: %d %%"), (DataRead[63] << 8) | DataRead[64]); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: BVoltPowerLimit: %d"), (DataRead[65] << 8) | DataRead[66]); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: WPowerManagerEnable: %d"), (DataRead[67] << 8) | DataRead[68]); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: WGlobalSeachMPPTStrartFlg: %d"), (DataRead[69] << 8) | DataRead[70]); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: WFrqProtectRestrictive: %d"), (DataRead[71] << 8) | DataRead[72]); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: WQuDelayTimer: %d sec"), (DataRead[73] << 8) | DataRead[74]); - AddLog(LOG_LEVEL_INFO, PSTR("SX1: WFreqActivePowerDelayTimer: %d ms"), (DataRead[75] << 8) | DataRead[76]); - - solaxX1_global.Command_QueryConfig = false; - } - DEBUG_SENSOR_LOG(PSTR("SX1: received config data")); - return; - } // end received "Response for query (config data)" -#endif // SOLAXX1_READCONFIG - - if (DataRead[6] == 0x10 && DataRead[7] == 0x80) { // received "register request" - solaxX1_global.QueryData_count = 5; // give time for next query - solaxX1_ExtractText(DataRead, solaxX1_SendData.Payload, 9, 22); // store serial number for register - DEBUG_SENSOR_LOG(PSTR("SX1: received register request and send register address")); - solaxX1_SendInverterAddress(); // "send register address" - return; - } - - if (DataRead[6] == 0x10 && DataRead[7] == 0x81 && DataRead[9] == 0x06) { // received "address confirm (ACK)" - solaxX1_global.QueryData_count = 5; // give time for next query - solaxX1_global.AddressAssigned = true; - DEBUG_SENSOR_LOG(PSTR("SX1: received \"address confirm (ACK)\"")); - return; - } - - } // end solaxX1Serial->available() - -// DEBUG_SENSOR_LOG(PSTR("SX1: solaxX1_global.AddressAssigned: %d, solaxX1_global.QueryData_count: %d, solaxX1_global.SendRetry_count: %d, solaxX1_global.QueryID_count: %d"), solaxX1_global.AddressAssigned, solaxX1_global.QueryData_count, solaxX1_global.SendRetry_count, solaxX1_global.QueryID_count); - if (solaxX1_global.AddressAssigned) { - if (!solaxX1_global.QueryData_count) { // normal periodically query - solaxX1_global.QueryData_count = 5; - if (!solaxX1_global.QueryID_count || solaxX1_global.Command_QueryID) { // ID query - DEBUG_SENSOR_LOG(PSTR("SX1: Send ID query")); - solaxX1_QueryIDData(); - } else if (solaxX1_global.Command_QueryConfig) { // Config query - DEBUG_SENSOR_LOG(PSTR("SX1: Send config query")); - solaxX1_QueryConfigData(); - } else { // live query - DEBUG_SENSOR_LOG(PSTR("SX1: Send live query")); - solaxX1_QueryLiveData(); - } - solaxX1_global.QueryID_count++; // query ID every 256th time - } // end normal periodically query - solaxX1_global.QueryData_count--; - if (!solaxX1_global.SendRetry_count) { // Inverter went "off" - solaxX1_global.SendRetry_count = 20; - DEBUG_SENSOR_LOG(PSTR("SX1: Inverter went \"off\"")); - 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 = Energy.current[0] = Energy.voltage[0] = Energy.frequency[0] = Energy.active_power[0] = 0; - solaxX1.runMode = -1; // off(line) - solaxX1_global.AddressAssigned = false; - } // end Inverter went "off" - } else { // sent query for inverters in offline status - if (!solaxX1_global.SendRetry_count) { - solaxX1_global.SendRetry_count = 20; - DEBUG_SENSOR_LOG(PSTR("SX1: Sent query for inverters in offline state")); - solaxX1_QueryOfflineInverters(); - } - } - solaxX1_global.SendRetry_count--; - -return; -} // end solaxX1_250MSecond - -void solaxX1_SnsInit(void) -{ - AddLog(LOG_LEVEL_INFO, PSTR("SX1: Init - RX-pin: %d, TX-pin: %d, RTS-pin: %d"), Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), Pin(GPIO_SOLAXX1_RTS)); - solaxX1Serial = new TasmotaSerial(Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), 1); - if (solaxX1Serial->begin(SOLAXX1_SPEED)) { - if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } - } else { - TasmotaGlobal.energy_driver = ENERGY_NONE; - } - if (PinUsed(GPIO_SOLAXX1_RTS)) { - pinMode(Pin(GPIO_SOLAXX1_RTS), OUTPUT); - } -} - -void solaxX1_DrvInit(void) -{ - if (PinUsed(GPIO_SOLAXX1_RX) && PinUsed(GPIO_SOLAXX1_TX)) { - TasmotaGlobal.energy_driver = XNRG_12; - } -} - -bool SolaxX1_cmd(void) -{ - if (!solaxX1_global.AddressAssigned) { - AddLog(LOG_LEVEL_INFO, PSTR("SX1: No inverter registered")); - return false; - } - - if (!strcasecmp(XdrvMailbox.data, "ReadIDinfo")) { - solaxX1_global.Command_QueryID = true; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: ReadIDinfo sent...")); - return true; - } else if (!strcasecmp(XdrvMailbox.data, "ReadConfig")) { -#ifdef SOLAXX1_READCONFIG - solaxX1_global.Command_QueryConfig = true; - AddLog(LOG_LEVEL_INFO, PSTR("SX1: ReadConfig sent...")); - return true; -#else - AddLog(LOG_LEVEL_INFO, PSTR("SX1: Command not available. Please set compiler directive '#define SOLAXX1_READCONFIG'.")); - return false; -#endif // SOLAXX1_READCONFIG - } - AddLog(LOG_LEVEL_INFO, PSTR("SX1: Unknown command: \"%s\""),XdrvMailbox.data); - return false; -} - -#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}%d " D_UNIT_HOUR "{e}" - "{s}" D_SOLAX_X1 " " D_STATUS "{m}%s" - "{s}" D_SOLAX_X1 " " D_ERROR "{m}%s" - "{s}" D_SOLAX_X1 " Inverter SN{m}%s"; -#endif // USE_WEBSERVER - -void solaxX1_Show(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 status[33]; - GetTextIndexed(status, sizeof(status), solaxX1.runMode + 1, 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 "\":%d,\"" D_JSON_RUNTIME "\":%d,\"" D_JSON_STATUS "\":\"%s\",\"" D_JSON_ERROR "\":%d"), - solaxX1.temperature, solaxX1.runtime_total, status, solaxX1.errorCode); - -#ifdef USE_DOMOTICZ - // Avoid bad temperature report at beginning of the day (spikes of 1200 celsius degrees) - if (0 == TasmotaGlobal.tele_period && solaxX1.temperature < 100) { DomoticzSensor(DZ_TEMP, solaxX1.temperature); } -#endif // USE_DOMOTICZ - -#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_Temp(D_SOLAX_X1, solaxX1.temperature); - char errorCodeString[33]; - WSContentSend_PD(HTTP_SNS_solaxX1_DATA3, solaxX1.runtime_total, status, - GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError), - solaxX1.SerialNumber); -#endif // USE_WEBSERVER - } -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -bool Xnrg12(uint8_t function) -{ - bool result = false; - - switch (function) { - case FUNC_EVERY_250_MSECOND: - solaxX1_250MSecond(); - break; - case FUNC_JSON_APPEND: - solaxX1_Show(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: - solaxX1_Show(0); - break; -#endif // USE_WEBSERVER - case FUNC_INIT: - solaxX1_SnsInit(); - break; - case FUNC_PRE_INIT: - solaxX1_DrvInit(); - break; - case FUNC_COMMAND: - result = SolaxX1_cmd(); - break; - } - return result; -} - -#endif // USE_SOLAX_X1_NRG -#endif // USE_ENERGY_SENSOR +/* + xnrg_12_solaxX1.ino - Solax X1 inverter RS485 support for Tasmota + + Copyright (C) 2021 by Pablo Zerón + Copyright (C) 2022 by Stefan Wershoven + + 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_ENERGY_SENSOR +#ifdef USE_SOLAX_X1 +/*********************************************************************************************\ + * Solax X1 Inverter +\*********************************************************************************************/ + +#define XNRG_12 12 + +#ifndef SOLAXX1_SPEED +#define SOLAXX1_SPEED 9600 // default solax rs485 speed +#endif + +#define SOLAXX1_READCONFIG // enable to read inverters config; disable to save codespace (3k1) + +#define INVERTER_ADDRESS 0x0A + +#define D_SOLAX_X1 "SolaxX1" + +#include + +const char kSolaxMode[] PROGMEM = + D_OFF "|" D_SOLAX_MODE_0 "|" D_SOLAX_MODE_1 "|" D_SOLAX_MODE_2 "|" D_SOLAX_MODE_3 "|" D_SOLAX_MODE_4 "|" + D_SOLAX_MODE_5 "|" D_SOLAX_MODE_6; + +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; + +#ifdef SOLAXX1_READCONFIG +const char kSolaxSafetyType[] PROGMEM = + "VDE0126|ARN4105|AS4777_AU|G98/1|C10/11|OVE/ONORME8001|EN50438_NL|EN50438_DK|CEB|CEI021|NRS097_2_1|" + "VDE0126_Gr_Is|UTE_C15_712|IEC61727|G99/1|VDE0126_Gr_Co|France_VFR2014|C15_712_is_50|C15_712_is_60|" + "AS4777_NZ|RD1699|Chile|EN50438_Ireland|Philippines|Czech_PPDS|Czech_50438"; +#endif // SOLAXX1_READCONFIG + +union { + uint32_t ErrMessage; + struct { + //BYTE0 + uint8_t TzProtectFault:1;//0 + uint8_t MainsLostFault:1;//1 + uint8_t GridVoltFault:1;//2 + uint8_t GridFreqFault:1;//3 + uint8_t PLLLostFault:1;//4 + uint8_t BusVoltFault:1;//5 + uint8_t ErrBit06:1;//6 + uint8_t OciFault:1;//7 + //BYTE1 + uint8_t Dci_OCP_Fault:1;//8 + uint8_t ResidualCurrentFault:1;//9 + uint8_t PvVoltFault:1;//10 + uint8_t Ac10Mins_Voltage_Fault:1;//11 + uint8_t IsolationFault:1;//12 + uint8_t TemperatureOverFault:1;//13 + uint8_t FanFault:1;//14 + uint8_t ErrBit15:1;//15 + //BYTE2 + uint8_t SpiCommsFault:1;//16 + uint8_t SciCommsFault:1;//17 + uint8_t ErrBit18:1;//18 + uint8_t InputConfigFault:1;//19 + uint8_t EepromFault:1;//20 + uint8_t RelayFault:1;//21 + uint8_t SampleConsistenceFault:1;//22 + uint8_t ResidualCurrent_DeviceFault:1;//23 + //BYTE3 + uint8_t ErrBit24:1;//24 + uint8_t ErrBit25:1;//25 + uint8_t ErrBit26:1;//26 + uint8_t ErrBit27:1;//27 + uint8_t ErrBit28:1;//28 + uint8_t DCI_DeviceFault:1;//29 + uint8_t OtherDeviceFault:1;//30 + uint8_t ErrBit31:1;//31 + }; +} solaxX1_ErrCode; + +struct SOLAXX1_LIVEDATA { + int16_t temperature = 0; + float energy_today = 0; + float dc1_voltage = 0; + float dc2_voltage = 0; + float dc1_current = 0; + float dc2_current = 0; + uint32_t runtime_total = 0; + float dc1_power = 0; + float dc2_power = 0; + int16_t runMode = 0; + uint32_t errorCode = 0; + uint8_t SerialNumber[16] = {0x6e, 0x2f, 0x61}; // "n/a" +} solaxX1; + +struct SOLAXX1_GLOBALDATA { + bool AddressAssigned = true; + uint8_t SendRetry_count = 20; + uint8_t QueryData_count = 0; + uint8_t QueryID_count = 240; + bool Command_QueryID = false;; + bool Command_QueryConfig = false; +} solaxX1_global; + +struct SOLAXX1_SENDDATA { + 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 Payload[16] = {0}; +} solaxX1_SendData; + +TasmotaSerial *solaxX1Serial; + +/*********************************************************************************************/ + +void solaxX1_RS485Send(void) +{ + uint8_t message[30]; + memcpy(message, solaxX1_SendData.Header, 2); + memcpy(message + 2, solaxX1_SendData.Source, 2); + memcpy(message + 4, solaxX1_SendData.Destination, 2); + memcpy(message + 6, solaxX1_SendData.ControlCode, 1); + memcpy(message + 7, solaxX1_SendData.FunctionCode, 1); + memcpy(message + 8, solaxX1_SendData.DataLength, 1); + memcpy(message + 9, solaxX1_SendData.Payload, sizeof(solaxX1_SendData.Payload)); + uint16_t crc = solaxX1_calculateCRC(message, 9 + solaxX1_SendData.DataLength[0]); // calculate out crc bytes + while (solaxX1Serial->available() > 0) { // read serial if any old data is available + solaxX1Serial->read(); + } + if (PinUsed(GPIO_SOLAXX1_RTS)) { + digitalWrite(Pin(GPIO_SOLAXX1_RTS), HIGH); + } + solaxX1Serial->flush(); + solaxX1Serial->write(message, 9 + solaxX1_SendData.DataLength[0]); + solaxX1Serial->write(highByte(crc)); + solaxX1Serial->write(lowByte(crc)); + solaxX1Serial->flush(); + if (PinUsed(GPIO_SOLAXX1_RTS)) { + digitalWrite(Pin(GPIO_SOLAXX1_RTS), LOW); + } + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, message, 9 + solaxX1_SendData.DataLength[0]); +} + +bool solaxX1_RS485Receive(uint8_t *ReadBuffer) +{ + uint8_t len = 0; + while (solaxX1Serial->available() > 0) { + ReadBuffer[len++] = (uint8_t)solaxX1Serial->read(); + } + AddLogBuffer(LOG_LEVEL_DEBUG_MORE, ReadBuffer, len); + uint16_t crc = solaxX1_calculateCRC(ReadBuffer, len - 2); // calculate out crc bytes + return !(ReadBuffer[len - 1] == lowByte(crc) && ReadBuffer[len - 2] == highByte(crc)); +} + +uint16_t solaxX1_calculateCRC(uint8_t *bExternTxPackage, uint8_t bLen) +{ + uint8_t i; + uint16_t wChkSum = 0; + for (i = 0; i < bLen; i++) { + wChkSum = wChkSum + bExternTxPackage[i]; + } + return wChkSum; +} + +void solaxX1_ExtractText(uint8_t *DataIn, uint8_t *DataOut, uint8_t Begin, uint8_t End) +{ + uint8_t i; + for (i = Begin; i <= End; i++) { + DataOut[i - Begin] = DataIn[i]; + } + DataOut[End - Begin + 1] = 0; +} + +void solaxX1_QueryOfflineInverters(void) +{ + solaxX1_SendData.Source[0] = 0x01; + solaxX1_SendData.Destination[0] = 0x00; + solaxX1_SendData.Destination[1] = 0x00; + solaxX1_SendData.ControlCode[0] = 0x10; + solaxX1_SendData.FunctionCode[0] = 0x00; + solaxX1_SendData.DataLength[0] = 0x00; + solaxX1_RS485Send(); +} + +void solaxX1_SendInverterAddress(void) +{ + solaxX1_SendData.Source[0] = 0x00; + solaxX1_SendData.Destination[0] = 0x00; + solaxX1_SendData.Destination[1] = 0x00; + solaxX1_SendData.ControlCode[0] = 0x10; + solaxX1_SendData.FunctionCode[0] = 0x01; + solaxX1_SendData.DataLength[0] = 0x0F; + solaxX1_SendData.Payload[14] = INVERTER_ADDRESS; // Inverter Address, It must be unique in case of more inverters in the same rs485 net. + solaxX1_RS485Send(); +} + +void solaxX1_QueryLiveData(void) +{ + solaxX1_SendData.Source[0] = 0x01; + solaxX1_SendData.Destination[0] = 0x00; + solaxX1_SendData.Destination[1] = INVERTER_ADDRESS; + solaxX1_SendData.ControlCode[0] = 0x11; + solaxX1_SendData.FunctionCode[0] = 0x02; + solaxX1_SendData.DataLength[0] = 0x00; + solaxX1_RS485Send(); +} + +void solaxX1_QueryIDData(void) +{ + solaxX1_SendData.Source[0] = 0x01; + solaxX1_SendData.Destination[0] = 0x00; + solaxX1_SendData.Destination[1] = INVERTER_ADDRESS; + solaxX1_SendData.ControlCode[0] = 0x11; + solaxX1_SendData.FunctionCode[0] = 0x03; + solaxX1_SendData.DataLength[0] = 0x00; + solaxX1_RS485Send(); +} + +void solaxX1_QueryConfigData(void) +{ + solaxX1_SendData.Source[0] = 0x01; + solaxX1_SendData.Destination[0] = 0x00; + solaxX1_SendData.Destination[1] = INVERTER_ADDRESS; + solaxX1_SendData.ControlCode[0] = 0x11; + solaxX1_SendData.FunctionCode[0] = 0x04; + solaxX1_SendData.DataLength[0] = 0x00; + solaxX1_RS485Send(); +} + +uint8_t solaxX1_ParseErrorCode(uint32_t code){ + solaxX1_ErrCode.ErrMessage = code; + if (code == 0) return 0; + if (solaxX1_ErrCode.MainsLostFault) return 1; + if (solaxX1_ErrCode.GridVoltFault) return 2; + if (solaxX1_ErrCode.GridFreqFault) return 3; + if (solaxX1_ErrCode.PvVoltFault) return 4; + if (solaxX1_ErrCode.IsolationFault) return 5; + if (solaxX1_ErrCode.TemperatureOverFault) return 6; + if (solaxX1_ErrCode.FanFault) return 7; + if (solaxX1_ErrCode.OtherDeviceFault) return 8; + return 0; +} + +/*********************************************************************************************/ + +void solaxX1_250MSecond(void) // Every 250 milliseconds +{ + uint8_t DataRead[80] = {0}; + uint8_t TempData[16] = {0}; + char TempDataChar[16]; + float TempFloat; + + if (solaxX1Serial->available()) { + if (solaxX1_RS485Receive(DataRead)) { // CRC-error -> no further action + DEBUG_SENSOR_LOG(PSTR("SX1: Data response CRC error")); + return; + } + + solaxX1_global.SendRetry_count = 20; // Inverter is responding + + if (DataRead[0] != 0xAA || DataRead[1] != 0x55) { // Check for header + DEBUG_SENSOR_LOG(PSTR("SX1: Check for header failed")); + return; + } + + if (DataRead[6] == 0x11 && DataRead[7] == 0x82) { // received "Response for query (live data)" + Energy.data_valid[0] = 0; + solaxX1.temperature = (DataRead[9] << 8) | DataRead[10]; // Temperature + solaxX1.energy_today = ((DataRead[11] << 8) | DataRead[12]) * 0.1f; // Energy Today + solaxX1.dc1_voltage = ((DataRead[13] << 8) | DataRead[14]) * 0.1f; // PV1 Voltage + solaxX1.dc2_voltage = ((DataRead[15] << 8) | DataRead[16]) * 0.1f; // PV2 Voltage + solaxX1.dc1_current = ((DataRead[17] << 8) | DataRead[18]) * 0.1f; // PV1 Current + solaxX1.dc2_current = ((DataRead[19] << 8) | DataRead[20]) * 0.1f; // PV2 Current + Energy.current[0] = ((DataRead[21] << 8) | DataRead[22]) * 0.1f; // AC Current + Energy.voltage[0] = ((DataRead[23] << 8) | DataRead[24]) * 0.1f; // AC Voltage + Energy.frequency[0] = ((DataRead[25] << 8) | DataRead[26]) * 0.01f; // AC Frequency + Energy.active_power[0] = ((DataRead[27] << 8) | DataRead[28]); // AC Power + //temporal = (float)((DataRead[29] << 8) | DataRead[30]) * 0.1f; // Not Used + Energy.import_active[0] = ((DataRead[31] << 24) | (DataRead[32] << 16) | (DataRead[33] << 8) | DataRead[34]) * 0.1f; // Energy Total + solaxX1.runtime_total = (DataRead[35] << 24) | (DataRead[36] << 16) | (DataRead[37] << 8) | DataRead[38]; // Work Time Total + solaxX1.runMode = (DataRead[39] << 8) | DataRead[40]; // Work mode + //temporal = (float)((DataRead[41] << 8) | DataRead[42]); // Grid voltage fault value 0.1V + //temporal = (float)((DataRead[43] << 8) | DataRead[44]); // Gird frequency fault value 0.01Hz + //temporal = (float)((DataRead[45] << 8) | DataRead[46]); // Dc injection fault value 1mA + //temporal = (float)((DataRead[47] << 8) | DataRead[48]); // Temperature fault value + //temporal = (float)((DataRead[49] << 8) | DataRead[50]); // Pv1 voltage fault value 0.1V + //temporal = (float)((DataRead[51] << 8) | DataRead[52]); // Pv2 voltage fault value 0.1V + //temporal = (float)((DataRead[53] << 8) | DataRead[54]); // GFC fault value + solaxX1.errorCode = (DataRead[58] << 24) | (DataRead[57] << 16) | (DataRead[56] << 8) | DataRead[55]; // Error Code + solaxX1.dc1_power = solaxX1.dc1_voltage * solaxX1.dc1_current; + solaxX1.dc2_power = solaxX1.dc2_voltage * solaxX1.dc2_current; + EnergyUpdateTotal(); // 484.708 kWh + DEBUG_SENSOR_LOG(PSTR("SX1: received live data")); + return; + } // end received "Response for query (live data)" + + if (DataRead[6] == 0x11 && DataRead[7] == 0x83) { // received "Response for query (ID data)" + solaxX1_ExtractText(DataRead, solaxX1.SerialNumber, 49, 62); // extract "real" serial number + if (solaxX1_global.Command_QueryID) { + AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter phases: %d"),DataRead[9]); // number of phases + solaxX1_ExtractText(DataRead, TempData, 10, 15); // extract rated bus power (my be empty) + AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter rated bus power: %s"),(char*)TempData); + solaxX1_ExtractText(DataRead, TempData, 16, 20); // extract firmware version + AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter firmware version: %s"),(char*)TempData); + solaxX1_ExtractText(DataRead, TempData, 21, 34); // extract module name (my be empty) + AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter module name: %s"),(char*)TempData); + solaxX1_ExtractText(DataRead, TempData, 35, 48); // extract factory name + AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter factory name: %s"),(char*)TempData); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter serial number: %s"),(char*)solaxX1.SerialNumber); + solaxX1_ExtractText(DataRead, TempData, 63, 66); // extract rated bus voltage + AddLog(LOG_LEVEL_INFO, PSTR("SX1: Inverter rated bus voltage: %s"),(char*)TempData); + solaxX1_global.Command_QueryID = false; + } else { + AddLog(LOG_LEVEL_DEBUG, PSTR("SX1: Inverter serial number: %s"),(char*)solaxX1.SerialNumber); + } + DEBUG_SENSOR_LOG(PSTR("SX1: received ID data")); + return; + } // end received "Response for query (ID data)" + +#ifdef SOLAXX1_READCONFIG + if (DataRead[6] == 0x11 && DataRead[7] == 0x84) { // received "Response for query (config data)" + if (solaxX1_global.Command_QueryConfig) { + // This values are displayed as they were received from the inverter. They are not interpreted in any way. + TempFloat = ((DataRead[9] << 8) | DataRead[10]) * 0.1f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wVpvStart: %1_f V (Inverter launch voltage threshold)"), &TempFloat); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wTimeStart: %d sec (launch wait time)"), (DataRead[11] << 8) | DataRead[12]); + TempFloat = ((DataRead[13] << 8) | DataRead[14]) * 0.1f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wVacMinProtect: %1_f V (allowed minimum grid voltage)"), &TempFloat); + TempFloat = ((DataRead[15] << 8) | DataRead[16]) * 0.1f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wVacMaxProtect: %1_f V (allowed maximum grid voltage)"), &TempFloat); + TempFloat = ((DataRead[17] << 8) | DataRead[18]) * 0.01f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wFacMinProtect: %2_f Hz (allowed minimum grid frequency)"), &TempFloat); + TempFloat = ((DataRead[19] << 8) | DataRead[20]) * 0.01f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wFacMaxProtect: %2_f Hz (allowed maximum grid frequency)"), &TempFloat); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wDciLimits: %d mA (DC component limits)"), (DataRead[21] << 8) | DataRead[22]); + TempFloat = ((DataRead[23] << 8) | DataRead[24]) * 0.1f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wGrid10MinAvgProtect: %1_f V (10 minutes over voltage protect)"), &TempFloat); + TempFloat = ((DataRead[25] << 8) | DataRead[26]) * 0.1f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wVacMinSlowProtect: %1_f V (grid undervoltage protect value)"), &TempFloat); + TempFloat = ((DataRead[27] << 8) | DataRead[28]) * 0.1f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wVacMaxSlowProtect: %1_f V (grid overvoltage protect value)"), &TempFloat); + TempFloat = ((DataRead[29] << 8) | DataRead[30]) * 0.01f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wFacMinSlowProtect: %2_f Hz (grid underfrequency protect value)"), &TempFloat); + TempFloat = ((DataRead[31] << 8) | DataRead[32]) * 0.01f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wFacMaxSlowProtect: %2_f Hz (grid overfrequency protect value)"), &TempFloat); + GetTextIndexed(TempDataChar, sizeof(TempDataChar), (DataRead[33] << 8) | DataRead[34], kSolaxSafetyType); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wSafety: %d ≙ %s"), (DataRead[33] << 8) | DataRead[34], TempDataChar); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wPowerfactor_mode: %d"), DataRead[35]); + TempFloat = DataRead[36] * 0.01f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wPowerfactor_data: %2_f"), &TempFloat); + TempFloat = DataRead[37] * 0.01f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wUpperLimit: %2_f (overexcite limits)"), &TempFloat); + TempFloat = DataRead[38] * 0.01f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wLowerLimit: %2_f (underexcite limits)"), &TempFloat); + TempFloat = DataRead[39] * 0.01f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wPowerLow: %2_f (power ratio change upper limits)"), &TempFloat); + TempFloat = DataRead[40] * 0.01f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wPowerUp: %2_f (power ratio change lower limits)"), &TempFloat); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: Qpower_set: %d"), (DataRead[41] << 8) | DataRead[42]); + TempFloat = ((DataRead[43] << 8) | DataRead[44]) * 0.01f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: WFreqSetPoint: %2_f Hz (Over Frequency drop output setpoint)"), &TempFloat); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: WFreqDroopRate: %d %% (drop output slope)"), (DataRead[45] << 8) | DataRead[46]); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: QuVupRate: %d %% (Q(U) curve up set point)"), (DataRead[47] << 8) | DataRead[48]); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: QuVlowRate: %d %% (Q(U) curve low set point)"), (DataRead[49] << 8) | DataRead[50]); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: WPowerLimitsPercent: %d %%"), (DataRead[51] << 8) | DataRead[52]); + TempFloat = ((DataRead[53] << 8) | DataRead[54]) * 0.01f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: WWgra: %2_f %%"), &TempFloat); + TempFloat = ((DataRead[55] << 8) | DataRead[56]) * 0.1f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wWv2: %1_f V"), &TempFloat); + TempFloat = ((DataRead[57] << 8) | DataRead[58]) * 0.1f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wWv3: %1_f V"), &TempFloat); + TempFloat = ((DataRead[59] << 8) | DataRead[60]) * 0.1f; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wWv4: %1_f V"), &TempFloat); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wQurangeV1: %d %%"), (DataRead[61] << 8) | DataRead[62]); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: wQurangeV4: %d %%"), (DataRead[63] << 8) | DataRead[64]); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: BVoltPowerLimit: %d"), (DataRead[65] << 8) | DataRead[66]); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: WPowerManagerEnable: %d"), (DataRead[67] << 8) | DataRead[68]); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: WGlobalSeachMPPTStrartFlg: %d"), (DataRead[69] << 8) | DataRead[70]); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: WFrqProtectRestrictive: %d"), (DataRead[71] << 8) | DataRead[72]); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: WQuDelayTimer: %d sec"), (DataRead[73] << 8) | DataRead[74]); + AddLog(LOG_LEVEL_INFO, PSTR("SX1: WFreqActivePowerDelayTimer: %d ms"), (DataRead[75] << 8) | DataRead[76]); + + solaxX1_global.Command_QueryConfig = false; + } + DEBUG_SENSOR_LOG(PSTR("SX1: received config data")); + return; + } // end received "Response for query (config data)" +#endif // SOLAXX1_READCONFIG + + if (DataRead[6] == 0x10 && DataRead[7] == 0x80) { // received "register request" + solaxX1_global.QueryData_count = 5; // give time for next query + solaxX1_ExtractText(DataRead, solaxX1_SendData.Payload, 9, 22); // store serial number for register + DEBUG_SENSOR_LOG(PSTR("SX1: received register request and send register address")); + solaxX1_SendInverterAddress(); // "send register address" + return; + } + + if (DataRead[6] == 0x10 && DataRead[7] == 0x81 && DataRead[9] == 0x06) { // received "address confirm (ACK)" + solaxX1_global.QueryData_count = 5; // give time for next query + solaxX1_global.AddressAssigned = true; + DEBUG_SENSOR_LOG(PSTR("SX1: received \"address confirm (ACK)\"")); + return; + } + + } // end solaxX1Serial->available() + +// DEBUG_SENSOR_LOG(PSTR("SX1: solaxX1_global.AddressAssigned: %d, solaxX1_global.QueryData_count: %d, solaxX1_global.SendRetry_count: %d, solaxX1_global.QueryID_count: %d"), solaxX1_global.AddressAssigned, solaxX1_global.QueryData_count, solaxX1_global.SendRetry_count, solaxX1_global.QueryID_count); + if (solaxX1_global.AddressAssigned) { + if (!solaxX1_global.QueryData_count) { // normal periodically query + solaxX1_global.QueryData_count = 5; + if (!solaxX1_global.QueryID_count || solaxX1_global.Command_QueryID) { // ID query + DEBUG_SENSOR_LOG(PSTR("SX1: Send ID query")); + solaxX1_QueryIDData(); + } else if (solaxX1_global.Command_QueryConfig) { // Config query + DEBUG_SENSOR_LOG(PSTR("SX1: Send config query")); + solaxX1_QueryConfigData(); + } else { // live query + DEBUG_SENSOR_LOG(PSTR("SX1: Send live query")); + solaxX1_QueryLiveData(); + } + solaxX1_global.QueryID_count++; // query ID every 256th time + } // end normal periodically query + solaxX1_global.QueryData_count--; + if (!solaxX1_global.SendRetry_count) { // Inverter went "off" + solaxX1_global.SendRetry_count = 20; + DEBUG_SENSOR_LOG(PSTR("SX1: Inverter went \"off\"")); + 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 = Energy.current[0] = Energy.voltage[0] = Energy.frequency[0] = Energy.active_power[0] = 0; + solaxX1.runMode = -1; // off(line) + solaxX1_global.AddressAssigned = false; + } // end Inverter went "off" + } else { // sent query for inverters in offline status + if (!solaxX1_global.SendRetry_count) { + solaxX1_global.SendRetry_count = 20; + DEBUG_SENSOR_LOG(PSTR("SX1: Sent query for inverters in offline state")); + solaxX1_QueryOfflineInverters(); + } + } + solaxX1_global.SendRetry_count--; + +return; +} // end solaxX1_250MSecond + +void solaxX1_SnsInit(void) +{ + AddLog(LOG_LEVEL_INFO, PSTR("SX1: Init - RX-pin: %d, TX-pin: %d, RTS-pin: %d"), Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), Pin(GPIO_SOLAXX1_RTS)); + solaxX1Serial = new TasmotaSerial(Pin(GPIO_SOLAXX1_RX), Pin(GPIO_SOLAXX1_TX), 1); + if (solaxX1Serial->begin(SOLAXX1_SPEED)) { + if (solaxX1Serial->hardwareSerial()) { ClaimSerial(); } + } else { + TasmotaGlobal.energy_driver = ENERGY_NONE; + } + if (PinUsed(GPIO_SOLAXX1_RTS)) { + pinMode(Pin(GPIO_SOLAXX1_RTS), OUTPUT); + } +} + +void solaxX1_DrvInit(void) +{ + if (PinUsed(GPIO_SOLAXX1_RX) && PinUsed(GPIO_SOLAXX1_TX)) { + TasmotaGlobal.energy_driver = XNRG_12; + } +} + +bool SolaxX1_cmd(void) +{ + if (!solaxX1_global.AddressAssigned) { + AddLog(LOG_LEVEL_INFO, PSTR("SX1: No inverter registered")); + return false; + } + + if (!strcasecmp(XdrvMailbox.data, "ReadIDinfo")) { + solaxX1_global.Command_QueryID = true; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: ReadIDinfo sent...")); + return true; + } else if (!strcasecmp(XdrvMailbox.data, "ReadConfig")) { +#ifdef SOLAXX1_READCONFIG + solaxX1_global.Command_QueryConfig = true; + AddLog(LOG_LEVEL_INFO, PSTR("SX1: ReadConfig sent...")); + return true; +#else + AddLog(LOG_LEVEL_INFO, PSTR("SX1: Command not available. Please set compiler directive '#define SOLAXX1_READCONFIG'.")); + return false; +#endif // SOLAXX1_READCONFIG + } + AddLog(LOG_LEVEL_INFO, PSTR("SX1: Unknown command: \"%s\""),XdrvMailbox.data); + return false; +} + +#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}%d " D_UNIT_HOUR "{e}" + "{s}" D_SOLAX_X1 " " D_STATUS "{m}%s" + "{s}" D_SOLAX_X1 " " D_ERROR "{m}%s" + "{s}" D_SOLAX_X1 " Inverter SN{m}%s"; +#endif // USE_WEBSERVER + +void solaxX1_Show(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 status[33]; + GetTextIndexed(status, sizeof(status), solaxX1.runMode + 1, 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 "\":%d,\"" D_JSON_RUNTIME "\":%d,\"" D_JSON_STATUS "\":\"%s\",\"" D_JSON_ERROR "\":%d"), + solaxX1.temperature, solaxX1.runtime_total, status, solaxX1.errorCode); + +#ifdef USE_DOMOTICZ + // Avoid bad temperature report at beginning of the day (spikes of 1200 celsius degrees) + if (0 == TasmotaGlobal.tele_period && solaxX1.temperature < 100) { DomoticzSensor(DZ_TEMP, solaxX1.temperature); } +#endif // USE_DOMOTICZ + +#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_Temp(D_SOLAX_X1, solaxX1.temperature); + char errorCodeString[33]; + WSContentSend_PD(HTTP_SNS_solaxX1_DATA3, solaxX1.runtime_total, status, + GetTextIndexed(errorCodeString, sizeof(errorCodeString), solaxX1_ParseErrorCode(solaxX1.errorCode), kSolaxError), + solaxX1.SerialNumber); +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xnrg12(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_EVERY_250_MSECOND: + solaxX1_250MSecond(); + break; + case FUNC_JSON_APPEND: + solaxX1_Show(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + solaxX1_Show(0); + break; +#endif // USE_WEBSERVER + case FUNC_INIT: + solaxX1_SnsInit(); + break; + case FUNC_PRE_INIT: + solaxX1_DrvInit(); + break; + case FUNC_COMMAND: + result = SolaxX1_cmd(); + break; + } + return result; +} + +#endif // USE_SOLAX_X1_NRG +#endif // USE_ENERGY_SENSOR diff --git a/tasmota/xnrg_13_fif_le01mr.ino b/tasmota/tasmota_xnrg_energy/xnrg_13_fif_le01mr.ino similarity index 100% rename from tasmota/xnrg_13_fif_le01mr.ino rename to tasmota/tasmota_xnrg_energy/xnrg_13_fif_le01mr.ino diff --git a/tasmota/xnrg_14_bl09xx.ino b/tasmota/tasmota_xnrg_energy/xnrg_14_bl09xx.ino similarity index 100% rename from tasmota/xnrg_14_bl09xx.ino rename to tasmota/tasmota_xnrg_energy/xnrg_14_bl09xx.ino diff --git a/tasmota/xnrg_15_teleinfo.ino b/tasmota/tasmota_xnrg_energy/xnrg_15_teleinfo.ino old mode 100755 new mode 100644 similarity index 100% rename from tasmota/xnrg_15_teleinfo.ino rename to tasmota/tasmota_xnrg_energy/xnrg_15_teleinfo.ino diff --git a/tasmota/xnrg_16_iem3000.ino b/tasmota/tasmota_xnrg_energy/xnrg_16_iem3000.ino similarity index 100% rename from tasmota/xnrg_16_iem3000.ino rename to tasmota/tasmota_xnrg_energy/xnrg_16_iem3000.ino diff --git a/tasmota/xnrg_17_ornowe517.ino b/tasmota/tasmota_xnrg_energy/xnrg_17_ornowe517.ino similarity index 100% rename from tasmota/xnrg_17_ornowe517.ino rename to tasmota/tasmota_xnrg_energy/xnrg_17_ornowe517.ino diff --git a/tasmota/xnrg_18_sdm72.ino b/tasmota/tasmota_xnrg_energy/xnrg_18_sdm72.ino similarity index 100% rename from tasmota/xnrg_18_sdm72.ino rename to tasmota/tasmota_xnrg_energy/xnrg_18_sdm72.ino diff --git a/tasmota/xnrg_19_cse7761.ino b/tasmota/tasmota_xnrg_energy/xnrg_19_cse7761.ino similarity index 100% rename from tasmota/xnrg_19_cse7761.ino rename to tasmota/tasmota_xnrg_energy/xnrg_19_cse7761.ino diff --git a/tasmota/xnrg_21_sdm230.ino b/tasmota/tasmota_xnrg_energy/xnrg_21_sdm230.ino similarity index 100% rename from tasmota/xnrg_21_sdm230.ino rename to tasmota/tasmota_xnrg_energy/xnrg_21_sdm230.ino diff --git a/tasmota/xnrg_22_bl6523.ino b/tasmota/tasmota_xnrg_energy/xnrg_22_bl6523.ino similarity index 100% rename from tasmota/xnrg_22_bl6523.ino rename to tasmota/tasmota_xnrg_energy/xnrg_22_bl6523.ino diff --git a/tasmota/xnrg_23_ade7880.ino b/tasmota/tasmota_xnrg_energy/xnrg_23_ade7880.ino similarity index 100% rename from tasmota/xnrg_23_ade7880.ino rename to tasmota/tasmota_xnrg_energy/xnrg_23_ade7880.ino diff --git a/tasmota/xnrg_30_dummy.ino b/tasmota/tasmota_xnrg_energy/xnrg_30_dummy.ino similarity index 100% rename from tasmota/xnrg_30_dummy.ino rename to tasmota/tasmota_xnrg_energy/xnrg_30_dummy.ino diff --git a/tasmota/xnrg_interface.ino b/tasmota/tasmota_xnrg_energy/xnrg_interface.ino similarity index 100% rename from tasmota/xnrg_interface.ino rename to tasmota/tasmota_xnrg_energy/xnrg_interface.ino diff --git a/tasmota/xsns_01_counter.ino b/tasmota/tasmota_xsns_sensor/xsns_01_counter.ino similarity index 100% rename from tasmota/xsns_01_counter.ino rename to tasmota/tasmota_xsns_sensor/xsns_01_counter.ino diff --git a/tasmota/xsns_02_analog.ino b/tasmota/tasmota_xsns_sensor/xsns_02_analog.ino similarity index 100% rename from tasmota/xsns_02_analog.ino rename to tasmota/tasmota_xsns_sensor/xsns_02_analog.ino diff --git a/tasmota/xsns_04_snfsc.ino b/tasmota/tasmota_xsns_sensor/xsns_04_snfsc.ino similarity index 100% rename from tasmota/xsns_04_snfsc.ino rename to tasmota/tasmota_xsns_sensor/xsns_04_snfsc.ino diff --git a/tasmota/xsns_05_ds18x20.ino b/tasmota/tasmota_xsns_sensor/xsns_05_ds18x20.ino similarity index 100% rename from tasmota/xsns_05_ds18x20.ino rename to tasmota/tasmota_xsns_sensor/xsns_05_ds18x20.ino diff --git a/tasmota/xsns_05_esp32_ds18x20.ino b/tasmota/tasmota_xsns_sensor/xsns_05_esp32_ds18x20.ino similarity index 100% rename from tasmota/xsns_05_esp32_ds18x20.ino rename to tasmota/tasmota_xsns_sensor/xsns_05_esp32_ds18x20.ino diff --git a/tasmota/xsns_06_dht.ino b/tasmota/tasmota_xsns_sensor/xsns_06_dht.ino similarity index 100% rename from tasmota/xsns_06_dht.ino rename to tasmota/tasmota_xsns_sensor/xsns_06_dht.ino diff --git a/tasmota/xsns_06_esp32_dht.ino b/tasmota/tasmota_xsns_sensor/xsns_06_esp32_dht.ino similarity index 100% rename from tasmota/xsns_06_esp32_dht.ino rename to tasmota/tasmota_xsns_sensor/xsns_06_esp32_dht.ino diff --git a/tasmota/xsns_07_sht1x.ino b/tasmota/tasmota_xsns_sensor/xsns_07_sht1x.ino similarity index 100% rename from tasmota/xsns_07_sht1x.ino rename to tasmota/tasmota_xsns_sensor/xsns_07_sht1x.ino diff --git a/tasmota/xsns_08_htu21.ino b/tasmota/tasmota_xsns_sensor/xsns_08_htu21.ino similarity index 100% rename from tasmota/xsns_08_htu21.ino rename to tasmota/tasmota_xsns_sensor/xsns_08_htu21.ino diff --git a/tasmota/xsns_09_bmp.ino b/tasmota/tasmota_xsns_sensor/xsns_09_bmp.ino similarity index 100% rename from tasmota/xsns_09_bmp.ino rename to tasmota/tasmota_xsns_sensor/xsns_09_bmp.ino diff --git a/tasmota/xsns_10_bh1750.ino b/tasmota/tasmota_xsns_sensor/xsns_10_bh1750.ino similarity index 100% rename from tasmota/xsns_10_bh1750.ino rename to tasmota/tasmota_xsns_sensor/xsns_10_bh1750.ino diff --git a/tasmota/xsns_11_veml6070.ino b/tasmota/tasmota_xsns_sensor/xsns_11_veml6070.ino similarity index 100% rename from tasmota/xsns_11_veml6070.ino rename to tasmota/tasmota_xsns_sensor/xsns_11_veml6070.ino diff --git a/tasmota/xsns_127_esp32_sensors.ino b/tasmota/tasmota_xsns_sensor/xsns_127_esp32_sensors.ino similarity index 100% rename from tasmota/xsns_127_esp32_sensors.ino rename to tasmota/tasmota_xsns_sensor/xsns_127_esp32_sensors.ino diff --git a/tasmota/xsns_12_ads1115.ino b/tasmota/tasmota_xsns_sensor/xsns_12_ads1115.ino similarity index 100% rename from tasmota/xsns_12_ads1115.ino rename to tasmota/tasmota_xsns_sensor/xsns_12_ads1115.ino diff --git a/tasmota/xsns_13_ina219.ino b/tasmota/tasmota_xsns_sensor/xsns_13_ina219.ino similarity index 100% rename from tasmota/xsns_13_ina219.ino rename to tasmota/tasmota_xsns_sensor/xsns_13_ina219.ino diff --git a/tasmota/xsns_14_sht3x.ino b/tasmota/tasmota_xsns_sensor/xsns_14_sht3x.ino similarity index 100% rename from tasmota/xsns_14_sht3x.ino rename to tasmota/tasmota_xsns_sensor/xsns_14_sht3x.ino diff --git a/tasmota/xsns_15_mhz19.ino b/tasmota/tasmota_xsns_sensor/xsns_15_mhz19.ino similarity index 100% rename from tasmota/xsns_15_mhz19.ino rename to tasmota/tasmota_xsns_sensor/xsns_15_mhz19.ino diff --git a/tasmota/xsns_16_tsl2561.ino b/tasmota/tasmota_xsns_sensor/xsns_16_tsl2561.ino similarity index 100% rename from tasmota/xsns_16_tsl2561.ino rename to tasmota/tasmota_xsns_sensor/xsns_16_tsl2561.ino diff --git a/tasmota/xsns_17_senseair.ino b/tasmota/tasmota_xsns_sensor/xsns_17_senseair.ino similarity index 100% rename from tasmota/xsns_17_senseair.ino rename to tasmota/tasmota_xsns_sensor/xsns_17_senseair.ino diff --git a/tasmota/xsns_18_pms5003.ino b/tasmota/tasmota_xsns_sensor/xsns_18_pms5003.ino similarity index 100% rename from tasmota/xsns_18_pms5003.ino rename to tasmota/tasmota_xsns_sensor/xsns_18_pms5003.ino diff --git a/tasmota/xsns_19_mgs.ino b/tasmota/tasmota_xsns_sensor/xsns_19_mgs.ino similarity index 100% rename from tasmota/xsns_19_mgs.ino rename to tasmota/tasmota_xsns_sensor/xsns_19_mgs.ino diff --git a/tasmota/xsns_20_novasds.ino b/tasmota/tasmota_xsns_sensor/xsns_20_novasds.ino similarity index 100% rename from tasmota/xsns_20_novasds.ino rename to tasmota/tasmota_xsns_sensor/xsns_20_novasds.ino diff --git a/tasmota/xsns_21_sgp30.ino b/tasmota/tasmota_xsns_sensor/xsns_21_sgp30.ino similarity index 100% rename from tasmota/xsns_21_sgp30.ino rename to tasmota/tasmota_xsns_sensor/xsns_21_sgp30.ino diff --git a/tasmota/xsns_22_sr04.ino b/tasmota/tasmota_xsns_sensor/xsns_22_sr04.ino similarity index 100% rename from tasmota/xsns_22_sr04.ino rename to tasmota/tasmota_xsns_sensor/xsns_22_sr04.ino diff --git a/tasmota/xsns_24_si1145.ino b/tasmota/tasmota_xsns_sensor/xsns_24_si1145.ino similarity index 100% rename from tasmota/xsns_24_si1145.ino rename to tasmota/tasmota_xsns_sensor/xsns_24_si1145.ino diff --git a/tasmota/xsns_26_lm75ad.ino b/tasmota/tasmota_xsns_sensor/xsns_26_lm75ad.ino similarity index 100% rename from tasmota/xsns_26_lm75ad.ino rename to tasmota/tasmota_xsns_sensor/xsns_26_lm75ad.ino diff --git a/tasmota/xsns_27_apds9960.ino b/tasmota/tasmota_xsns_sensor/xsns_27_apds9960.ino similarity index 100% rename from tasmota/xsns_27_apds9960.ino rename to tasmota/tasmota_xsns_sensor/xsns_27_apds9960.ino diff --git a/tasmota/xsns_28_tm1638.ino b/tasmota/tasmota_xsns_sensor/xsns_28_tm1638.ino similarity index 100% rename from tasmota/xsns_28_tm1638.ino rename to tasmota/tasmota_xsns_sensor/xsns_28_tm1638.ino diff --git a/tasmota/xsns_29_mcp230xx.ino b/tasmota/tasmota_xsns_sensor/xsns_29_mcp230xx.ino similarity index 100% rename from tasmota/xsns_29_mcp230xx.ino rename to tasmota/tasmota_xsns_sensor/xsns_29_mcp230xx.ino diff --git a/tasmota/xsns_30_mpr121.ino b/tasmota/tasmota_xsns_sensor/xsns_30_mpr121.ino similarity index 100% rename from tasmota/xsns_30_mpr121.ino rename to tasmota/tasmota_xsns_sensor/xsns_30_mpr121.ino diff --git a/tasmota/xsns_31_ccs811.ino b/tasmota/tasmota_xsns_sensor/xsns_31_ccs811.ino similarity index 100% rename from tasmota/xsns_31_ccs811.ino rename to tasmota/tasmota_xsns_sensor/xsns_31_ccs811.ino diff --git a/tasmota/xsns_31_ccs811_v2.ino b/tasmota/tasmota_xsns_sensor/xsns_31_ccs811_v2.ino similarity index 100% rename from tasmota/xsns_31_ccs811_v2.ino rename to tasmota/tasmota_xsns_sensor/xsns_31_ccs811_v2.ino diff --git a/tasmota/xsns_32_mpu6050.ino b/tasmota/tasmota_xsns_sensor/xsns_32_mpu6050.ino similarity index 100% rename from tasmota/xsns_32_mpu6050.ino rename to tasmota/tasmota_xsns_sensor/xsns_32_mpu6050.ino diff --git a/tasmota/xsns_34_hx711.ino b/tasmota/tasmota_xsns_sensor/xsns_34_hx711.ino similarity index 100% rename from tasmota/xsns_34_hx711.ino rename to tasmota/tasmota_xsns_sensor/xsns_34_hx711.ino diff --git a/tasmota/xsns_35_tx20.ino b/tasmota/tasmota_xsns_sensor/xsns_35_tx20.ino similarity index 100% rename from tasmota/xsns_35_tx20.ino rename to tasmota/tasmota_xsns_sensor/xsns_35_tx20.ino diff --git a/tasmota/xsns_36_mgc3130.ino b/tasmota/tasmota_xsns_sensor/xsns_36_mgc3130.ino similarity index 100% rename from tasmota/xsns_36_mgc3130.ino rename to tasmota/tasmota_xsns_sensor/xsns_36_mgc3130.ino diff --git a/tasmota/xsns_37_rfsensor.ino b/tasmota/tasmota_xsns_sensor/xsns_37_rfsensor.ino similarity index 100% rename from tasmota/xsns_37_rfsensor.ino rename to tasmota/tasmota_xsns_sensor/xsns_37_rfsensor.ino diff --git a/tasmota/xsns_38_az7798.ino b/tasmota/tasmota_xsns_sensor/xsns_38_az7798.ino similarity index 100% rename from tasmota/xsns_38_az7798.ino rename to tasmota/tasmota_xsns_sensor/xsns_38_az7798.ino diff --git a/tasmota/xsns_39_max31855.ino b/tasmota/tasmota_xsns_sensor/xsns_39_max31855.ino similarity index 100% rename from tasmota/xsns_39_max31855.ino rename to tasmota/tasmota_xsns_sensor/xsns_39_max31855.ino diff --git a/tasmota/xsns_40_pn532.ino b/tasmota/tasmota_xsns_sensor/xsns_40_pn532.ino similarity index 100% rename from tasmota/xsns_40_pn532.ino rename to tasmota/tasmota_xsns_sensor/xsns_40_pn532.ino diff --git a/tasmota/xsns_41_max44009.ino b/tasmota/tasmota_xsns_sensor/xsns_41_max44009.ino similarity index 100% rename from tasmota/xsns_41_max44009.ino rename to tasmota/tasmota_xsns_sensor/xsns_41_max44009.ino diff --git a/tasmota/xsns_42_scd30.ino b/tasmota/tasmota_xsns_sensor/xsns_42_scd30.ino similarity index 100% rename from tasmota/xsns_42_scd30.ino rename to tasmota/tasmota_xsns_sensor/xsns_42_scd30.ino diff --git a/tasmota/xsns_43_hre.ino b/tasmota/tasmota_xsns_sensor/xsns_43_hre.ino similarity index 100% rename from tasmota/xsns_43_hre.ino rename to tasmota/tasmota_xsns_sensor/xsns_43_hre.ino diff --git a/tasmota/xsns_44_sps30.ino b/tasmota/tasmota_xsns_sensor/xsns_44_sps30.ino similarity index 100% rename from tasmota/xsns_44_sps30.ino rename to tasmota/tasmota_xsns_sensor/xsns_44_sps30.ino diff --git a/tasmota/xsns_45_vl53l0x.ino b/tasmota/tasmota_xsns_sensor/xsns_45_vl53l0x.ino similarity index 100% rename from tasmota/xsns_45_vl53l0x.ino rename to tasmota/tasmota_xsns_sensor/xsns_45_vl53l0x.ino diff --git a/tasmota/xsns_46_MLX90614.ino b/tasmota/tasmota_xsns_sensor/xsns_46_MLX90614.ino similarity index 100% rename from tasmota/xsns_46_MLX90614.ino rename to tasmota/tasmota_xsns_sensor/xsns_46_MLX90614.ino diff --git a/tasmota/xsns_47_max31865.ino b/tasmota/tasmota_xsns_sensor/xsns_47_max31865.ino similarity index 100% rename from tasmota/xsns_47_max31865.ino rename to tasmota/tasmota_xsns_sensor/xsns_47_max31865.ino diff --git a/tasmota/xsns_48_chirp.ino b/tasmota/tasmota_xsns_sensor/xsns_48_chirp.ino similarity index 100% rename from tasmota/xsns_48_chirp.ino rename to tasmota/tasmota_xsns_sensor/xsns_48_chirp.ino diff --git a/tasmota/xsns_50_paj7620.ino b/tasmota/tasmota_xsns_sensor/xsns_50_paj7620.ino similarity index 100% rename from tasmota/xsns_50_paj7620.ino rename to tasmota/tasmota_xsns_sensor/xsns_50_paj7620.ino diff --git a/tasmota/xsns_51_rdm6300.ino b/tasmota/tasmota_xsns_sensor/xsns_51_rdm6300.ino similarity index 100% rename from tasmota/xsns_51_rdm6300.ino rename to tasmota/tasmota_xsns_sensor/xsns_51_rdm6300.ino diff --git a/tasmota/xsns_52_esp32_ibeacon_ble.ino b/tasmota/tasmota_xsns_sensor/xsns_52_esp32_ibeacon_ble.ino similarity index 100% rename from tasmota/xsns_52_esp32_ibeacon_ble.ino rename to tasmota/tasmota_xsns_sensor/xsns_52_esp32_ibeacon_ble.ino diff --git a/tasmota/xsns_52_ibeacon.ino b/tasmota/tasmota_xsns_sensor/xsns_52_ibeacon.ino old mode 100755 new mode 100644 similarity index 100% rename from tasmota/xsns_52_ibeacon.ino rename to tasmota/tasmota_xsns_sensor/xsns_52_ibeacon.ino diff --git a/tasmota/xsns_53_sml.ino b/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino old mode 100755 new mode 100644 similarity index 100% rename from tasmota/xsns_53_sml.ino rename to tasmota/tasmota_xsns_sensor/xsns_53_sml.ino diff --git a/tasmota/xsns_54_ina226.ino b/tasmota/tasmota_xsns_sensor/xsns_54_ina226.ino similarity index 100% rename from tasmota/xsns_54_ina226.ino rename to tasmota/tasmota_xsns_sensor/xsns_54_ina226.ino diff --git a/tasmota/xsns_55_hih_series.ino b/tasmota/tasmota_xsns_sensor/xsns_55_hih_series.ino similarity index 100% rename from tasmota/xsns_55_hih_series.ino rename to tasmota/tasmota_xsns_sensor/xsns_55_hih_series.ino diff --git a/tasmota/xsns_56_hpma.ino b/tasmota/tasmota_xsns_sensor/xsns_56_hpma.ino similarity index 100% rename from tasmota/xsns_56_hpma.ino rename to tasmota/tasmota_xsns_sensor/xsns_56_hpma.ino diff --git a/tasmota/xsns_57_tsl2591.ino b/tasmota/tasmota_xsns_sensor/xsns_57_tsl2591.ino similarity index 100% rename from tasmota/xsns_57_tsl2591.ino rename to tasmota/tasmota_xsns_sensor/xsns_57_tsl2591.ino diff --git a/tasmota/xsns_58_dht12.ino b/tasmota/tasmota_xsns_sensor/xsns_58_dht12.ino similarity index 100% rename from tasmota/xsns_58_dht12.ino rename to tasmota/tasmota_xsns_sensor/xsns_58_dht12.ino diff --git a/tasmota/xsns_59_ds1624.ino b/tasmota/tasmota_xsns_sensor/xsns_59_ds1624.ino similarity index 100% rename from tasmota/xsns_59_ds1624.ino rename to tasmota/tasmota_xsns_sensor/xsns_59_ds1624.ino diff --git a/tasmota/xsns_60_GPS.ino b/tasmota/tasmota_xsns_sensor/xsns_60_GPS.ino similarity index 97% rename from tasmota/xsns_60_GPS.ino rename to tasmota/tasmota_xsns_sensor/xsns_60_GPS.ino index 7108f9757..edf3d9c81 100644 --- a/tasmota/xsns_60_GPS.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_60_GPS.ino @@ -1,930 +1,930 @@ -/* - xsns_60_GPS.ino - GPS UBLOX support for Tasmota - - Copyright (C) 2021 Theo Arends, Christian Baars and Adrian Scillato - - 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_GPS -#if defined(ESP32) && defined(USE_FLOG) - #undef USE_FLOG - #warning FLOG deactivated on ESP32 -#endif //ESP32 -/*********************************************************************************************\ - -------------------------------------------------------------------------------------------- - Version Date Action Description - -------------------------------------------------------------------------------------------- - - 0.9.3.0 20200214 integrate - fix set lat/lon via commandd 13, V-Port now works parallel - --- - 0.9.2.0 20200110 integrate - Added UART-over-TCP/IP-bridge (virtual serial port). Minor tweaks. - --- - 0.9.1.0 20191216 integrate - Added pin specifications from Tasmota WEB UI. Minor tweaks. - --- - 0.9.0.0 20190817 started - further development by Christian Baars - https://github.com/Staars/Sonoff-Tasmota - forked - from arendst/tasmota - https://github.com/arendst/Sonoff-Tasmota - base - code base from arendst and - https://www.youtube.com/watch?v=TwhCX0c8Xe0 - -## GPS-driver for the Ublox-series 6-8 -Driver is tested on a NEO-6m and a Beitian-220. Series 7 should work too. This adds only about 6kb to the program size, because the efficient UBX-protocol is used. These modules are quite cheap, starting at about 3.50€ for the NEO-6m. - -## Features: -- get position and time data -- sets system time automatically and Settings->latitude and Settings->longitude via command -- can log postion data with timestamp to flash with a small memory footprint of only 12 Bytes per record -- constructs a GPX-file for download of this data -- Web-UI -- simplified NTP-server and UART-over-TCP/IP-bridge (virtual serial port) -- command interface - -## Usage: -The serial pins are GPS_RX and GPS_TX, no further installation steps needed. To get more debug information compile it with option "DEBUG_TASMOTA_SENSOR". - - -## Commands: - -+ sensor60 0 - write to all available sectors, then restart and overwrite the older ones - -+ sensor60 1 - write to all available sectors, then restart and overwrite the older ones - -+ sensor60 2 - filter out horizontal drift noise - -+ sensor60 3 - turn off noise filter - -+ sensor60 4 - start recording, new data will be appended - -+ sensor60 5 - start new recording, old data will lost - -+ sensor60 6 - stop recording, download link will be visible in Web-UI - -+ sensor60 7 - send mqtt on new postion + TELE -> consider to set TELE to a very high value - -+ sensor60 8 - only TELE message - -+ sensor60 9 - start NTP-server - -+ sensor60 10 - deactivate NTP-server - -+ sensor60 11 - force update of Tasmota-system-UTC with every new GPS-time-message - -+ sensor60 12 - do not update of Tasmota-system-UTC with every new GPS-time-message - -+ sensor60 13 - set latitude and longitude in settings - -+ sensor60 14 - open virtual serial port over TCP, usable for u-center - -+ sensor60 15 - pause virtual serial port over TCP - -## Rules examples for SSD1306 32x128 - - -rule1 on tele-GPS#lat do DisplayText [s1p21c1l01f1]LAT: %value% endon on tele-GPS#lon do DisplayText [s1p21c1l2]LON: %value% endon on switch1#state==3 do sensor60 4 endon on switch1#state==2 do sensor60 6 endon - -rule2 on tele-GPS#int>9 do DisplayText [f0c9l4]I%value% endon on tele-GPS#int<10 do DisplayText [f0c9l4]I0%value% endon on tele-GPS#fil==1 do DisplayText [f0c18l4]F endon on tele-GPS#fil==0 do DisplayText [f0c18l4]N endon - -rule3 on tele-FLOG#sec do DisplayText [f0c1l4]SAV:%value% endon on tele-FLOG#rec==1 do DisplayText [f0c1l4]REC: endon on tele-FLOG#mode do DisplayText [f0c14l4]M%value% endon - -\*********************************************************************************************/ - -#define XSNS_60 60 - -#include "NTPServer.h" -#include "NTPPacket.h" - -/*********************************************************************************************\ - * constants -\*********************************************************************************************/ - -#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 // filter out some noise of local drift - -#define UBX_SERIAL_BUFFER_SIZE 256 -#define UBX_TCP_PORT 1234 -#define NTP_MILLIS_OFFSET 50 // estimated latency in milliseconds - -/********************************************************************************************\ -| *globals -\*********************************************************************************************/ - -const char UBLOX_INIT[] PROGMEM = { - // Disable NMEA - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x24, // GxGGA off - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x2B, // GxGLL off - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x32, // GxGSA off - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x39, // GxGSV off - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x04,0x40, // GxRMC off - 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x05,0x47, // GxVTG off - - // Disable UBX - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xDC, //NAV-PVT off - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xB9, //NAV-POSLLH off - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xC0, //NAV-STATUS off - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x92, //NAV-TIMEUTC off - - // Enable UBX - // 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x01,0x00,0x00,0x00,0x00,0x18,0xE1, //NAV-PVT on - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x13,0xBE, //NAV-POSLLH on - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x14,0xC5, //NAV-STATUS on - 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x01,0x00,0x00,0x00,0x00,0x32,0x97, //NAV-TIMEUTC on - - // Rate - we will not reset it for the moment after restart - // 0xB5,0x62,0x06,0x08,0x06,0x00,0x64,0x00,0x01,0x00,0x01,0x00,0x7A,0x12, //(10Hz) - // 0xB5,0x62,0x06,0x08,0x06,0x00,0xC8,0x00,0x01,0x00,0x01,0x00,0xDE,0x6A, //(5Hz) - // 0xB5,0x62,0x06,0x08,0x06,0x00,0xE8,0x03,0x01,0x00,0x01,0x00,0x01,0x39 //(1Hz) - // 0xB5,0x62,0x06,0x08,0x06,0x00,0xD0,0x07,0x01,0x00,0x01,0x00,0xED,0xBD //(0.5Hz) -}; - -char UBX_name[4]; - -struct UBX_t { - const char UBX_HEADER[2] = { 0xB5, 0x62 }; // TODO: Check if we really save space here inside the struct - 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; //raw sensor value - int32_t lon; //raw sensor value - uint32_t time; //local time from system (maybe provided by the sensor) - }; - - 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; //bit 0 - gpsfix valid - 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; // Nanoseconds of second, range -1e9 .. 1e9 (UTC) - 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; // week number - uint8_t TOW:1; // time of week - uint8_t padding:5; - } valid; - }; - - struct CFG_RATE { - uint8_t cls; //0x06 - uint8_t id; //0x08 - uint16_t len; // 6 bytes - uint16_t measRate; // in every ms -> 1 Hz = 1000 ms; 10 Hz = 100 ms -> x = 1000 ms / Hz - uint16_t navRate; // x measurements for 1 navigation event - uint16_t timeRef; // align to time system: 0= UTC, 1 = GPS, 2 = GLONASS, ... - char CK[2]; // checksum - }; - - 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; // in case of an unintended reset of the GPS, the serial interface will get flooded with NMEA - uint16_t log_interval; // in tenth of seconds - int32_t timeOffset; // roughly computed offset millis() - iTOW - } state; - - struct { - uint32_t init:1; - uint32_t filter_noise:1; - uint32_t send_when_new:1; // no teleinterval - uint32_t send_UI_only:1; - uint32_t runningNTP:1; - // uint32_t blockedNTP:1; - uint32_t forceUTCupdate:1; - uint32_t runningVPort:1; - // TODO: more to come - } mode; - - union { - NAV_POSLLH navPosllh; - NAV_STATUS navStatus; - NAV_TIME_UTC navTime; - POLL_MSG pollMsg; - CFG_RATE cfgRate; - } Message; - - uint32_t utc_time; - - 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 //USE_FLOG -TasmotaSerial *UBXSerial; - -NtpServer timeServer(PortUdp); - -WiFiServer vPortServer(UBX_TCP_PORT); -WiFiClient vPortClient; - -/*********************************************************************************************\ - * helper function -\*********************************************************************************************/ - -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 UBXsendCFGLine(uint8_t _line) -{ - if (_line>sizeof(UBLOX_INIT)/16) return; - for (uint32_t i = 0; i < 16; i++) { - UBXSerial->write( pgm_read_byte(UBLOX_INIT+i+(_line*16)) ); - } - DEBUG_SENSOR_LOG(PSTR("UBX: send line %u of UBLOX_INIT"), _line); -} - -/********************************************************************************************/ - -void UBXDetect(void) -{ - UBX.mode.init = 0; - if (PinUsed(GPIO_GPS_RX) && PinUsed(GPIO_GPS_TX)) { - UBXSerial = new TasmotaSerial(Pin(GPIO_GPS_RX), Pin(GPIO_GPS_TX), 1, 0, UBX_SERIAL_BUFFER_SIZE); // 64 byte buffer is NOT enough - 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(); // turn off NMEA, only use "our" UBX-messages - UBX.mode.init = 1; - -#ifdef USE_FLOG - if (!Flog) { - Flog = new FLOG; // init Flash Log - Flog->init(); - } -#endif // USE_FLOG - - UBX.state.log_interval = 10; // 1 second - UBX.mode.send_UI_only = true; // send UI data ... -// MqttPublishTeleperiodSensor(); // ... once at after start (No MQTT ready yet so do NOT try to send) -} - -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); - - // DEBUG_SENSOR_LOG(PSTR("UBX: check for serial data")); - uint32_t data_bytes = 0; - while ( UBXSerial->available() ) { - data_bytes++; - byte c = UBXSerial->read(); - if (UBX.mode.runningVPort){ - UBX.TCPbuf[data_bytes-1] = c; // immediately copy byte to TCP-buf - UBX.TCPbufSize = data_bytes; - } - if ( fpos < 2 ) { - // For the first two bytes we are simply looking for a match with the UBX header bytes (0xB5,0x62) - if ( c == UBX.UBX_HEADER[fpos] ) { - fpos++; - } else { - fpos = 0; // Reset to beginning state. - } - } else { - // If we come here then fpos >= 2, which means we have found a match with the UBX_HEADER - // and we are now reading in the bytes that make up the payload. - - // Place the incoming byte into the ubxMessage struct. The position is fpos-2 because - // the struct does not include the initial two-byte header (UBX_HEADER). - if ( (fpos-2) < payloadSize ) { - ((char*)(&UBX.Message))[fpos-2] = c; - } - fpos++; - - if ( fpos == 4 ) { - // We have just received the second byte of the message type header, - // so now we can check to see what kind of message it is. - 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 { - // unknown message type, bail - fpos = 0; - continue; - } - } - - if ( fpos == (payloadSize+2) ) { - // All payload bytes have now been received, so we can calculate the - // expected checksum value to compare with the next two incoming bytes. - UBXcalcChecksum(checksum, payloadSize); - } - else if ( fpos == (payloadSize+3) ) { - // First byte after the payload, ie. first byte of the checksum. - // Does it match the first byte of the checksum we calculated? - if ( c != checksum[0] ) { - // Checksum doesn't match, reset to beginning state and try again. - fpos = 0; - } - } - else if ( fpos == (payloadSize+4) ) { - // Second byte after the payload, ie. second byte of the checksum. - // Does it match the second byte of the checksum we calculated? - fpos = 0; // We will reset the state regardless of whether the checksum matches. - if ( c == checksum[1] ) { - // Checksum matches, we have a valid message. - return currentMsgType; - } - } - else if ( fpos > (payloadSize+4) ) { - // We have now read more bytes than both the expected payload and checksum - // together, so something went wrong. Reset to beginning state and try again. - fpos = 0; - } - } - } - // DEBUG_SENSOR_LOG(PSTR("UBX: got none or unknown Message")); - 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; // now a hidden GPS-device reset is unlikely - } - return MT_NONE; -} - -/********************************************************************************************\ -| * callback functions for the download -\*********************************************************************************************/ - -#ifdef USE_FLOG -void UBXsendHeader(void) -{ - Webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); - Webserver->sendHeader(F("Content-Disposition"), F("attachment; filename=TASMOTA.gpx")); - WSSend(200, CT_APP_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); - // DEBUG_SENSOR_LOG(PSTR("FLOG: DL %u %u"), Flog->sector.dword_buffer[k+j],Flog->sector.dword_buffer[k+j+1]); - Webserver->sendContent_P(record); -} - -void UBXsendFooter(void) -{ - Webserver->sendContent(F("\n\n")); - Webserver->sendContent(""); - Rtc.user_time_entry = false; // we have blocked the main loop and want a new valid time -} - -/********************************************************************************************/ - -void UBXsendFile(void) -{ - if (!HttpCheckPriviledgedAccess()) { return; } - Flog->startDownload(sizeof(UBX.rec_buffer),UBXsendHeader,UBXsendRecord,UBXsendFooter); -} -#endif //USE_FLOG - -/********************************************************************************************/ - -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); //seconds to milliseconds - if (measRate > 0xffff) { - measRate = 0xffff; // max. 65535 ms interval - } - 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; // write once to all available sectors, then stop - break; - case 1: - Flog->mode = 1; // write to all available sectors, then restart and overwrite the older ones - break; - case 2: - UBX.mode.filter_noise = true; // filter out horizontal drift noise, TODO: find useful values - break; - case 3: - UBX.mode.filter_noise = false; - break; - case 4: - Flog->startRecording(true); - AddLog(LOG_LEVEL_INFO, PSTR("UBX: start recording - appending")); - break; - case 5: - Flog->startRecording(false); - AddLog(LOG_LEVEL_INFO, PSTR("UBX: start recording - new log")); - break; - case 6: - if(Flog->recording == true){ - Flog->stopRecording(); - } - AddLog(LOG_LEVEL_INFO, PSTR("UBX: stop recording")); - break; -#endif //USE_FLOG - case 7: - UBX.mode.send_when_new = 1; // send mqtt on new postion + TELE -> consider to set TELE to a very high value - break; - case 8: - UBX.mode.send_when_new = 0; // only TELE - break; - case 9: - if (timeServer.beginListening()) { - UBX.mode.runningNTP = true; - } - break; - case 10: - UBX.mode.runningNTP = false; - UBXsendCFGLine(10); //NAV-POSLLH on - UBXsendCFGLine(11); //NAV-STATUS on - 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: - // vPortServer.stop(); // seems not to work reliably - UBX.mode.runningVPort = 0; - break; - default: - if (mode>1000 && mode <1066) { - UBXSetRate(mode-1000); // set interval between measurements in seconds from 1 to 65 - } - break; - } - UBX.mode.send_UI_only = true; - MqttPublishTeleperiodSensor(); -} - -/********************************************************************************************/ - -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.lat go to pure NTP-mode - UBXsendCFGLine(7); //NAV-POSLLH off - UBXsendCFGLine(8); //NAV-STATUS off - } - return true; // new position - } else { - DEBUG_SENSOR_LOG(PSTR("UBX: no valid position data")); - } - return false; // no GPS-fix -} - -void UBXHandleSTATUS() -{ - DEBUG_SENSOR_LOG(PSTR("UBX: gpsFix: %u, valid: %u"), UBX.Message.navStatus.gpsFix, (UBX.Message.navStatus.flags)&1); - if ((UBX.Message.navStatus.flags)&1) { - UBX.state.gpsFix = UBX.Message.navStatus.gpsFix; //only store fixed status if flag is valid - } else { - UBX.state.gpsFix = 0; // without valid flag, everything is "no fix" - } -} - -void UBXHandleTIME() -{ - DEBUG_SENSOR_LOG(PSTR("UBX: UTC-Time: %u-%u-%u %u:%u:%u"), UBX.Message.navTime.year, UBX.Message.navTime.month ,UBX.Message.navTime.day,UBX.Message.navTime.hour,UBX.Message.navTime.min,UBX.Message.navTime.sec); - if (UBX.Message.navTime.valid.UTC == 1) { - UBX.state.timeOffset = millis(); // iTOW%1000 should be 0 here, when NTP-server is enabled and in "pure mode" - DEBUG_SENSOR_LOG(PSTR("UBX: UTC-Time is valid")); - bool resync = (Rtc.utc_time > UBX.utc_time); // Sync local time every hour - if (Rtc.user_time_entry == false || UBX.mode.forceUTCupdate || UBX.mode.runningNTP || resync) { - TIME_T gpsTime; - gpsTime.year = UBX.Message.navTime.year - 1970; - gpsTime.month = UBX.Message.navTime.month; - gpsTime.day_of_month = UBX.Message.navTime.day; - gpsTime.hour = UBX.Message.navTime.hour; - gpsTime.minute = UBX.Message.navTime.min; - gpsTime.second = UBX.Message.navTime.sec; - UBX.rec_buffer.values.time = MakeTime(gpsTime); - if (UBX.mode.forceUTCupdate || (Rtc.user_time_entry == false) || resync) { -// AddLog(LOG_LEVEL_INFO, PSTR("UBX: UTC-Time is valid, set system time")); - UBX.utc_time = UBX.rec_buffer.values.time + 3600; - Rtc.utc_time = UBX.rec_buffer.values.time; - RtcSync("UBX"); - } - Rtc.user_time_entry = true; - } - } -} - -void UBXHandleOther(void) -{ - if (UBX.state.non_empty_loops>6) { // we expect only 4-5 non-empty loops in a row, could change with other sensor speed (Hz) - if(UBX.mode.runningVPort) return; - UBXinitCFG(); // this should only happen with lots of NMEA-messages, but it is only a guess!! - AddLog(LOG_LEVEL_ERROR, PSTR("UBX: possible device-reset, will re-init")); - UBXSerial->flush(); - UBX.state.non_empty_loops = 0; - } -} - -/********************************************************************************************/ - -void UBXLoop50msec(void) -{ - // handle virtual serial port - 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; - } - } - // handle NTP-server - if(UBX.mode.runningNTP){ - timeServer.processOneRequest(UBX.rec_buffer.values.time, UBX.state.timeOffset - NTP_MILLIS_OFFSET); - } -} - -void UBXLoop(void) -{ - static uint16_t counter; //count up every 100 msec - 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 // USE_FLOG - - counter++; -} - -/********************************************************************************************/ -// normaly in i18n.h - -#ifdef USE_WEBSERVER - // {s} = , {m} = , {e} = - -#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 // DEBUG_TASMOTA_SENSOR - - 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 //USE_FLOG - 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}; - -// const char UBX_GOOGLE_MAPS[] =""; - - -#endif // USE_WEBSERVER - -/********************************************************************************************/ - -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 //USE_FLOG - UBX.mode.send_UI_only = false; -#ifdef USE_WEBSERVER - } else { - WSContentSend_PD(HTTP_SNS_GPS, lat, lon, alt, hAcc, vAcc, kGPSFix[UBX.state.gpsFix]); - //WSContentSend_P(UBX_GOOGLE_MAPS, lat, lon); -#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 //USE_FLOG -#endif // DEBUG_TASMOTA_SENSOR -#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 //USE_FLOG - if (UBX.mode.runningNTP) { - WSContentSend_P(HTTP_SNS_NTPSERVER); - } -#endif // USE_WEBSERVER - } -} - -/*********************************************************************************************\ - * check the UBX commands -\*********************************************************************************************/ - -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; -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -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(); // handles virtual serial port and NTP server - break; - case FUNC_EVERY_100_MSECOND: -#ifdef USE_FLOG - if (!Flog->running_download) -#endif //USE_FLOG - { - UBXLoop(); - } - break; -#ifdef USE_FLOG - case FUNC_WEB_ADD_HANDLER: - WebServer_on(PSTR("/UBX"), UBXsendFile); - break; -#endif //USE_FLOG - case FUNC_JSON_APPEND: - UBXShow(1); - break; -#ifdef USE_WEBSERVER - case FUNC_WEB_SENSOR: -#ifdef USE_FLOG - if (!Flog->running_download) -#endif //USE_FLOG - { - UBXShow(0); - } - break; -#endif // USE_WEBSERVER - } - } - return result; -} - +/* + xsns_60_GPS.ino - GPS UBLOX support for Tasmota + + Copyright (C) 2021 Theo Arends, Christian Baars and Adrian Scillato + + 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_GPS +#if defined(ESP32) && defined(USE_FLOG) + #undef USE_FLOG + #warning FLOG deactivated on ESP32 +#endif //ESP32 +/*********************************************************************************************\ + -------------------------------------------------------------------------------------------- + Version Date Action Description + -------------------------------------------------------------------------------------------- + + 0.9.3.0 20200214 integrate - fix set lat/lon via commandd 13, V-Port now works parallel + --- + 0.9.2.0 20200110 integrate - Added UART-over-TCP/IP-bridge (virtual serial port). Minor tweaks. + --- + 0.9.1.0 20191216 integrate - Added pin specifications from Tasmota WEB UI. Minor tweaks. + --- + 0.9.0.0 20190817 started - further development by Christian Baars - https://github.com/Staars/Sonoff-Tasmota + forked - from arendst/tasmota - https://github.com/arendst/Sonoff-Tasmota + base - code base from arendst and - https://www.youtube.com/watch?v=TwhCX0c8Xe0 + +## GPS-driver for the Ublox-series 6-8 +Driver is tested on a NEO-6m and a Beitian-220. Series 7 should work too. This adds only about 6kb to the program size, because the efficient UBX-protocol is used. These modules are quite cheap, starting at about 3.50€ for the NEO-6m. + +## Features: +- get position and time data +- sets system time automatically and Settings->latitude and Settings->longitude via command +- can log postion data with timestamp to flash with a small memory footprint of only 12 Bytes per record +- constructs a GPX-file for download of this data +- Web-UI +- simplified NTP-server and UART-over-TCP/IP-bridge (virtual serial port) +- command interface + +## Usage: +The serial pins are GPS_RX and GPS_TX, no further installation steps needed. To get more debug information compile it with option "DEBUG_TASMOTA_SENSOR". + + +## Commands: + ++ sensor60 0 + write to all available sectors, then restart and overwrite the older ones + ++ sensor60 1 + write to all available sectors, then restart and overwrite the older ones + ++ sensor60 2 + filter out horizontal drift noise + ++ sensor60 3 + turn off noise filter + ++ sensor60 4 + start recording, new data will be appended + ++ sensor60 5 + start new recording, old data will lost + ++ sensor60 6 + stop recording, download link will be visible in Web-UI + ++ sensor60 7 + send mqtt on new postion + TELE -> consider to set TELE to a very high value + ++ sensor60 8 + only TELE message + ++ sensor60 9 + start NTP-server + ++ sensor60 10 + deactivate NTP-server + ++ sensor60 11 + force update of Tasmota-system-UTC with every new GPS-time-message + ++ sensor60 12 + do not update of Tasmota-system-UTC with every new GPS-time-message + ++ sensor60 13 + set latitude and longitude in settings + ++ sensor60 14 + open virtual serial port over TCP, usable for u-center + ++ sensor60 15 + pause virtual serial port over TCP + +## Rules examples for SSD1306 32x128 + + +rule1 on tele-GPS#lat do DisplayText [s1p21c1l01f1]LAT: %value% endon on tele-GPS#lon do DisplayText [s1p21c1l2]LON: %value% endon on switch1#state==3 do sensor60 4 endon on switch1#state==2 do sensor60 6 endon + +rule2 on tele-GPS#int>9 do DisplayText [f0c9l4]I%value% endon on tele-GPS#int<10 do DisplayText [f0c9l4]I0%value% endon on tele-GPS#fil==1 do DisplayText [f0c18l4]F endon on tele-GPS#fil==0 do DisplayText [f0c18l4]N endon + +rule3 on tele-FLOG#sec do DisplayText [f0c1l4]SAV:%value% endon on tele-FLOG#rec==1 do DisplayText [f0c1l4]REC: endon on tele-FLOG#mode do DisplayText [f0c14l4]M%value% endon + +\*********************************************************************************************/ + +#define XSNS_60 60 + +#include "NTPServer.h" +#include "NTPPacket.h" + +/*********************************************************************************************\ + * constants +\*********************************************************************************************/ + +#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 // filter out some noise of local drift + +#define UBX_SERIAL_BUFFER_SIZE 256 +#define UBX_TCP_PORT 1234 +#define NTP_MILLIS_OFFSET 50 // estimated latency in milliseconds + +/********************************************************************************************\ +| *globals +\*********************************************************************************************/ + +const char UBLOX_INIT[] PROGMEM = { + // Disable NMEA + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x24, // GxGGA off + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x2B, // GxGLL off + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x02,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x32, // GxGSA off + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x39, // GxGSV off + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x04,0x00,0x00,0x00,0x00,0x00,0x01,0x04,0x40, // GxRMC off + 0xB5,0x62,0x06,0x01,0x08,0x00,0xF0,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x05,0x47, // GxVTG off + + // Disable UBX + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0xDC, //NAV-PVT off + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0xB9, //NAV-POSLLH off + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0xC0, //NAV-STATUS off + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x92, //NAV-TIMEUTC off + + // Enable UBX + // 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x07,0x00,0x01,0x00,0x00,0x00,0x00,0x18,0xE1, //NAV-PVT on + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x02,0x00,0x01,0x00,0x00,0x00,0x00,0x13,0xBE, //NAV-POSLLH on + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x14,0xC5, //NAV-STATUS on + 0xB5,0x62,0x06,0x01,0x08,0x00,0x01,0x21,0x00,0x01,0x00,0x00,0x00,0x00,0x32,0x97, //NAV-TIMEUTC on + + // Rate - we will not reset it for the moment after restart + // 0xB5,0x62,0x06,0x08,0x06,0x00,0x64,0x00,0x01,0x00,0x01,0x00,0x7A,0x12, //(10Hz) + // 0xB5,0x62,0x06,0x08,0x06,0x00,0xC8,0x00,0x01,0x00,0x01,0x00,0xDE,0x6A, //(5Hz) + // 0xB5,0x62,0x06,0x08,0x06,0x00,0xE8,0x03,0x01,0x00,0x01,0x00,0x01,0x39 //(1Hz) + // 0xB5,0x62,0x06,0x08,0x06,0x00,0xD0,0x07,0x01,0x00,0x01,0x00,0xED,0xBD //(0.5Hz) +}; + +char UBX_name[4]; + +struct UBX_t { + const char UBX_HEADER[2] = { 0xB5, 0x62 }; // TODO: Check if we really save space here inside the struct + 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; //raw sensor value + int32_t lon; //raw sensor value + uint32_t time; //local time from system (maybe provided by the sensor) + }; + + 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; //bit 0 - gpsfix valid + 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; // Nanoseconds of second, range -1e9 .. 1e9 (UTC) + 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; // week number + uint8_t TOW:1; // time of week + uint8_t padding:5; + } valid; + }; + + struct CFG_RATE { + uint8_t cls; //0x06 + uint8_t id; //0x08 + uint16_t len; // 6 bytes + uint16_t measRate; // in every ms -> 1 Hz = 1000 ms; 10 Hz = 100 ms -> x = 1000 ms / Hz + uint16_t navRate; // x measurements for 1 navigation event + uint16_t timeRef; // align to time system: 0= UTC, 1 = GPS, 2 = GLONASS, ... + char CK[2]; // checksum + }; + + 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; // in case of an unintended reset of the GPS, the serial interface will get flooded with NMEA + uint16_t log_interval; // in tenth of seconds + int32_t timeOffset; // roughly computed offset millis() - iTOW + } state; + + struct { + uint32_t init:1; + uint32_t filter_noise:1; + uint32_t send_when_new:1; // no teleinterval + uint32_t send_UI_only:1; + uint32_t runningNTP:1; + // uint32_t blockedNTP:1; + uint32_t forceUTCupdate:1; + uint32_t runningVPort:1; + // TODO: more to come + } mode; + + union { + NAV_POSLLH navPosllh; + NAV_STATUS navStatus; + NAV_TIME_UTC navTime; + POLL_MSG pollMsg; + CFG_RATE cfgRate; + } Message; + + uint32_t utc_time; + + 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 //USE_FLOG +TasmotaSerial *UBXSerial; + +NtpServer timeServer(PortUdp); + +WiFiServer vPortServer(UBX_TCP_PORT); +WiFiClient vPortClient; + +/*********************************************************************************************\ + * helper function +\*********************************************************************************************/ + +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 UBXsendCFGLine(uint8_t _line) +{ + if (_line>sizeof(UBLOX_INIT)/16) return; + for (uint32_t i = 0; i < 16; i++) { + UBXSerial->write( pgm_read_byte(UBLOX_INIT+i+(_line*16)) ); + } + DEBUG_SENSOR_LOG(PSTR("UBX: send line %u of UBLOX_INIT"), _line); +} + +/********************************************************************************************/ + +void UBXDetect(void) +{ + UBX.mode.init = 0; + if (PinUsed(GPIO_GPS_RX) && PinUsed(GPIO_GPS_TX)) { + UBXSerial = new TasmotaSerial(Pin(GPIO_GPS_RX), Pin(GPIO_GPS_TX), 1, 0, UBX_SERIAL_BUFFER_SIZE); // 64 byte buffer is NOT enough + 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(); // turn off NMEA, only use "our" UBX-messages + UBX.mode.init = 1; + +#ifdef USE_FLOG + if (!Flog) { + Flog = new FLOG; // init Flash Log + Flog->init(); + } +#endif // USE_FLOG + + UBX.state.log_interval = 10; // 1 second + UBX.mode.send_UI_only = true; // send UI data ... +// MqttPublishTeleperiodSensor(); // ... once at after start (No MQTT ready yet so do NOT try to send) +} + +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); + + // DEBUG_SENSOR_LOG(PSTR("UBX: check for serial data")); + uint32_t data_bytes = 0; + while ( UBXSerial->available() ) { + data_bytes++; + byte c = UBXSerial->read(); + if (UBX.mode.runningVPort){ + UBX.TCPbuf[data_bytes-1] = c; // immediately copy byte to TCP-buf + UBX.TCPbufSize = data_bytes; + } + if ( fpos < 2 ) { + // For the first two bytes we are simply looking for a match with the UBX header bytes (0xB5,0x62) + if ( c == UBX.UBX_HEADER[fpos] ) { + fpos++; + } else { + fpos = 0; // Reset to beginning state. + } + } else { + // If we come here then fpos >= 2, which means we have found a match with the UBX_HEADER + // and we are now reading in the bytes that make up the payload. + + // Place the incoming byte into the ubxMessage struct. The position is fpos-2 because + // the struct does not include the initial two-byte header (UBX_HEADER). + if ( (fpos-2) < payloadSize ) { + ((char*)(&UBX.Message))[fpos-2] = c; + } + fpos++; + + if ( fpos == 4 ) { + // We have just received the second byte of the message type header, + // so now we can check to see what kind of message it is. + 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 { + // unknown message type, bail + fpos = 0; + continue; + } + } + + if ( fpos == (payloadSize+2) ) { + // All payload bytes have now been received, so we can calculate the + // expected checksum value to compare with the next two incoming bytes. + UBXcalcChecksum(checksum, payloadSize); + } + else if ( fpos == (payloadSize+3) ) { + // First byte after the payload, ie. first byte of the checksum. + // Does it match the first byte of the checksum we calculated? + if ( c != checksum[0] ) { + // Checksum doesn't match, reset to beginning state and try again. + fpos = 0; + } + } + else if ( fpos == (payloadSize+4) ) { + // Second byte after the payload, ie. second byte of the checksum. + // Does it match the second byte of the checksum we calculated? + fpos = 0; // We will reset the state regardless of whether the checksum matches. + if ( c == checksum[1] ) { + // Checksum matches, we have a valid message. + return currentMsgType; + } + } + else if ( fpos > (payloadSize+4) ) { + // We have now read more bytes than both the expected payload and checksum + // together, so something went wrong. Reset to beginning state and try again. + fpos = 0; + } + } + } + // DEBUG_SENSOR_LOG(PSTR("UBX: got none or unknown Message")); + 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; // now a hidden GPS-device reset is unlikely + } + return MT_NONE; +} + +/********************************************************************************************\ +| * callback functions for the download +\*********************************************************************************************/ + +#ifdef USE_FLOG +void UBXsendHeader(void) +{ + Webserver->setContentLength(CONTENT_LENGTH_UNKNOWN); + Webserver->sendHeader(F("Content-Disposition"), F("attachment; filename=TASMOTA.gpx")); + WSSend(200, CT_APP_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); + // DEBUG_SENSOR_LOG(PSTR("FLOG: DL %u %u"), Flog->sector.dword_buffer[k+j],Flog->sector.dword_buffer[k+j+1]); + Webserver->sendContent_P(record); +} + +void UBXsendFooter(void) +{ + Webserver->sendContent(F("\n\n")); + Webserver->sendContent(""); + Rtc.user_time_entry = false; // we have blocked the main loop and want a new valid time +} + +/********************************************************************************************/ + +void UBXsendFile(void) +{ + if (!HttpCheckPriviledgedAccess()) { return; } + Flog->startDownload(sizeof(UBX.rec_buffer),UBXsendHeader,UBXsendRecord,UBXsendFooter); +} +#endif //USE_FLOG + +/********************************************************************************************/ + +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); //seconds to milliseconds + if (measRate > 0xffff) { + measRate = 0xffff; // max. 65535 ms interval + } + 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; // write once to all available sectors, then stop + break; + case 1: + Flog->mode = 1; // write to all available sectors, then restart and overwrite the older ones + break; + case 2: + UBX.mode.filter_noise = true; // filter out horizontal drift noise, TODO: find useful values + break; + case 3: + UBX.mode.filter_noise = false; + break; + case 4: + Flog->startRecording(true); + AddLog(LOG_LEVEL_INFO, PSTR("UBX: start recording - appending")); + break; + case 5: + Flog->startRecording(false); + AddLog(LOG_LEVEL_INFO, PSTR("UBX: start recording - new log")); + break; + case 6: + if(Flog->recording == true){ + Flog->stopRecording(); + } + AddLog(LOG_LEVEL_INFO, PSTR("UBX: stop recording")); + break; +#endif //USE_FLOG + case 7: + UBX.mode.send_when_new = 1; // send mqtt on new postion + TELE -> consider to set TELE to a very high value + break; + case 8: + UBX.mode.send_when_new = 0; // only TELE + break; + case 9: + if (timeServer.beginListening()) { + UBX.mode.runningNTP = true; + } + break; + case 10: + UBX.mode.runningNTP = false; + UBXsendCFGLine(10); //NAV-POSLLH on + UBXsendCFGLine(11); //NAV-STATUS on + 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: + // vPortServer.stop(); // seems not to work reliably + UBX.mode.runningVPort = 0; + break; + default: + if (mode>1000 && mode <1066) { + UBXSetRate(mode-1000); // set interval between measurements in seconds from 1 to 65 + } + break; + } + UBX.mode.send_UI_only = true; + MqttPublishTeleperiodSensor(); +} + +/********************************************************************************************/ + +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.lat go to pure NTP-mode + UBXsendCFGLine(7); //NAV-POSLLH off + UBXsendCFGLine(8); //NAV-STATUS off + } + return true; // new position + } else { + DEBUG_SENSOR_LOG(PSTR("UBX: no valid position data")); + } + return false; // no GPS-fix +} + +void UBXHandleSTATUS() +{ + DEBUG_SENSOR_LOG(PSTR("UBX: gpsFix: %u, valid: %u"), UBX.Message.navStatus.gpsFix, (UBX.Message.navStatus.flags)&1); + if ((UBX.Message.navStatus.flags)&1) { + UBX.state.gpsFix = UBX.Message.navStatus.gpsFix; //only store fixed status if flag is valid + } else { + UBX.state.gpsFix = 0; // without valid flag, everything is "no fix" + } +} + +void UBXHandleTIME() +{ + DEBUG_SENSOR_LOG(PSTR("UBX: UTC-Time: %u-%u-%u %u:%u:%u"), UBX.Message.navTime.year, UBX.Message.navTime.month ,UBX.Message.navTime.day,UBX.Message.navTime.hour,UBX.Message.navTime.min,UBX.Message.navTime.sec); + if (UBX.Message.navTime.valid.UTC == 1) { + UBX.state.timeOffset = millis(); // iTOW%1000 should be 0 here, when NTP-server is enabled and in "pure mode" + DEBUG_SENSOR_LOG(PSTR("UBX: UTC-Time is valid")); + bool resync = (Rtc.utc_time > UBX.utc_time); // Sync local time every hour + if (Rtc.user_time_entry == false || UBX.mode.forceUTCupdate || UBX.mode.runningNTP || resync) { + TIME_T gpsTime; + gpsTime.year = UBX.Message.navTime.year - 1970; + gpsTime.month = UBX.Message.navTime.month; + gpsTime.day_of_month = UBX.Message.navTime.day; + gpsTime.hour = UBX.Message.navTime.hour; + gpsTime.minute = UBX.Message.navTime.min; + gpsTime.second = UBX.Message.navTime.sec; + UBX.rec_buffer.values.time = MakeTime(gpsTime); + if (UBX.mode.forceUTCupdate || (Rtc.user_time_entry == false) || resync) { +// AddLog(LOG_LEVEL_INFO, PSTR("UBX: UTC-Time is valid, set system time")); + UBX.utc_time = UBX.rec_buffer.values.time + 3600; + Rtc.utc_time = UBX.rec_buffer.values.time; + RtcSync("UBX"); + } + Rtc.user_time_entry = true; + } + } +} + +void UBXHandleOther(void) +{ + if (UBX.state.non_empty_loops>6) { // we expect only 4-5 non-empty loops in a row, could change with other sensor speed (Hz) + if(UBX.mode.runningVPort) return; + UBXinitCFG(); // this should only happen with lots of NMEA-messages, but it is only a guess!! + AddLog(LOG_LEVEL_ERROR, PSTR("UBX: possible device-reset, will re-init")); + UBXSerial->flush(); + UBX.state.non_empty_loops = 0; + } +} + +/********************************************************************************************/ + +void UBXLoop50msec(void) +{ + // handle virtual serial port + 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; + } + } + // handle NTP-server + if(UBX.mode.runningNTP){ + timeServer.processOneRequest(UBX.rec_buffer.values.time, UBX.state.timeOffset - NTP_MILLIS_OFFSET); + } +} + +void UBXLoop(void) +{ + static uint16_t counter; //count up every 100 msec + 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 // USE_FLOG + + counter++; +} + +/********************************************************************************************/ +// normaly in i18n.h + +#ifdef USE_WEBSERVER + // {s} = , {m} = , {e} = + +#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 // DEBUG_TASMOTA_SENSOR + + 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 //USE_FLOG + 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}; + +// const char UBX_GOOGLE_MAPS[] =""; + + +#endif // USE_WEBSERVER + +/********************************************************************************************/ + +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 //USE_FLOG + UBX.mode.send_UI_only = false; +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_GPS, lat, lon, alt, hAcc, vAcc, kGPSFix[UBX.state.gpsFix]); + //WSContentSend_P(UBX_GOOGLE_MAPS, lat, lon); +#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 //USE_FLOG +#endif // DEBUG_TASMOTA_SENSOR +#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 //USE_FLOG + if (UBX.mode.runningNTP) { + WSContentSend_P(HTTP_SNS_NTPSERVER); + } +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * check the UBX commands +\*********************************************************************************************/ + +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; +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +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(); // handles virtual serial port and NTP server + break; + case FUNC_EVERY_100_MSECOND: +#ifdef USE_FLOG + if (!Flog->running_download) +#endif //USE_FLOG + { + UBXLoop(); + } + break; +#ifdef USE_FLOG + case FUNC_WEB_ADD_HANDLER: + WebServer_on(PSTR("/UBX"), UBXsendFile); + break; +#endif //USE_FLOG + case FUNC_JSON_APPEND: + UBXShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: +#ifdef USE_FLOG + if (!Flog->running_download) +#endif //USE_FLOG + { + UBXShow(0); + } + break; +#endif // USE_WEBSERVER + } + } + return result; +} + #endif // USE_GPS \ No newline at end of file diff --git a/tasmota/xsns_61_MI_NRF24.ino b/tasmota/tasmota_xsns_sensor/xsns_61_MI_NRF24.ino similarity index 100% rename from tasmota/xsns_61_MI_NRF24.ino rename to tasmota/tasmota_xsns_sensor/xsns_61_MI_NRF24.ino diff --git a/tasmota/xsns_62_MI_HM10.ino b/tasmota/tasmota_xsns_sensor/xsns_62_MI_HM10.ino similarity index 100% rename from tasmota/xsns_62_MI_HM10.ino rename to tasmota/tasmota_xsns_sensor/xsns_62_MI_HM10.ino diff --git a/tasmota/xsns_62_esp32_mi.ino b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino similarity index 99% rename from tasmota/xsns_62_esp32_mi.ino rename to tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino index 1adf5ab08..ef705efa9 100644 --- a/tasmota/xsns_62_esp32_mi.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi.ino @@ -24,15 +24,15 @@ -------------------------------------------------------------------------------------------- 0.9.5.5 20220326 changed - refactored connection task for asynchronous op, add response option, fixed MI32Key command - ------- + ------- 0.9.5.4 20220325 changed - add Berry adv_watch and adv_block to BLE class - ------- + ------- 0.9.5.3 20220315 changed - reworked Berry part, active scanning and holding active connections possible, new format of advertisement buffer - ------- + ------- 0.9.5.1 20220209 changed - rename YEERC to YLYK01, add dimmer YLKG08 (incl. YLKG07), change button report scheme ------- 0.9.5.0 20211016 changed - major rewrite, added mi32cfg (file and command), Homekit-Bridge, - extended GUI, + extended GUI, removed BLOCK, PERIOD, TIME, UNIT, BATTERY and PAGE -> replaced via Berry-Support ------- 0.9.1.7 20201116 changed - small bugfixes, add BLOCK and OPTION command, send BLE scan via MQTT @@ -63,7 +63,7 @@ #include -#include "xsns_62_esp32_mi.h" +#include "include/xsns_62_esp32_mi.h" #ifdef USE_MI_HOMEKIT extern "C" void mi_homekit_main(void); @@ -140,7 +140,7 @@ class MI32AdvCallbacks: public NimBLEAdvertisedDeviceCallbacks { _mutex = false; return; } - + uint16_t UUID = advertisedDevice->getServiceDataUUID(0).getNative()->u16.value; ServiceDataLength = advertisedDevice->getServiceData(0).length(); @@ -517,7 +517,7 @@ void MI32StatusInfo() { #ifdef USE_MI_EXT_GUI /** * @brief Saves a sensor value mapped to the graph range of 0-20 pixel, this function automatically reads the actual hour from system time - * + * * @param history - pointer to uint8_t[23] * @param value - value as float, this * @param type - internal type. for BLE: 0 - temperature, 1 - humidity, 2 - illuminance, for internal sensors: 100 - wattage @@ -550,7 +550,7 @@ void MI32addHistory(uint8_t *history, float value, uint32_t type){ /** * @brief Returns a value betwenn 0-21 for use as a data point in the history graph of the extended web UI - * + * * @param history - pointer to uint8_t[23] * @param hour - hour of datapoint * @return uint8_t - value for the y-axis, should be between 0-21 @@ -566,7 +566,7 @@ uint8_t MI32fetchHistory(uint8_t *history, uint32_t hour){ /** * @brief Invalidates the history data of the following hour by setting MSB to 0, should be called at FUNC_JSON_APPEND - * + * */ void Mi32invalidateOldHistory(){ uint32_t _hour = (LocalTime()%SECS_PER_DAY)/SECS_PER_HOUR; @@ -634,11 +634,11 @@ void MI32Init(void) { MI32getSetupCodeFromMAC(MI32.hk_setup_code); AddLog(LOG_LEVEL_INFO,PSTR("M32: Init HAP core")); mi_homekit_main(); - #else + #else MI32.mode.didStartHAP = 1; #endif //USE_MI_HOMEKIT } - + if (!MI32.mode.init) { NimBLEDevice::setScanFilterMode(CONFIG_BTDM_SCAN_DUPL_TYPE_DATA_DEVICE); NimBLEDevice::setScanDuplicateCacheSize(40); // will not be perfect for every situation (few vs many BLE devices nearby) @@ -820,8 +820,8 @@ extern "C" { /** * @brief Get at least a bit of the status of the HAP core, i.e. to reduce the activy of the driver while doing the pairing - * - * @param event + * + * @param event */ void MI32passHapEvent(uint32_t event){ switch(event){ @@ -852,7 +852,7 @@ extern "C" { /** * @brief Simply store the writeable HAP characteristics as void pointers in the "main" driver for updates of the values - * + * * @param slot - sensor slot in MIBLEsensors * @param type - sensors type, except for the buttons this is equal to the mibeacon types * @param handle - a void ponter to a characteristic @@ -897,7 +897,7 @@ extern "C" { * Example: AABBCC1234f2 * -> 111-11-234 * This is no security feature, only for convenience - * * @param setupcode + * * @param setupcode */ void MI32getSetupCodeFromMAC(char *setupcode){ uint8_t _mac[6]; @@ -1064,10 +1064,10 @@ void MI32ScanTask(void *pvParameters){ } MI32Scan = NimBLEDevice::getScan(); - + MI32Scan->setAdvertisedDeviceCallbacks(&MI32ScanCallbacks,false); if(NimBLEDevice::getWhiteListCount()>0){ - MI32Scan->setFilterPolicy(BLE_HCI_SCAN_FILT_USE_WL); + MI32Scan->setFilterPolicy(BLE_HCI_SCAN_FILT_USE_WL); } else { MI32Scan->setFilterPolicy(BLE_HCI_SCAN_FILT_NO_WL); @@ -1077,7 +1077,7 @@ void MI32ScanTask(void *pvParameters){ MI32Scan->setMaxResults(0); MI32Scan->start(0, MI32scanEndedCB, false); // never stop scanning, will pause automatically while connecting MI32.infoMsg = MI32.option.activeScan?MI32_START_SCANNING_ACTIVE:MI32_START_SCANNING_PASSIVE; - + uint32_t timer = 0; for(;;){ vTaskDelay(1000/ portTICK_PERIOD_MS); @@ -1224,7 +1224,7 @@ void MI32ConnectionTask(void *pvParameters){ // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: write op done")); } else{ - MI32.conCtx->error = MI32_CONN_DID_NOT_WRITE; + MI32.conCtx->error = MI32_CONN_DID_NOT_WRITE; } } else{ @@ -1352,7 +1352,7 @@ if(decryptRet!=0){ if((void**)MIBLEsensors[_slot].button_hap_service[0] != nullptr){ mi_homekit_update_value(MIBLEsensors[_slot].button_hap_service[0], (float)2.0f, 0x01); // only one button, long press = 2 } -#endif //USE_MI_HOMEKIT +#endif //USE_MI_HOMEKIT break; } // single, double, long @@ -1661,10 +1661,10 @@ void MI32Every50mSecond(){ else if(MI32.beAdvBuf[_index+1] == 0xff){ _manu = _index + 2; } - _index += MI32.beAdvBuf[_index] + 1; + _index += MI32.beAdvBuf[_index] + 1; } // AddLog(LOG_LEVEL_DEBUG,PSTR("M32: svc:%u , manu:%u"),_svc,_manu); - void (*func_ptr)(int,int) = (void (*)(int,int))MI32.beAdvCB; + void (*func_ptr)(int,int) = (void (*)(int,int))MI32.beAdvCB; func_ptr(_svc,_manu); } MI32.mode.triggerBerryAdvCB = 0; @@ -2344,7 +2344,7 @@ int ExtStopBLE(){ MI32Scan->stop(); MI32.mode.deleteScanTask = 1; AddLog(LOG_LEVEL_INFO,PSTR("M32: stop BLE")); - } + } #ifdef USE_MI_HOMEKIT if(MI32.mode.didStartHAP) { AddLog(LOG_LEVEL_INFO,PSTR("M32: stop Homebridge")); diff --git a/tasmota/xsns_62_esp32_mi_ble.ino b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi_ble.ino similarity index 100% rename from tasmota/xsns_62_esp32_mi_ble.ino rename to tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi_ble.ino diff --git a/tasmota/xsns_62_esp32_mi_homekit.c b/tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi_homekit.c similarity index 100% rename from tasmota/xsns_62_esp32_mi_homekit.c rename to tasmota/tasmota_xsns_sensor/xsns_62_esp32_mi_homekit.c diff --git a/tasmota/xsns_63_aht1x.ino b/tasmota/tasmota_xsns_sensor/xsns_63_aht1x.ino similarity index 100% rename from tasmota/xsns_63_aht1x.ino rename to tasmota/tasmota_xsns_sensor/xsns_63_aht1x.ino diff --git a/tasmota/xsns_64_hrxl.ino b/tasmota/tasmota_xsns_sensor/xsns_64_hrxl.ino similarity index 100% rename from tasmota/xsns_64_hrxl.ino rename to tasmota/tasmota_xsns_sensor/xsns_64_hrxl.ino diff --git a/tasmota/xsns_65_hdc1080.ino b/tasmota/tasmota_xsns_sensor/xsns_65_hdc1080.ino similarity index 100% rename from tasmota/xsns_65_hdc1080.ino rename to tasmota/tasmota_xsns_sensor/xsns_65_hdc1080.ino diff --git a/tasmota/xsns_66_iAQ.ino b/tasmota/tasmota_xsns_sensor/xsns_66_iAQ.ino similarity index 100% rename from tasmota/xsns_66_iAQ.ino rename to tasmota/tasmota_xsns_sensor/xsns_66_iAQ.ino diff --git a/tasmota/xsns_67_as3935.ino b/tasmota/tasmota_xsns_sensor/xsns_67_as3935.ino similarity index 100% rename from tasmota/xsns_67_as3935.ino rename to tasmota/tasmota_xsns_sensor/xsns_67_as3935.ino diff --git a/tasmota/xsns_68_windmeter.ino b/tasmota/tasmota_xsns_sensor/xsns_68_windmeter.ino similarity index 100% rename from tasmota/xsns_68_windmeter.ino rename to tasmota/tasmota_xsns_sensor/xsns_68_windmeter.ino diff --git a/tasmota/xsns_69_opentherm.ino b/tasmota/tasmota_xsns_sensor/xsns_69_opentherm.ino similarity index 100% rename from tasmota/xsns_69_opentherm.ino rename to tasmota/tasmota_xsns_sensor/xsns_69_opentherm.ino diff --git a/tasmota/xsns_69_opentherm_protocol.ino b/tasmota/tasmota_xsns_sensor/xsns_69_opentherm_protocol.ino similarity index 100% rename from tasmota/xsns_69_opentherm_protocol.ino rename to tasmota/tasmota_xsns_sensor/xsns_69_opentherm_protocol.ino diff --git a/tasmota/xsns_70_veml6075.ino b/tasmota/tasmota_xsns_sensor/xsns_70_veml6075.ino similarity index 100% rename from tasmota/xsns_70_veml6075.ino rename to tasmota/tasmota_xsns_sensor/xsns_70_veml6075.ino diff --git a/tasmota/xsns_71_veml7700.ino b/tasmota/tasmota_xsns_sensor/xsns_71_veml7700.ino similarity index 100% rename from tasmota/xsns_71_veml7700.ino rename to tasmota/tasmota_xsns_sensor/xsns_71_veml7700.ino diff --git a/tasmota/xsns_72_mcp9808.ino b/tasmota/tasmota_xsns_sensor/xsns_72_mcp9808.ino similarity index 100% rename from tasmota/xsns_72_mcp9808.ino rename to tasmota/tasmota_xsns_sensor/xsns_72_mcp9808.ino diff --git a/tasmota/xsns_73_hp303b.ino b/tasmota/tasmota_xsns_sensor/xsns_73_hp303b.ino similarity index 100% rename from tasmota/xsns_73_hp303b.ino rename to tasmota/tasmota_xsns_sensor/xsns_73_hp303b.ino diff --git a/tasmota/xsns_74_lmt01.ino b/tasmota/tasmota_xsns_sensor/xsns_74_lmt01.ino similarity index 100% rename from tasmota/xsns_74_lmt01.ino rename to tasmota/tasmota_xsns_sensor/xsns_74_lmt01.ino diff --git a/tasmota/xsns_75_prometheus.ino b/tasmota/tasmota_xsns_sensor/xsns_75_prometheus.ino similarity index 100% rename from tasmota/xsns_75_prometheus.ino rename to tasmota/tasmota_xsns_sensor/xsns_75_prometheus.ino diff --git a/tasmota/xsns_76_dyp.ino b/tasmota/tasmota_xsns_sensor/xsns_76_dyp.ino similarity index 100% rename from tasmota/xsns_76_dyp.ino rename to tasmota/tasmota_xsns_sensor/xsns_76_dyp.ino diff --git a/tasmota/xsns_77_vl53l1x.ino b/tasmota/tasmota_xsns_sensor/xsns_77_vl53l1x.ino similarity index 100% rename from tasmota/xsns_77_vl53l1x.ino rename to tasmota/tasmota_xsns_sensor/xsns_77_vl53l1x.ino diff --git a/tasmota/xsns_78_ezo.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezo.ino similarity index 100% rename from tasmota/xsns_78_ezo.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezo.ino diff --git a/tasmota/xsns_78_ezoco2.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezoco2.ino similarity index 100% rename from tasmota/xsns_78_ezoco2.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezoco2.ino diff --git a/tasmota/xsns_78_ezodo.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezodo.ino similarity index 100% rename from tasmota/xsns_78_ezodo.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezodo.ino diff --git a/tasmota/xsns_78_ezoec.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezoec.ino similarity index 100% rename from tasmota/xsns_78_ezoec.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezoec.ino diff --git a/tasmota/xsns_78_ezoflo.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezoflo.ino similarity index 100% rename from tasmota/xsns_78_ezoflo.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezoflo.ino diff --git a/tasmota/xsns_78_ezohum.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezohum.ino similarity index 100% rename from tasmota/xsns_78_ezohum.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezohum.ino diff --git a/tasmota/xsns_78_ezoo2.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezoo2.ino similarity index 100% rename from tasmota/xsns_78_ezoo2.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezoo2.ino diff --git a/tasmota/xsns_78_ezoorp.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezoorp.ino similarity index 100% rename from tasmota/xsns_78_ezoorp.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezoorp.ino diff --git a/tasmota/xsns_78_ezoph.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezoph.ino similarity index 100% rename from tasmota/xsns_78_ezoph.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezoph.ino diff --git a/tasmota/xsns_78_ezopmp.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezopmp.ino similarity index 100% rename from tasmota/xsns_78_ezopmp.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezopmp.ino diff --git a/tasmota/xsns_78_ezoprs.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezoprs.ino similarity index 100% rename from tasmota/xsns_78_ezoprs.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezoprs.ino diff --git a/tasmota/xsns_78_ezorgb.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezorgb.ino similarity index 100% rename from tasmota/xsns_78_ezorgb.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezorgb.ino diff --git a/tasmota/xsns_78_ezortd.ino b/tasmota/tasmota_xsns_sensor/xsns_78_ezortd.ino similarity index 100% rename from tasmota/xsns_78_ezortd.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_ezortd.ino diff --git a/tasmota/xsns_78_xezo.ino b/tasmota/tasmota_xsns_sensor/xsns_78_xezo.ino similarity index 100% rename from tasmota/xsns_78_xezo.ino rename to tasmota/tasmota_xsns_sensor/xsns_78_xezo.ino diff --git a/tasmota/xsns_79_as608.ino b/tasmota/tasmota_xsns_sensor/xsns_79_as608.ino similarity index 100% rename from tasmota/xsns_79_as608.ino rename to tasmota/tasmota_xsns_sensor/xsns_79_as608.ino diff --git a/tasmota/xsns_80_mfrc522.ino b/tasmota/tasmota_xsns_sensor/xsns_80_mfrc522.ino similarity index 100% rename from tasmota/xsns_80_mfrc522.ino rename to tasmota/tasmota_xsns_sensor/xsns_80_mfrc522.ino diff --git a/tasmota/xsns_81_seesaw_soil.ino b/tasmota/tasmota_xsns_sensor/xsns_81_seesaw_soil.ino similarity index 100% rename from tasmota/xsns_81_seesaw_soil.ino rename to tasmota/tasmota_xsns_sensor/xsns_81_seesaw_soil.ino diff --git a/tasmota/xsns_82_wiegand.ino b/tasmota/tasmota_xsns_sensor/xsns_82_wiegand.ino similarity index 100% rename from tasmota/xsns_82_wiegand.ino rename to tasmota/tasmota_xsns_sensor/xsns_82_wiegand.ino diff --git a/tasmota/xsns_83_neopool.ino b/tasmota/tasmota_xsns_sensor/xsns_83_neopool.ino similarity index 100% rename from tasmota/xsns_83_neopool.ino rename to tasmota/tasmota_xsns_sensor/xsns_83_neopool.ino diff --git a/tasmota/xsns_84_tof10120.ino b/tasmota/tasmota_xsns_sensor/xsns_84_tof10120.ino similarity index 100% rename from tasmota/xsns_84_tof10120.ino rename to tasmota/tasmota_xsns_sensor/xsns_84_tof10120.ino diff --git a/tasmota/xsns_85_mpu6886.ino b/tasmota/tasmota_xsns_sensor/xsns_85_mpu6886.ino similarity index 100% rename from tasmota/xsns_85_mpu6886.ino rename to tasmota/tasmota_xsns_sensor/xsns_85_mpu6886.ino diff --git a/tasmota/xsns_86_tfminiplus.ino b/tasmota/tasmota_xsns_sensor/xsns_86_tfminiplus.ino similarity index 100% rename from tasmota/xsns_86_tfminiplus.ino rename to tasmota/tasmota_xsns_sensor/xsns_86_tfminiplus.ino diff --git a/tasmota/xsns_87_mcp2515.ino b/tasmota/tasmota_xsns_sensor/xsns_87_mcp2515.ino similarity index 100% rename from tasmota/xsns_87_mcp2515.ino rename to tasmota/tasmota_xsns_sensor/xsns_87_mcp2515.ino diff --git a/tasmota/xsns_88_am2320.ino b/tasmota/tasmota_xsns_sensor/xsns_88_am2320.ino similarity index 100% rename from tasmota/xsns_88_am2320.ino rename to tasmota/tasmota_xsns_sensor/xsns_88_am2320.ino diff --git a/tasmota/xsns_89_t67xx.ino b/tasmota/tasmota_xsns_sensor/xsns_89_t67xx.ino similarity index 100% rename from tasmota/xsns_89_t67xx.ino rename to tasmota/tasmota_xsns_sensor/xsns_89_t67xx.ino diff --git a/tasmota/xsns_90_hrg15.ino b/tasmota/tasmota_xsns_sensor/xsns_90_hrg15.ino similarity index 100% rename from tasmota/xsns_90_hrg15.ino rename to tasmota/tasmota_xsns_sensor/xsns_90_hrg15.ino diff --git a/tasmota/xsns_91_vindriktning.ino b/tasmota/tasmota_xsns_sensor/xsns_91_vindriktning.ino similarity index 100% rename from tasmota/xsns_91_vindriktning.ino rename to tasmota/tasmota_xsns_sensor/xsns_91_vindriktning.ino diff --git a/tasmota/xsns_92_scd40.ino b/tasmota/tasmota_xsns_sensor/xsns_92_scd40.ino similarity index 100% rename from tasmota/xsns_92_scd40.ino rename to tasmota/tasmota_xsns_sensor/xsns_92_scd40.ino diff --git a/tasmota/xsns_93_hm330x.ino b/tasmota/tasmota_xsns_sensor/xsns_93_hm330x.ino similarity index 100% rename from tasmota/xsns_93_hm330x.ino rename to tasmota/tasmota_xsns_sensor/xsns_93_hm330x.ino diff --git a/tasmota/xsns_94_hdc2010.ino b/tasmota/tasmota_xsns_sensor/xsns_94_hdc2010.ino old mode 100755 new mode 100644 similarity index 100% rename from tasmota/xsns_94_hdc2010.ino rename to tasmota/tasmota_xsns_sensor/xsns_94_hdc2010.ino diff --git a/tasmota/xsns_95_cm110x.ino b/tasmota/tasmota_xsns_sensor/xsns_95_cm110x.ino similarity index 100% rename from tasmota/xsns_95_cm110x.ino rename to tasmota/tasmota_xsns_sensor/xsns_95_cm110x.ino diff --git a/tasmota/xsns_96_flowratemeter.ino b/tasmota/tasmota_xsns_sensor/xsns_96_flowratemeter.ino similarity index 100% rename from tasmota/xsns_96_flowratemeter.ino rename to tasmota/tasmota_xsns_sensor/xsns_96_flowratemeter.ino diff --git a/tasmota/xsns_interface.ino b/tasmota/tasmota_xsns_sensor/xsns_interface.ino similarity index 100% rename from tasmota/xsns_interface.ino rename to tasmota/tasmota_xsns_sensor/xsns_interface.ino diff --git a/tasmota/xx2c_interface.ino b/tasmota/tasmota_xx2c_global/xx2c_interface.ino similarity index 100% rename from tasmota/xx2c_interface.ino rename to tasmota/tasmota_xx2c_global/xx2c_interface.ino