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