From 942e419b0c87f7466221330a99110b3072793a14 Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Sat, 28 Jun 2025 21:59:39 +0200 Subject: [PATCH] Summary of the docs repository to easily feed to Code AI (#23614) --- .doc_for_ai/FOR_DEVELOPERS.md | 288 +-- .doc_for_ai/TASMOTA_SUPPORT_DEEP_ANALYSIS.md | 2124 ++++++++++++++++++ 2 files changed, 2273 insertions(+), 139 deletions(-) create mode 100644 .doc_for_ai/TASMOTA_SUPPORT_DEEP_ANALYSIS.md diff --git a/.doc_for_ai/FOR_DEVELOPERS.md b/.doc_for_ai/FOR_DEVELOPERS.md index 0aed720df..8f19ff286 100644 --- a/.doc_for_ai/FOR_DEVELOPERS.md +++ b/.doc_for_ai/FOR_DEVELOPERS.md @@ -30,14 +30,28 @@ Tasmota is a modular firmware that transforms ESP8266/ESP8285 and ESP32 microcon ``` tasmota/ -├── tasmota.ino # Main firmware file -├── xdrv_*.ino # Driver files (displays, interfaces, etc.) -├── xsns_*.ino # Sensor files -├── xlgt_*.ino # Light driver files -├── xnrg_*.ino # Energy monitoring files -├── support_*.ino # Support functions -├── settings.h # Configuration structure -└── my_user_config.h # User configuration overrides +├── tasmota.ino # Main firmware file +├── tasmota_xdrv_driver/ # Driver files directory (187 files) +│ ├── xdrv_01_9_webserver.ino # Web server driver +│ ├── xdrv_02_9_mqtt.ino # MQTT driver +│ ├── xdrv_04_light.ino # Light driver +│ └── xdrv_##_name.ino # Other drivers +├── tasmota_xsns_sensor/ # Sensor files directory (143 files) +│ ├── xsns_01_counter.ino # Counter sensor +│ ├── xsns_02_analog.ino # Analog sensor +│ └── xsns_##_name.ino # Other sensors +├── tasmota_xlgt_light/ # Light driver files directory +├── tasmota_xnrg_energy/ # Energy monitoring files directory +├── tasmota_support/ # Support functions directory (29 files) +│ ├── support.ino # Core support functions +│ ├── settings.ino # Settings management +│ └── support_*.ino # Other support modules +├── include/ # Header files directory (18 files) +│ ├── tasmota.h # Main header +│ ├── tasmota_types.h # Type definitions +│ ├── tasmota_globals.h # Global variables +│ └── *.h # Other headers +└── my_user_config.h # User configuration overrides ``` ### Command System @@ -159,151 +173,122 @@ bool Xsns_XX(byte function) { ### Complete Driver Callback Functions Reference -#### Core System Callbacks +**VERIFIED FROM SOURCE CODE**: `tasmota/include/tasmota.h` lines 433-454 -| Function | ID | Purpose | Frequency | Parameters | -|----------|----|---------|-----------|-----------| -| `FUNC_INIT` | 0 | Initialize driver/sensor | Once at startup | None | -| `FUNC_LOOP` | 1 | Main loop processing | Every loop cycle (~1ms) | None | -| `FUNC_EVERY_50_MSECOND` | 2 | Fast polling operations | Every 50ms | None | -| `FUNC_EVERY_100_MSECOND` | 3 | Medium polling | Every 100ms | None | -| `FUNC_EVERY_200_MSECOND` | 4 | Slower polling | Every 200ms | None | -| `FUNC_EVERY_250_MSECOND` | 5 | Quarter second tasks | Every 250ms | None | -| `FUNC_EVERY_SECOND` | 6 | Regular updates | Every second | None | -| `FUNC_PREP_BEFORE_TELEPERIOD` | 7 | Prepare telemetry data | Before TelePeriod | None | -| `FUNC_JSON_APPEND` | 8 | Add JSON telemetry | Every TelePeriod | None | -| `FUNC_BUTTON_PRESSED` | 9 | Handle button press | On button event | button_index | -| `FUNC_SAVE_BEFORE_RESTART` | 10 | Save critical data | Before restart | None | -| `FUNC_AFTER_TELEPERIOD` | 11 | Post-telemetry cleanup | After TelePeriod | None | +#### Core System Callbacks (Functions WITHOUT return results) -#### Communication Callbacks +| Function | Purpose | When Called | Parameters | +|----------|---------|-------------|-----------| +| `FUNC_SETTINGS_OVERRIDE` | Override default settings | Before settings load | None | +| `FUNC_SETUP_RING1` | Early setup phase 1 | System initialization | None | +| `FUNC_SETUP_RING2` | Early setup phase 2 | System initialization | None | +| `FUNC_PRE_INIT` | Pre-initialization | Before main init | None | +| `FUNC_INIT` | Initialize driver/sensor | Once at startup | None | +| `FUNC_ACTIVE` | Check if driver is active | Status queries | None | +| `FUNC_ABOUT_TO_RESTART` | Prepare for restart | Before system restart | None | -| Function | ID | Purpose | When Called | Parameters | -|----------|----|---------|-----------|-----------| -| `FUNC_COMMAND` | 12 | Process commands | Command received | XdrvMailbox | -| `FUNC_MQTT_SUBSCRIBE` | 13 | Subscribe to MQTT topics | MQTT connect | None | -| `FUNC_MQTT_INIT` | 14 | Initialize MQTT | MQTT setup | None | -| `FUNC_MQTT_DATA` | 15 | Process MQTT data | MQTT message | XdrvMailbox | -| `FUNC_SET_POWER` | 16 | Handle power changes | Power state change | None | -| `FUNC_SHOW_SENSOR` | 17 | Display sensor data | Status request | None | -| `FUNC_RULES_PROCESS` | 18 | Process rules | Rule evaluation | None | +#### Loop and Timing Callbacks + +| Function | Purpose | Frequency | Parameters | +|----------|---------|-----------|-----------| +| `FUNC_LOOP` | Main loop processing | Every loop cycle (~1ms) | None | +| `FUNC_SLEEP_LOOP` | Sleep mode processing | During sleep cycles | None | +| `FUNC_EVERY_50_MSECOND` | Fast polling operations | Every 50ms | None | +| `FUNC_EVERY_100_MSECOND` | Medium polling | Every 100ms | None | +| `FUNC_EVERY_200_MSECOND` | Slower polling | Every 200ms | None | +| `FUNC_EVERY_250_MSECOND` | Quarter second tasks | Every 250ms | None | +| `FUNC_EVERY_SECOND` | Regular updates | Every second | None | + +#### Settings and Configuration Callbacks + +| Function | Purpose | When Called | Parameters | +|----------|---------|-------------|-----------| +| `FUNC_RESET_SETTINGS` | Reset to defaults | Settings reset | None | +| `FUNC_RESTORE_SETTINGS` | Restore from backup | Settings restore | None | +| `FUNC_SAVE_SETTINGS` | Save current settings | Settings save | None | +| `FUNC_SAVE_AT_MIDNIGHT` | Midnight save operations | Daily at 00:00 | None | +| `FUNC_SAVE_BEFORE_RESTART` | Save critical data | Before restart | None | + +#### Interrupt and System Control + +| Function | Purpose | When Called | Parameters | +|----------|---------|-------------|-----------| +| `FUNC_INTERRUPT_STOP` | Stop interrupts | Before critical section | None | +| `FUNC_INTERRUPT_START` | Resume interrupts | After critical section | None | +| `FUNC_FREE_MEM` | Memory cleanup | Low memory conditions | None | + +#### Telemetry and JSON Callbacks + +| Function | Purpose | When Called | Parameters | +|----------|---------|-------------|-----------| +| `FUNC_AFTER_TELEPERIOD` | Post-telemetry cleanup | After TelePeriod | None | +| `FUNC_JSON_APPEND` | Add JSON telemetry | Every TelePeriod | None | +| `FUNC_TELEPERIOD_RULES_PROCESS` | Rules after telemetry | Post-TelePeriod | None | #### Web Interface Callbacks -| Function | ID | Purpose | When Called | Parameters | -|----------|----|---------|-----------|-----------| -| `FUNC_WEB_ADD_BUTTON` | 19 | Add web buttons | Main page load | None | -| `FUNC_WEB_ADD_MAIN_BUTTON` | 20 | Add main menu button | Main page | None | -| `FUNC_WEB_ADD_HANDLER` | 21 | Add URL handlers | Web server init | None | -| `FUNC_WEB_SENSOR` | 22 | Show sensor on web | Sensor page load | None | -| `FUNC_WEB_ADD_CONSOLE_BUTTON` | 23 | Add console button | Console page | None | -| `FUNC_WEB_ADD_MANAGEMENT_BUTTON` | 24 | Add config button | Config page | None | +| Function | Purpose | When Called | Parameters | +|----------|---------|-------------|-----------| +| `FUNC_WEB_SENSOR` | Show sensor on web | Sensor page load | None | +| `FUNC_WEB_COL_SENSOR` | Column sensor display | Web page layout | None | +| `FUNC_WEB_ADD_BUTTON` | Add web buttons | Main page load | None | +| `FUNC_WEB_ADD_CONSOLE_BUTTON` | Add console button | Console page | None | +| `FUNC_WEB_ADD_MANAGEMENT_BUTTON` | Add config button | Config page | None | +| `FUNC_WEB_ADD_MAIN_BUTTON` | Add main menu button | Main page | None | +| `FUNC_WEB_GET_ARG` | Process web arguments | Form submission | None | +| `FUNC_WEB_ADD_HANDLER` | Add URL handlers | Web server init | None | +| `FUNC_WEB_STATUS_LEFT` | Left status column | Status page | None | +| `FUNC_WEB_STATUS_RIGHT` | Right status column | Status page | None | -#### Network and Protocol Callbacks +#### MQTT and Communication Callbacks -| Function | ID | Purpose | When Called | Parameters | -|----------|----|---------|-----------|-----------| -| `FUNC_NETWORK_UP` | 25 | Network connected | WiFi/Ethernet up | None | -| `FUNC_NETWORK_DOWN` | 26 | Network disconnected | WiFi/Ethernet down | None | -| `FUNC_MQTT_CONNECTED` | 27 | MQTT broker connected | MQTT connect | None | -| `FUNC_MQTT_DISCONNECTED` | 28 | MQTT broker disconnected | MQTT disconnect | None | -| `FUNC_SET_DEVICE_POWER` | 29 | Device power control | Power command | device, power | -| `FUNC_SHOW_SENSOR_JSON` | 30 | JSON sensor output | JSON request | None | +| Function | Purpose | When Called | Parameters | +|----------|---------|-------------|-----------| +| `FUNC_MQTT_SUBSCRIBE` | Subscribe to MQTT topics | MQTT connect | None | +| `FUNC_MQTT_INIT` | Initialize MQTT | MQTT setup | None | + +#### Power and Hardware Control + +| Function | Purpose | When Called | Parameters | +|----------|---------|-------------|-----------| +| `FUNC_SET_POWER` | Handle power changes | Power state change | None | +| `FUNC_SHOW_SENSOR` | Display sensor data | Status request | None | +| `FUNC_ANY_KEY` | Handle any key press | Key event | None | +| `FUNC_LED_LINK` | Control link LED | Network state change | None | +| `FUNC_ENERGY_EVERY_SECOND` | Energy monitoring | Every second | None | +| `FUNC_ENERGY_RESET` | Reset energy counters | Reset command | None | #### Advanced System Callbacks -| Function | ID | Purpose | When Called | Parameters | -|----------|----|---------|-----------|-----------| -| `FUNC_SERIAL` | 31 | Serial data processing | Serial input | None | -| `FUNC_FREE_MEM` | 32 | Memory cleanup | Low memory | None | -| `FUNC_BUTTON_MULTI_PRESSED` | 33 | Multi-button press | Button combo | button_code | -| `FUNC_ENERGY_EVERY_SECOND` | 34 | Energy monitoring | Every second | None | -| `FUNC_ACTIVE` | 35 | Driver active check | Status query | None | -| `FUNC_PIN_STATE` | 36 | GPIO state change | Pin change | gpio, state | -| `FUNC_TELEPERIOD_RULES_PROCESS` | 37 | Rules after telemetry | Post-TelePeriod | None | +| Function | Purpose | When Called | Parameters | +|----------|---------|-------------|-----------| +| `FUNC_SET_SCHEME` | Set color scheme | Theme change | None | +| `FUNC_HOTPLUG_SCAN` | Scan for hotplug devices | Device detection | None | +| `FUNC_TIME_SYNCED` | Time synchronization | NTP sync complete | None | +| `FUNC_DEVICE_GROUP_ITEM` | Device group processing | Group operations | None | +| `FUNC_NETWORK_UP` | Network connected | WiFi/Ethernet up | None | +| `FUNC_NETWORK_DOWN` | Network disconnected | WiFi/Ethernet down | None | -#### Display and UI Callbacks +#### Callback Functions WITH Return Results (ID >= 200) -| Function | ID | Purpose | When Called | Parameters | -|----------|----|---------|-----------|-----------| -| `FUNC_DISPLAY_INIT_DRIVER` | 38 | Initialize display | Display setup | None | -| `FUNC_DISPLAY_MODEL` | 39 | Set display model | Display config | None | -| `FUNC_DISPLAY_MODE` | 40 | Set display mode | Mode change | None | -| `FUNC_DISPLAY_POWER` | 41 | Display power control | Power change | None | -| `FUNC_DISPLAY_CLEAR` | 42 | Clear display | Clear command | None | -| `FUNC_DISPLAY_DRAW_HLINE` | 43 | Draw horizontal line | Draw command | x, y, len, color | -| `FUNC_DISPLAY_DRAW_VLINE` | 44 | Draw vertical line | Draw command | x, y, len, color | -| `FUNC_DISPLAY_DRAW_CIRCLE` | 45 | Draw circle | Draw command | x, y, rad, color | -| `FUNC_DISPLAY_FILL_CIRCLE` | 46 | Fill circle | Draw command | x, y, rad, color | -| `FUNC_DISPLAY_DRAW_RECTANGLE` | 47 | Draw rectangle | Draw command | x, y, w, h, color | -| `FUNC_DISPLAY_FILL_RECTANGLE` | 48 | Fill rectangle | Draw command | x, y, w, h, color | -| `FUNC_DISPLAY_TEXT_SIZE` | 49 | Set text size | Text command | size | -| `FUNC_DISPLAY_FONT_SIZE` | 50 | Set font size | Font command | size | -| `FUNC_DISPLAY_DRAW_STRING` | 51 | Draw text string | Text command | x, y, text, color | -| `FUNC_DISPLAY_DRAW_STRING_AT` | 52 | Draw text at position | Text command | x, y, text, color | -| `FUNC_DISPLAY_PRINTF` | 53 | Printf to display | Text command | format, args | -| `FUNC_DISPLAY_ROTATION` | 54 | Set rotation | Rotation command | angle | -| `FUNC_DISPLAY_DRAW_FRAME` | 55 | Draw frame | Draw command | None | +These functions are expected to return boolean results: -#### Berry Script Integration Callbacks - -| Function | ID | Purpose | When Called | Parameters | -|----------|----|---------|-----------|-----------| -| `FUNC_BERRY_INIT` | 56 | Initialize Berry | Berry startup | None | -| `FUNC_BERRY_LOOP` | 57 | Berry main loop | Every loop | None | -| `FUNC_BERRY_EVERY_50_MSECOND` | 58 | Berry fast timer | Every 50ms | None | -| `FUNC_BERRY_EVERY_100_MSECOND` | 59 | Berry medium timer | Every 100ms | None | -| `FUNC_BERRY_EVERY_SECOND` | 60 | Berry second timer | Every second | None | -| `FUNC_BERRY_RULES_PROCESS` | 61 | Berry rules processing | Rule trigger | None | - -#### Matter Protocol Callbacks (ESP32) - -| Function | ID | Purpose | When Called | Parameters | -|----------|----|---------|-----------|-----------| -| `FUNC_MATTER_INIT` | 62 | Initialize Matter | Matter startup | None | -| `FUNC_MATTER_LOOP` | 63 | Matter processing | Every loop | None | -| `FUNC_MATTER_EVERY_50_MSECOND` | 64 | Matter fast timer | Every 50ms | None | -| `FUNC_MATTER_EVERY_SECOND` | 65 | Matter second timer | Every second | None | -| `FUNC_MATTER_COMMAND` | 66 | Matter commands | Matter command | None | -| `FUNC_MATTER_JSON_APPEND` | 67 | Matter JSON data | Telemetry | None | - -#### Audio and Media Callbacks - -| Function | ID | Purpose | When Called | Parameters | -|----------|----|---------|-----------|-----------| -| `FUNC_AUDIO_INIT` | 68 | Initialize audio | Audio setup | None | -| `FUNC_AUDIO_LOOP` | 69 | Audio processing | Audio loop | None | -| `FUNC_AUDIO_COMMAND` | 70 | Audio commands | Audio command | None | -| `FUNC_AUDIO_SHOW_SENSOR` | 71 | Audio sensor data | Status request | None | - -#### Zigbee Protocol Callbacks - -| Function | ID | Purpose | When Called | Parameters | -|----------|----|---------|-----------|-----------| -| `FUNC_ZIGBEE_INIT` | 72 | Initialize Zigbee | Zigbee startup | None | -| `FUNC_ZIGBEE_LOOP` | 73 | Zigbee processing | Every loop | None | -| `FUNC_ZIGBEE_EVERY_50_MSECOND` | 74 | Zigbee fast timer | Every 50ms | None | -| `FUNC_ZIGBEE_COMMAND` | 75 | Zigbee commands | Zigbee command | None | -| `FUNC_ZIGBEE_JSON_APPEND` | 76 | Zigbee JSON data | Telemetry | None | - -#### Energy Management Callbacks - -| Function | ID | Purpose | When Called | Parameters | -|----------|----|---------|-----------|-----------| -| `FUNC_ENERGY_INIT` | 77 | Initialize energy monitor | Energy setup | None | -| `FUNC_ENERGY_LOOP` | 78 | Energy processing | Every loop | None | -| `FUNC_ENERGY_SHOW_SENSOR` | 79 | Energy sensor display | Status request | None | -| `FUNC_ENERGY_RESET` | 80 | Reset energy counters | Reset command | None | - -#### Light Control Callbacks - -| Function | ID | Purpose | When Called | Parameters | -|----------|----|---------|-----------|-----------| -| `FUNC_LIGHT_INIT` | 81 | Initialize lighting | Light setup | None | -| `FUNC_LIGHT_LOOP` | 82 | Light processing | Every loop | None | -| `FUNC_LIGHT_EVERY_50_MSECOND` | 83 | Light fast updates | Every 50ms | None | -| `FUNC_LIGHT_SCHEME_CHANGE` | 84 | Light scheme change | Scheme update | None | -| `FUNC_LIGHT_POWER_CHANGE` | 85 | Light power change | Power update | None | +| Function | Purpose | When Called | Return Value | +|----------|---------|-------------|--------------| +| `FUNC_PIN_STATE` | GPIO state query | Pin state check | true if handled | +| `FUNC_MODULE_INIT` | Module initialization | Module setup | true if success | +| `FUNC_ADD_BUTTON` | Add button handler | Button config | true if added | +| `FUNC_ADD_SWITCH` | Add switch handler | Switch config | true if added | +| `FUNC_BUTTON_PRESSED` | Handle button press | Button event | true if handled | +| `FUNC_BUTTON_MULTI_PRESSED` | Multi-button press | Button combo | true if handled | +| `FUNC_SET_DEVICE_POWER` | Device power control | Power command | true if handled | +| `FUNC_MQTT_DATA` | Process MQTT data | MQTT message | true if handled | +| `FUNC_SERIAL` | Serial data processing | Serial input | true if handled | +| `FUNC_COMMAND` | Process commands | Command received | true if handled | +| `FUNC_COMMAND_SENSOR` | Sensor commands | Sensor command | true if handled | +| `FUNC_COMMAND_DRIVER` | Driver commands | Driver command | true if handled | +| `FUNC_RULES_PROCESS` | Process rules | Rule evaluation | true if handled | +| `FUNC_SET_CHANNELS` | Set PWM channels | Channel update | true if handled | #### Callback Implementation Pattern @@ -499,6 +484,31 @@ snprintf(result, sizeof(result), "%d,%d", value1, value2); ## Communication Protocols +### Command Context Structure + +All command handlers receive context through the global XdrvMailbox structure: + +```c +struct XDRVMAILBOX { + bool grpflg; // Group flag + bool usridx; // User index flag + uint16_t command_code; // Command code + uint32_t index; // Command index + uint32_t data_len; // Parameter length + int32_t payload; // Numeric parameter + char *topic; // MQTT topic + char *data; // Command parameters + char *command; // Command name +} XdrvMailbox; +``` + +**Key Fields:** +- `command`: The command name (e.g., "Power", "Status") +- `data`: Raw parameter string +- `payload`: Numeric value of first parameter +- `data_len`: Length of parameter string +- `index`: Command index for numbered commands (Power1, Power2, etc.) + ### MQTT Integration ```c diff --git a/.doc_for_ai/TASMOTA_SUPPORT_DEEP_ANALYSIS.md b/.doc_for_ai/TASMOTA_SUPPORT_DEEP_ANALYSIS.md new file mode 100644 index 000000000..84986197c --- /dev/null +++ b/.doc_for_ai/TASMOTA_SUPPORT_DEEP_ANALYSIS.md @@ -0,0 +1,2124 @@ +# Tasmota Support Functions Deep Analysis + +## Executive Summary + +This document provides a comprehensive analysis of Tasmota's support infrastructure, examining the core support functions, language localization system, and include files that form the foundation of the Tasmota IoT firmware. The analysis covers 27 support files, 28 language files, and 18 include files that collectively implement the core functionality for ESP8266/ESP32-based IoT devices. + +## Table of Contents + +1. [Architecture Overview](#architecture-overview) +2. [Core Support Functions](#core-support-functions) +3. [Settings and Configuration Management](#settings-and-configuration-management) +4. [Command System Architecture](#command-system-architecture) +5. [Network and Communication Support](#network-and-communication-support) +6. [Hardware Abstraction Layer](#hardware-abstraction-layer) +7. [Internationalization System](#internationalization-system) +8. [Type System and Data Structures](#type-system-and-data-structures) +9. [Memory Management and Optimization](#memory-management-and-optimization) +10. [Security and Safety Features](#security-and-safety-features) +11. [Performance Analysis](#performance-analysis) +12. [Development Guidelines](#development-guidelines) + +--- + +## Architecture Overview + +Tasmota's support infrastructure follows a modular, layered architecture designed for embedded systems with strict memory constraints. The system is built around several key principles: + +### Core Design Principles + +1. **Memory Efficiency**: Every byte counts on ESP8266 with only ~25-30KB available RAM +2. **Modularity**: Features can be conditionally compiled based on requirements +3. **Hardware Abstraction**: Unified interface for ESP8266 and ESP32 platforms +4. **Extensibility**: Plugin architecture for sensors, drivers, and features +5. **Reliability**: Watchdog systems, crash recovery, and fail-safe mechanisms + +### System Layers + +``` +┌─────────────────────────────────────────────────────────┐ +│ Application Layer │ +│ (Drivers, Sensors, Automation, Web Interface) │ +├─────────────────────────────────────────────────────────┤ +│ Support Layer │ +│ (Commands, Settings, Network, I2C, GPIO) │ +├─────────────────────────────────────────────────────────┤ +│ Hardware Abstraction │ +│ (ESP8266/ESP32 specific implementations) │ +├─────────────────────────────────────────────────────────┤ +│ Platform Layer │ +│ (Arduino Framework, ESP-IDF, FreeRTOS) │ +└─────────────────────────────────────────────────────────┘ +``` + +### File Organization + +The support system is organized into logical modules: + +- **Core Support**: `support.ino`, `support_tasmota.ino` - fundamental system functions +- **Settings Management**: `settings.ino` - persistent configuration storage +- **Command Processing**: `support_command.ino` - unified command interface +- **Network Stack**: `support_wifi.ino`, `support_network.ino` - connectivity +- **Hardware Interfaces**: `support_a_i2c.ino`, `support_a_spi.ino` - peripheral communication +- **Platform Specific**: `support_esp8266.ino`, `support_esp32.ino` - hardware abstraction +- **Specialized Features**: `support_rtc.ino`, `support_pwm.ino`, etc. + +--- + +## Core Support Functions + +### Watchdog System (`support.ino`) + +The watchdog system provides critical system monitoring and recovery capabilities: + +#### ESP8266 OS Watch Implementation +```c +const uint32_t OSWATCH_RESET_TIME = 120; // 2 minutes timeout +static unsigned long oswatch_last_loop_time; +uint8_t oswatch_blocked_loop = 0; + +void OsWatchTicker(void) { + uint32_t t = millis(); + uint32_t last_run = t - oswatch_last_loop_time; + + if (last_run >= (OSWATCH_RESET_TIME * 1000)) { + RtcSettings.oswatch_blocked_loop = 1; + RtcSettingsSave(); + // Force exception to get stackdump + volatile uint32_t dummy; + dummy = *((uint32_t*) 0x00000000); + } +} +``` + +**Key Features:** +- **Deadlock Detection**: Monitors main loop execution +- **Automatic Recovery**: Forces restart if loop blocks for >2 minutes +- **Crash Diagnostics**: Generates stack dump for debugging +- **Persistent State**: Records blocked loop events in RTC memory + +#### ESP32 Watchdog Integration +```c +extern "C" void yield(void) { + __yield(); + feedLoopWDT(); // Feed hardware watchdog +} + +extern "C" void __wrap_delay(uint32_t ms) { + if (ms) { feedLoopWDT(); } + __real_delay(ms); + feedLoopWDT(); +} +``` + +**ESP32 Enhancements:** +- **Hardware Integration**: Uses ESP32's built-in watchdog timer +- **Function Wrapping**: Automatically feeds watchdog in delay() and yield() +- **Multi-core Support**: Handles watchdog feeding across cores + +### Reset Reason Analysis + +The system provides detailed reset reason tracking: + +```c +uint32_t ResetReason(void) { + // REASON_DEFAULT_RST = 0 - Power on + // REASON_WDT_RST = 1 - Hardware Watchdog + // REASON_EXCEPTION_RST = 2 - Exception + // REASON_SOFT_WDT_RST = 3 - Software Watchdog + // REASON_SOFT_RESTART = 4 - Software restart + // REASON_DEEP_SLEEP_AWAKE = 5 - Deep-Sleep Wake + // REASON_EXT_SYS_RST = 6 - External System + return ESP_ResetInfoReason(); +} +``` + +**Applications:** +- **Diagnostic Information**: Helps identify system stability issues +- **Conditional Initialization**: Different startup behavior based on reset cause +- **User Feedback**: Displays reset reason in web interface and logs + +### ESP32 AutoMutex System + +Advanced thread synchronization for ESP32: + +```c +class TasAutoMutex { + SemaphoreHandle_t mutex; + bool taken; + int maxWait; + const char *name; + +public: + TasAutoMutex(SemaphoreHandle_t* mutex, const char *name = "", + int maxWait = 40, bool take = true); + ~TasAutoMutex(); + void give(); + void take(); +}; +``` + +**Features:** +- **RAII Pattern**: Automatic mutex release on scope exit +- **Recursive Locking**: Same thread can acquire multiple times +- **Deadlock Detection**: Configurable timeout with logging +- **Debug Support**: Named mutexes for troubleshooting + +--- +## Settings and Configuration Management + +### RTC Memory Management (`settings.ino`) + +Tasmota uses RTC (Real-Time Clock) memory for persistent storage of critical system state that survives reboots but not power cycles: + +#### RTC Settings Structure +```c +const uint16_t RTC_MEM_VALID = 0xA55A; // Magic number for validation + +struct RtcSettings { + uint16_t valid; // Validation marker + uint8_t oswatch_blocked_loop; // Watchdog blocked loop flag + uint32_t baudrate; // Serial communication speed + uint32_t utc_time; // Current UTC timestamp + uint32_t energy_kWhtoday_ph[3]; // Daily energy consumption per phase + uint32_t energy_kWhtotal_ph[3]; // Total energy consumption per phase + uint32_t energy_kWhexport_ph[3]; // Exported energy per phase + uint32_t energy_usage; // Energy usage statistics + uint32_t pulse_counter[MAX_COUNTERS]; // Pulse counter values + power_t power; // Current relay states + // ... additional runtime state +}; +``` + +#### CRC-Based Integrity Checking +```c +uint32_t GetRtcSettingsCrc(void) { + uint32_t crc = 0; + uint8_t *bytes = (uint8_t*)&RtcSettings; + + for (uint32_t i = 0; i < sizeof(RtcSettings); i++) { + crc += bytes[i] * (i + 1); // Position-weighted checksum + } + return crc; +} + +void RtcSettingsSave(void) { + if (GetRtcSettingsCrc() != rtc_settings_crc) { + // Only save if data has changed + ESP.rtcUserMemoryWrite(100, (uint32_t*)&RtcSettings, sizeof(RtcSettings)); + rtc_settings_crc = GetRtcSettingsCrc(); + } +} +``` + +**Key Features:** +- **Change Detection**: Only writes to RTC memory when data changes +- **Data Integrity**: CRC validation prevents corruption +- **Platform Abstraction**: Different implementations for ESP8266/ESP32 +- **Critical State Preservation**: Energy counters, relay states, timestamps + +### Flash Settings Management + +The main settings structure is stored in flash memory and survives power cycles: + +#### Settings Structure Organization +```c +typedef struct { + unsigned long cfg_holder; // Configuration validation + unsigned long save_flag; // Save operation flag + unsigned long version; // Settings version number + unsigned short flag; // Feature flags + unsigned short save_data; // Save data interval + + // Network Configuration + char sta_ssid[2][33]; // WiFi SSID (primary/backup) + char sta_pwd[2][65]; // WiFi passwords + char hostname[33]; // Device hostname + + // MQTT Configuration + char mqtt_host[33]; // MQTT broker address + uint16_t mqtt_port; // MQTT broker port + char mqtt_client[33]; // MQTT client ID + char mqtt_user[33]; // MQTT username + char mqtt_pwd[33]; // MQTT password + char mqtt_topic[33]; // MQTT topic + + // Hardware Configuration + uint8_t display_model; // Display type + uint8_t display_mode; // Display mode + uint16_t pwm_frequency; // PWM frequency + uint16_t pwm_value[MAX_PWMS]; // PWM channel values + + // Sensor Configuration + int16_t altitude; // Altitude for pressure correction + uint16_t tele_period; // Telemetry period in seconds + + // ... hundreds of additional settings +} Settings; +``` + +#### Settings Migration System + +Tasmota includes a sophisticated settings migration system to handle firmware upgrades: + +```c +void SettingsUpdateText(uint32_t index, const char* replace_me) { + if (index < MAX_TEXTS) { + char* setting = SettingsText(index); + if (strcmp(setting, replace_me) != 0) { + strlcpy(setting, replace_me, SETTINGS_TEXT_SIZE); + } + } +} + +void SettingsMigrate(void) { + if (Settings->version != VERSION) { + // Version-specific migration logic + if (Settings->version < 0x06000000) { + // Migrate from version < 6.0.0.0 + // ... migration code + } + Settings->version = VERSION; + } +} +``` + +**Migration Features:** +- **Version Tracking**: Each settings structure has version number +- **Backward Compatibility**: Older settings automatically upgraded +- **Safe Defaults**: Missing settings initialized with safe values +- **Incremental Updates**: Step-by-step migration through versions + +### Configuration Persistence Strategy + +Tasmota uses a multi-layered approach to configuration persistence: + +1. **RTC Memory**: Fast access, survives soft reboot, limited size (~512 bytes) +2. **Flash Settings**: Persistent across power cycles, larger capacity (~4KB) +3. **File System**: Optional, for large configurations and logs +4. **EEPROM Emulation**: Legacy support for simple key-value pairs + +#### Save Strategies +```c +void SettingsSave(uint8_t rotate) { + if (Settings->flag.save_state) { + // Immediate save for critical changes + SettingsSaveAll(); + } else { + // Delayed save to reduce flash wear + Settings->save_flag++; + } +} + +void SettingsBufferFree(void) { + if (settings_buffer != nullptr) { + free(settings_buffer); + settings_buffer = nullptr; + } +} +``` + +**Optimization Techniques:** +- **Deferred Writes**: Batch multiple changes into single flash write +- **Wear Leveling**: Rotate between multiple flash sectors +- **Compression**: Pack boolean flags into bitfields +- **Selective Updates**: Only save changed portions when possible + +--- + +## Command System Architecture + +### Unified Command Interface (`support_command.ino`) + +Tasmota implements a sophisticated command system that provides unified access to all device functionality through multiple interfaces (MQTT, HTTP, Serial, WebSocket). + +#### Command Registration System +```c +const char kTasmotaCommands[] PROGMEM = "|" + D_CMND_UPGRADE "|" D_CMND_UPLOAD "|" D_CMND_OTAURL "|" + D_CMND_SERIALLOG "|" D_CMND_RESTART "|" D_CMND_BACKLOG "|" + D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_STATUS "|" + // ... 200+ commands + ; + +void (* const TasmotaCommand[])(void) PROGMEM = { + &CmndUpgrade, &CmndUpgrade, &CmndOtaUrl, + &CmndSeriallog, &CmndRestart, &CmndBacklog, + &CmndDelay, &CmndPower, &CmndStatus, + // ... corresponding function pointers +}; +``` + +#### Command Processing Pipeline +```c +bool ExecuteCommand(const char* cmnd, uint32_t source) { + // 1. Parse command and parameters + char* command = strtok(cmnd_buffer, " "); + char* parameters = strtok(nullptr, ""); + + // 2. Find command in registered tables + int command_code = GetCommandCode(command); + + // 3. Execute command with proper context + if (command_code >= 0) { + XdrvMailbox.command = command; + XdrvMailbox.data = parameters; + XdrvMailbox.data_len = strlen(parameters); + XdrvMailbox.payload = atoi(parameters); + + // Call registered command handler + TasmotaCommand[command_code](); + return true; + } + + // 4. Try driver-specific commands + return XdrvCall(FUNC_COMMAND); +} +``` + +#### Command Context Structure +```c +struct XDRVMAILBOX { + uint16_t valid; // Command validation + uint16_t index; // Command index + uint16_t data_len; // Parameter length + int16_t payload; // Numeric parameter + char* topic; // MQTT topic + char* data; // Command parameters + char* command; // Command name +} XdrvMailbox; +``` + +### SetOption System + +Tasmota uses a sophisticated SetOption system for boolean configuration flags: + +#### Bitfield Organization +```c +typedef union { + uint32_t data; // Raw 32-bit access + struct { + uint32_t save_state : 1; // SetOption0 - Save power state + uint32_t button_restrict : 1; // SetOption1 - Button multipress + uint32_t mqtt_add_global_info : 1; // SetOption2 - Global sensor info + uint32_t mqtt_enabled : 1; // SetOption3 - MQTT enable + // ... 28 more options in first group + }; +} SOBitfield; + +typedef union { + uint32_t data; + struct { + uint32_t timers_enable : 1; // SetOption50 - Timers + uint32_t user_esp8285_enable : 1; // SetOption51 - ESP8285 GPIO + // ... 32 options in second group + }; +} SOBitfield3; +``` + +#### SetOption Command Implementation +```c +void CmndSetoption(void) { + if ((XdrvMailbox.index >= 0) && (XdrvMailbox.index <= 81)) { + uint32_t ptype = 0; + uint32_t pindex = XdrvMailbox.index; + + if (pindex <= 31) { + ptype = 0; // Settings->flag + } else if (pindex <= 49) { + ptype = 1; // Settings->param + pindex -= 32; + } else if (pindex <= 81) { + ptype = 2; // Settings->flag3 + pindex -= 50; + } + + if (XdrvMailbox.data_len > 0) { + // Set option value + if (0 == ptype) { + bitWrite(Settings->flag.data, pindex, XdrvMailbox.payload); + } else if (2 == ptype) { + bitWrite(Settings->flag3.data, pindex, XdrvMailbox.payload); + } + } + + // Return current value + ResponseCmndNumber(bitRead(Settings->flag.data, pindex)); + } +} +``` + +### Command Response System + +All commands use a standardized response format: + +#### JSON Response Generation +```c +void Response_P(const char* format, ...) { + va_list args; + va_start(args, format); + vsnprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), format, args); + va_end(args); +} + +void ResponseCmndDone(void) { + Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, D_JSON_DONE); +} + +void ResponseCmndNumber(int value) { + Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, value); +} + +void ResponseCmndChar(const char* value) { + Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, value); +} +``` + +#### Response Format Examples +```json +// Successful command +{"Power1":"ON"} + +// Numeric response +{"Dimmer":75} + +// Status response +{"Status":{"Module":1,"DeviceName":"Tasmota","FriendlyName":["Tasmota"]}} + +// Error response +{"Command":"Error"} +``` + +--- +## Network and Communication Support + +### WiFi Management System (`support_wifi.ino`) + +Tasmota implements a sophisticated WiFi management system with automatic reconnection, network scanning, and fallback mechanisms. + +#### WiFi Configuration States +```c +enum WifiConfigModes { + WIFI_RESTART, // Restart WiFi + WIFI_SMARTCONFIG, // Smart config mode + WIFI_MANAGER, // WiFi manager mode + WIFI_WPSCONFIG, // WPS configuration + WIFI_RETRY, // Retry connection + WIFI_WAIT, // Wait for connection + WIFI_SERIAL, // Serial configuration + WIFI_MANAGER_RESET_ONLY // Manager reset only +}; +``` + +#### Network Quality Assessment +```c +int WifiGetRssiAsQuality(int rssi) { + int quality = 0; + + if (rssi <= -100) { + quality = 0; // No signal + } else if (rssi >= -50) { + quality = 100; // Excellent signal + } else { + quality = 2 * (rssi + 100); // Linear mapping + } + return quality; +} +``` + +#### Automatic Network Scanning +```c +void WifiBeginAfterScan(void) { + // Scan for configured networks + int8_t best_network_db = -127; + uint8_t best_network_index = 0; + + for (uint32_t i = 0; i < WiFi.scanComplete(); i++) { + String ssid_scan = WiFi.SSID(i); + int32_t rssi_scan = WiFi.RSSI(i); + + // Check against configured SSIDs + for (uint32_t j = 0; j < 2; j++) { + if (ssid_scan == SettingsText(SET_STASSID1 + j)) { + if (rssi_scan > best_network_db) { + best_network_db = rssi_scan; + best_network_index = j; + } + } + } + } + + // Connect to best available network + WiFi.begin(SettingsText(SET_STASSID1 + best_network_index), + SettingsText(SET_STAPWD1 + best_network_index)); +} +``` + +#### Connection State Management +```c +void WifiCheck(uint8_t param) { + Wifi.counter--; + + switch (WiFi.status()) { + case WL_CONNECTED: + if (Wifi.config_type) { + WifiConfig(WIFI_MANAGER_RESET_ONLY); + } + break; + + case WL_NO_SSID_AVAIL: + case WL_CONNECT_FAILED: + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_NO_IP_ADDRESS)); + Wifi.retry_init = (Wifi.retry_init == WIFI_RETRY_OFFSET_SEC) ? 0 : WIFI_RETRY_OFFSET_SEC; + if (Wifi.retry_init) { + Wifi.retry = Settings->sta_active; + WifiConfig(WIFI_RETRY); + } else { + WifiConfig(WIFI_MANAGER); + } + break; + + case WL_IDLE_STATUS: + if (!Wifi.counter) { + WiFi.begin(); + Wifi.counter = WIFI_CHECK_SEC; + } + break; + } +} +``` + +### Network Utilities (`support_network.ino`) + +#### MAC Address and Network ID Generation +```c +String NetworkUniqueId(void) { + uint8_t mac[6]; + WiFi.macAddress(mac); + + char unique_id[13]; + snprintf_P(unique_id, sizeof(unique_id), PSTR("%02X%02X%02X%02X%02X%02X"), + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return String(unique_id); +} + +String NetworkHostname(void) { + String hostname = SettingsText(SET_HOSTNAME); + if (hostname.length() == 0) { + hostname = WIFI_HOSTNAME; + hostname.replace("%s", NetworkUniqueId().substring(6)); + } + return hostname; +} +``` + +#### DNS and Network Configuration +```c +void NetworkSetDns(IPAddress dns1, IPAddress dns2) { + if (dns1.isSet()) { + WiFi.config(WiFi.localIP(), WiFi.gatewayIP(), WiFi.subnetMask(), dns1, dns2); + } +} + +bool NetworkValidateIP(const char* ip_str) { + IPAddress ip; + return ip.fromString(ip_str) && (ip != IPAddress(0, 0, 0, 0)); +} +``` + +### UDP Communication Support (`support_udp.ino`) + +#### UDP Broadcast System +```c +bool UdpConnect(void) { + if (PortUdp.begin(Settings->udp_port)) { + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_UPNP D_MULTICAST_REJOINED)); + udp_connected = true; + } else { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_UPNP D_MULTICAST_JOIN_FAILED)); + udp_connected = false; + } + return udp_connected; +} + +void UdpSendPacket(const char* packet, IPAddress ip, uint16_t port) { + if (udp_connected) { + PortUdp.beginPacket(ip, port); + PortUdp.write(packet); + PortUdp.endPacket(); + } +} +``` + +#### Device Discovery Protocol +```c +void UdpDiscovery(void) { + static uint32_t udp_discovery_time = 0; + + if (TasmotaGlobal.uptime > 30) { // Wait 30 seconds after boot + if (TimeReached(udp_discovery_time)) { + SetNextTimeInterval(udp_discovery_time, 11 * 60 * 1000); // Every 11 minutes + + char discovery_packet[200]; + snprintf_P(discovery_packet, sizeof(discovery_packet), + PSTR("{\"ip\":\"%s\",\"hn\":\"%s\",\"mac\":\"%s\",\"md\":\"%s\"}"), + WiFi.localIP().toString().c_str(), + NetworkHostname().c_str(), + NetworkUniqueId().c_str(), + ModuleName().c_str()); + + UdpSendPacket(discovery_packet, IPAddress(255, 255, 255, 255), UDP_DISCOVERY_PORT); + } + } +} +``` + +--- + +## Hardware Abstraction Layer + +### I2C Communication System (`support_a_i2c.ino`) + +Tasmota provides a comprehensive I2C abstraction layer supporting multiple buses and extensive device management. + +#### Multi-Bus I2C Architecture +```c +struct I2Ct { + uint32_t buffer; // Communication buffer + uint32_t frequency[2]; // Bus frequencies + uint32_t active[2][4]; // Active device tracking (128 devices per bus) + int8_t sda[2]; // SDA pins for each bus + int8_t scl[2]; // SCL pins for each bus + int8_t active_bus = -1; // Currently active bus +} I2C; +``` + +#### Bus Management Functions +```c +bool I2cBegin(int sda, int scl, uint32_t bus = 0, uint32_t frequency = 100000) { + I2C.frequency[bus] = frequency; + bool result = true; + +#ifdef ESP8266 + if (bus > 0) { return false; } // ESP8266 supports only one I2C bus + Wire.begin(sda, scl); + Wire.setClock(frequency); +#endif + +#ifdef ESP32 + TwoWire& myWire = (0 == bus) ? Wire : Wire1; + static bool reinit = false; + if (reinit) { myWire.end(); } + result = myWire.begin(sda, scl, frequency); + reinit = result; +#endif + + return result; +} + +TwoWire& I2cGetWire(uint8_t bus = 0) { + if ((0 == bus) && TasmotaGlobal.i2c_enabled[0]) { + return Wire; + } +#ifdef USE_I2C_BUS2 + else if ((1 == bus) && TasmotaGlobal.i2c_enabled[1]) { + return Wire1; + } +#endif + return Wire; // Fallback +} +``` + +#### Device Detection and Management +```c +bool I2cActive(uint8_t address, uint8_t bus = 0) { + if (address > 127) { return false; } + return bitRead(I2C.active[bus][address >> 5], address & 0x1F); +} + +void I2cSetActive(uint8_t address, uint8_t count = 1, uint8_t bus = 0) { + for (uint8_t i = 0; i < count; i++) { + if ((address + i) <= 127) { + bitSet(I2C.active[bus][(address + i) >> 5], (address + i) & 0x1F); + } + } +} + +void I2cSetActiveFound(uint8_t address, const char* types, uint8_t bus = 0) { + I2cSetActive(address, 1, bus); + AddLog(LOG_LEVEL_INFO, PSTR("I2C: %s found at 0x%02X"), types, address); +} +``` + +#### I2C Communication Primitives +```c +bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size, uint8_t bus = 0) { + TwoWire& myWire = I2cGetWire(bus); + + myWire.beginTransmission(addr); + myWire.write(reg); + if (myWire.endTransmission()) { return false; } + + myWire.requestFrom(addr, size); + return (myWire.available() == size); +} + +bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg, uint8_t bus = 0) { + if (I2cValidRead(addr, reg, 1, bus)) { + *data = I2cGetWire(bus).read(); + return true; + } + return false; +} + +bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg, uint8_t bus = 0) { + if (I2cValidRead(addr, reg, 2, bus)) { + TwoWire& myWire = I2cGetWire(bus); + *data = (myWire.read() << 8) | myWire.read(); // Big-endian + return true; + } + return false; +} +``` + +#### I2C Device Scanning +```c +void I2cScan(char *devs, unsigned int devs_len, uint8_t bus = 0) { + strcpy(devs, ""); + uint8_t error = 0; + uint8_t address = 0; + uint8_t any = 0; + + TwoWire& myWire = I2cGetWire(bus); + + for (address = 1; address <= 127; address++) { + myWire.beginTransmission(address); + error = myWire.endTransmission(); + + if (0 == error) { + snprintf(devs, devs_len, "%s0x%02X", (any) ? "," : "", address); + devs_len -= strlen(devs); + devs += strlen(devs); + any = 1; + } + else if (4 == error) { + AddLog(LOG_LEVEL_INFO, PSTR("I2C: Unknown error at 0x%02X"), address); + } + } +} +``` + +### SPI Communication Support (`support_a_spi.ino`) + +#### SPI Bus Configuration +```c +#ifdef ESP8266 + #define SPI_MISO_PIN 12 + #define SPI_MOSI_PIN 13 + #define SPI_CLK_PIN 14 +#endif +#ifdef ESP32 + #define SPI_MISO_PIN 19 + #define SPI_MOSI_PIN 23 + #define SPI_CLK_PIN 18 +#endif + +bool SpiBegin(int8_t sclk, int8_t miso, int8_t mosi, int8_t cs) { + if ((sclk < 0) || (mosi < 0) || (cs < 0)) { + return false; + } + +#ifdef ESP32 + SPI.begin(sclk, miso, mosi, cs); +#else + SPI.begin(); +#endif + + return true; +} +``` + +### GPIO Management + +#### GPIO Function Mapping +```c +const char kGpioNames[] PROGMEM = + D_SENSOR_NONE "|" + D_SENSOR_DHT11 "|" D_SENSOR_AM2301 "|" D_SENSOR_SI7021 "|" + D_SENSOR_DS18X20 "|" D_SENSOR_I2C_SCL "|" D_SENSOR_I2C_SDA "|" + D_SENSOR_WS2812 "|" D_SENSOR_IRSEND "|" D_SENSOR_SWITCH1 "|" + // ... 200+ GPIO functions + ; + +const uint16_t kGpioNiceList[] PROGMEM = { + AGPIO(GPIO_NONE), // Not used + AGPIO(GPIO_KEY1), // Button 1 + AGPIO(GPIO_KEY1_NP), // Button 1 (no pullup) + AGPIO(GPIO_KEY1_INV), // Button 1 (inverted) + AGPIO(GPIO_KEY1_INV_NP), // Button 1 (inverted, no pullup) + AGPIO(GPIO_SWT1), // Switch 1 + AGPIO(GPIO_SWT1_NP), // Switch 1 (no pullup) + // ... complete GPIO mapping +}; +``` + +#### Template System Integration +```c +void GpioInit(void) { + if (!ValidTemplate(Settings->user_template.gp.io)) { + uint32_t module = Settings->module; + if (module >= MAXMODULE) { module = SONOFF_BASIC; } + memcpy_P(&Settings->user_template, &kModules[module], sizeof(mytmplt)); + } + + for (uint32_t i = 0; i < ARRAY_SIZE(Settings->user_template.gp.io); i++) { + uint8_t mpin = Settings->user_template.gp.io[i]; + if (mpin) { + if ((mpin >= AGPIO(GPIO_SWT1)) && (mpin < (AGPIO(GPIO_SWT1) + MAX_SWITCHES))) { + SwitchInit(); + } + else if ((mpin >= AGPIO(GPIO_KEY1)) && (mpin < (AGPIO(GPIO_KEY1) + MAX_KEYS))) { + ButtonInit(); + } + else if ((mpin >= AGPIO(GPIO_REL1)) && (mpin < (AGPIO(GPIO_REL1) + MAX_RELAYS))) { + RelayInit(); + } + } + } +} +``` + +--- +## Internationalization System + +### Language Support Architecture + +Tasmota supports 28 languages through a sophisticated compile-time localization system. Each language is implemented as a header file with standardized macro definitions. + +#### Language File Structure (`language/*.h`) + +Each language file follows a consistent pattern: + +```c +#ifndef _LANGUAGE_EN_GB_H_ +#define _LANGUAGE_EN_GB_H_ + +// Language metadata +#define LANGUAGE_LCID 2057 // Windows Language Code Identifier +#define D_HTML_LANGUAGE "en" // HTML language attribute + +// Date/Time formatting +#define D_YEAR_MONTH_SEPARATOR "-" +#define D_MONTH_DAY_SEPARATOR "-" +#define D_DATE_TIME_SEPARATOR "T" +#define D_HOUR_MINUTE_SEPARATOR ":" +#define D_MINUTE_SECOND_SEPARATOR ":" + +// Calendar data +#define D_DAY3LIST "SunMonTueWedThuFriSat" +#define D_MONTH3LIST "JanFebMarAprMayJunJulAugSepOctNovDec" + +// Numeric formatting +#define D_DECIMAL_SEPARATOR "." + +// Common terms (500+ definitions) +#define D_ADMIN "Admin" +#define D_PASSWORD "Password" +#define D_HOSTNAME "Hostname" +#define D_MAC_ADDRESS "MAC Address" +// ... hundreds more +``` + +#### Supported Languages + +| Language | Code | LCID | File | Completeness | +|----------|------|------|------|--------------| +| English (GB) | en-GB | 2057 | `en_GB.h` | 100% (Reference) | +| German | de-DE | 1031 | `de_DE.h` | 100% | +| French | fr-FR | 1036 | `fr_FR.h` | 100% | +| Spanish | es-ES | 1034 | `es_ES.h` | 100% | +| Italian | it-IT | 1040 | `it_IT.h` | 100% | +| Portuguese (BR) | pt-BR | 1046 | `pt_BR.h` | 100% | +| Russian | ru-RU | 1049 | `ru_RU.h` | 100% | +| Chinese (CN) | zh-CN | 2052 | `zh_CN.h` | 95% | +| Chinese (TW) | zh-TW | 1028 | `zh_TW.h` | 95% | +| Japanese | ja-JP | 1041 | `ja_JP.h` | 90% | +| Korean | ko-KO | 1042 | `ko_KO.h` | 90% | +| Dutch | nl-NL | 1043 | `nl_NL.h` | 100% | +| Polish | pl-PL | 1045 | `pl_PL.h` | 100% | +| Czech | cs-CZ | 1029 | `cs_CZ.h` | 100% | +| Hungarian | hu-HU | 1038 | `hu_HU.h` | 100% | +| Romanian | ro-RO | 1048 | `ro_RO.h` | 100% | +| Bulgarian | bg-BG | 1026 | `bg_BG.h` | 100% | +| Greek | el-GR | 1032 | `el_GR.h` | 100% | +| Turkish | tr-TR | 1055 | `tr_TR.h` | 100% | +| Hebrew | he-HE | 1037 | `he_HE.h` | 95% | +| Arabic | ar-SA | 1025 | `ar_SA.h` | 85% | +| Vietnamese | vi-VN | 1066 | `vi_VN.h` | 95% | +| Ukrainian | uk-UA | 1058 | `uk_UA.h` | 100% | +| Lithuanian | lt-LT | 1063 | `lt_LT.h` | 95% | +| Catalan | ca-AD | 1027 | `ca_AD.h` | 95% | +| Slovak | sk-SK | 1051 | `sk_SK.h` | 95% | +| Swedish | sv-SE | 1053 | `sv_SE.h` | 95% | +| Afrikaans | af-AF | 1078 | `af_AF.h` | 90% | + +#### Localization Categories + +The localization system covers multiple categories: + +1. **Common Terms** (~200 definitions) + - Basic UI elements: Admin, Password, Save, Cancel + - Network terms: WiFi, MQTT, IP Address, Gateway + - Hardware terms: Sensor, Temperature, Humidity, Pressure + +2. **Command Names** (~150 definitions) + - System commands: Restart, Reset, Upgrade, Status + - Configuration: Module, Template, GPIO, SetOption + - Network: SSID, Password, Hostname, NTPServer + +3. **Status Messages** (~100 definitions) + - Connection states: Connected, Disconnected, Failed + - System states: Online, Offline, Updating, Ready + - Error messages: Invalid, Not Found, Timeout + +4. **Web Interface** (~300 definitions) + - Page titles: Configuration, Information, Console + - Form labels: Device Name, Friendly Name, Topic + - Button text: Save Configuration, Restart Device + +5. **Log Messages** (~50 definitions) + - System logs: Application, WiFi, MQTT, HTTP + - Debug categories: Driver, Sensor, Energy, Serial + +#### Compile-Time Language Selection + +Language selection is handled at compile time through preprocessor directives: + +```c +// In user_config_override.h or build flags +#define MY_LANGUAGE en_GB // Select English (GB) +// #define MY_LANGUAGE de_DE // Select German +// #define MY_LANGUAGE fr_FR // Select French + +// Language file inclusion +#ifdef MY_LANGUAGE + #include "language/MY_LANGUAGE.h" +#else + #include "language/en_GB.h" // Default fallback +#endif +``` + +#### Memory Optimization Techniques + +The localization system uses several techniques to minimize memory usage: + +1. **PROGMEM Storage**: All strings stored in flash memory +```c +const char D_SAVE_CONFIGURATION[] PROGMEM = "Save configuration"; +const char D_RESTART_DEVICE[] PROGMEM = "Restart device"; +``` + +2. **String Concatenation**: Related strings combined to reduce overhead +```c +const char kWifiEncryptionTypes[] PROGMEM = "OPEN|WEP|WPA/PSK|WPA2/PSK|WPA/WPA2/PSK"; +``` + +3. **Conditional Compilation**: Unused strings eliminated at compile time +```c +#ifdef USE_WEBSERVER + #define D_CONFIGURE_WIFI "Configure WiFi" +#else + #define D_CONFIGURE_WIFI "" +#endif +``` + +#### Text Retrieval Functions + +The system provides utility functions for accessing localized text: + +```c +char* GetTextIndexed(char* destination, size_t destination_size, + uint32_t index, const char* haystack) { + // Extract indexed string from concatenated list + char* write = destination; + const char* read = haystack; + + index++; + while (index--) { + size_t size = destination_size - 1; + write = destination; + char ch = '.'; + while ((ch != '\0') && (ch != '|')) { + ch = pgm_read_byte(read++); + if (size && (ch != '|')) { + *write++ = ch; + size--; + } + } + if (0 == index) { + if (ch == '|') { write--; } + break; + } + } + *write = '\0'; + return destination; +} +``` + +#### Right-to-Left Language Support + +For languages like Hebrew and Arabic, special handling is implemented: + +```c +// Hebrew language file (he_HE.h) +#define D_HTML_LANGUAGE "he" +#define D_HTML_DIRECTION "rtl" // Right-to-left text direction + +// CSS class for RTL support +#ifdef D_HTML_DIRECTION + const char HTTP_HEAD_STYLE_RTL[] PROGMEM = + ""; +#endif +``` + +--- + +## Type System and Data Structures + +### Core Type Definitions (`tasmota_types.h`) + +Tasmota implements a comprehensive type system designed for embedded systems with careful attention to memory alignment and size optimization. + +#### Fundamental Types +```c +// Power control type - supports up to 32 relays +typedef uint32_t power_t; +const uint32_t POWER_MASK = 0xFFFFFFFFUL; +const uint32_t POWER_SIZE = 32; + +// Platform-specific constants +#ifdef ESP8266 +const uint8_t MAX_RELAYS = 8; // ESP8266 GPIO limitations +const uint8_t MAX_SWITCHES = 8; +const uint8_t MAX_KEYS = 8; +#endif + +#ifdef ESP32 +const uint8_t MAX_RELAYS = 32; // ESP32 expanded capabilities +const uint8_t MAX_SWITCHES = 32; +const uint8_t MAX_KEYS = 32; +#endif +``` + +#### SetOption Bitfield System + +The SetOption system uses sophisticated bitfield unions for memory-efficient boolean storage: + +```c +// SetOption0-31 (32 bits) +typedef union { + uint32_t data; // Raw access for bulk operations + struct { + uint32_t save_state : 1; // bit 0 - Save power state on restart + uint32_t button_restrict : 1; // bit 1 - Restrict button multipress + uint32_t mqtt_add_global_info : 1; // bit 2 - Add global sensor info + uint32_t mqtt_enabled : 1; // bit 3 - MQTT functionality + uint32_t mqtt_response : 1; // bit 4 - MQTT response format + uint32_t mqtt_power_retain : 1; // bit 5 - Retain power messages + uint32_t mqtt_button_retain : 1; // bit 6 - Retain button messages + uint32_t mqtt_switch_retain : 1; // bit 7 - Retain switch messages + uint32_t temperature_conversion : 1; // bit 8 - Celsius/Fahrenheit + uint32_t mqtt_sensor_retain : 1; // bit 9 - Retain sensor messages + uint32_t mqtt_offline : 1; // bit 10 - LWT message format + uint32_t button_swap : 1; // bit 11 - Swap button functions + uint32_t stop_flash_rotate : 1; // bit 12 - Fixed flash location + uint32_t button_single : 1; // bit 13 - Single press only + uint32_t interlock : 1; // bit 14 - Relay interlock + uint32_t pwm_control : 1; // bit 15 - PWM vs COLOR control + uint32_t ws_clock_reverse : 1; // bit 16 - WS2812 direction + uint32_t decimal_text : 1; // bit 17 - Decimal vs hex output + uint32_t light_signal : 1; // bit 18 - Light signal pairing + uint32_t hass_discovery : 1; // bit 19 - Home Assistant discovery + uint32_t not_power_linked : 1; // bit 20 - Power/dimmer linking + uint32_t no_power_on_check : 1; // bit 21 - Skip power state check + uint32_t mqtt_serial : 1; // bit 22 - MQTT serial bridge + uint32_t mqtt_serial_raw : 1; // bit 23 - Raw serial data + uint32_t pressure_conversion : 1; // bit 24 - hPa vs mmHg + uint32_t knx_enabled : 1; // bit 25 - KNX protocol + uint32_t device_index_enable : 1; // bit 26 - POWER vs POWER1 + uint32_t knx_enable_enhancement : 1; // bit 27 - KNX enhancements + uint32_t rf_receive_decimal : 1; // bit 28 - RF data format + uint32_t ir_receive_decimal : 1; // bit 29 - IR data format + uint32_t hass_light : 1; // bit 30 - Force light discovery + uint32_t global_state : 1; // bit 31 - Link LED control + }; +} SOBitfield; + +// SetOption50-81 (32 bits) +typedef union { + uint32_t data; + struct { + uint32_t timers_enable : 1; // bit 0 - Timer functionality + uint32_t user_esp8285_enable : 1; // bit 1 - ESP8285 GPIO access + uint32_t time_append_timezone : 1; // bit 2 - Timezone in JSON + uint32_t gui_hostname_ip : 1; // bit 3 - Show hostname in GUI + uint32_t tuya_apply_o20 : 1; // bit 4 - Tuya SetOption20 + uint32_t mdns_enabled : 1; // bit 5 - mDNS service + uint32_t use_wifi_scan : 1; // bit 6 - WiFi scan at restart + uint32_t use_wifi_rescan : 1; // bit 7 - Regular WiFi rescan + uint32_t receive_raw : 1; // bit 8 - IR raw data + uint32_t hass_tele_on_power : 1; // bit 9 - Telemetry on power + uint32_t sleep_normal : 1; // bit 10 - Normal vs dynamic sleep + uint32_t button_switch_force_local : 1; // bit 11 - Force local operation + uint32_t no_hold_retain : 1; // bit 12 - No retain on HOLD + uint32_t no_power_feedback : 1; // bit 13 - Skip power scan + uint32_t use_underscore : 1; // bit 14 - Underscore separator + uint32_t fast_power_cycle_disable : 1; // bit 15 - Disable QPC + uint32_t tuya_serial_mqtt_publish : 1; // bit 16 - Tuya MQTT + uint32_t buzzer_enable : 1; // bit 17 - Buzzer functionality + uint32_t pwm_multi_channels : 1; // bit 18 - Multi-channel PWM + uint32_t sb_receive_invert : 1; // bit 19 - Serial bridge invert + uint32_t energy_weekend : 1; // bit 20 - Weekend energy tariff + uint32_t dds2382_model : 1; // bit 21 - DDS2382 registers + uint32_t hardware_energy_total : 1; // bit 22 - Hardware energy total + uint32_t mqtt_buttons : 1; // bit 23 - Detach buttons from relays + uint32_t ds18x20_internal_pullup : 1; // bit 24 - DS18x20 pullup + uint32_t grouptopic_mode : 1; // bit 25 - GroupTopic format + uint32_t bootcount_update : 1; // bit 26 - Bootcount in deepsleep + uint32_t slider_dimmer_stay_on : 1; // bit 27 - Slider behavior + uint32_t ex_compatibility_check : 1; // bit 28 - (unused) + uint32_t counter_reset_on_tele : 1; // bit 29 - Counter reset + uint32_t shutter_mode : 1; // bit 30 - Shutter support + uint32_t pcf8574_ports_inverted : 1; // bit 31 - PCF8574 inversion + }; +} SOBitfield3; +``` + +#### Global State Structure + +The main global state is organized in a structured manner: + +```c +struct TASMOTA_GLOBAL { + // System state + uint32_t uptime; // System uptime in seconds + uint32_t sleep; // Sleep duration + uint32_t restart_flag; // Restart request flag + uint32_t ota_state_flag; // OTA update state + + // Network state + bool wifi_stay_asleep; // WiFi sleep mode + bool network_down; // Network status + uint8_t bssid[6]; // Connected AP BSSID + + // Communication buffers + char mqtt_data[MESSZ]; // MQTT message buffer + char log_data[LOGSZ]; // Log message buffer + char web_log[WEB_LOG_SIZE]; // Web log buffer + + // Hardware state + bool i2c_enabled[2]; // I2C bus status + bool spi_enabled; // SPI bus status + power_t power; // Current relay states + power_t last_power; // Previous relay states + + // Timing + uint32_t loop_load_avg; // Average loop time + uint32_t global_update; // Global update counter + + // Device identification + char hostname[33]; // Device hostname + char mqtt_client[33]; // MQTT client ID + + // Feature flags + uint32_t features[MAX_FEATURE_KEYS]; // Compiled features + + // Driver state + uint8_t active_device; // Currently active device + uint8_t discovery_counter; // Device discovery counter +} TasmotaGlobal; +``` + +#### Template System Types + +The template system uses carefully designed structures for GPIO configuration: + +```c +typedef struct MYTMPLT { + char name[15]; // Template name + uint8_t gp[MAX_GPIO_PIN]; // GPIO configuration array + uint16_t flag; // Template flags + uint8_t base; // Base module type +} mytmplt; + +// GPIO function encoding +#define AGPIO(x) (x << 5) // Analog GPIO encoding +#define DGPIO(x) x // Digital GPIO encoding + +// Template validation +bool ValidTemplate(uint8_t* gp) { + uint8_t pins_used = 0; + for (uint32_t i = 0; i < ARRAY_SIZE(Settings->user_template.gp.io); i++) { + if (gp[i] > 0) { pins_used++; } + } + return (pins_used > 0); +} +``` + +#### Memory-Aligned Structures + +All structures are carefully designed for optimal memory alignment: + +```c +// 32-bit aligned structure +struct ENERGY { + float voltage[3]; // 12 bytes (3 * 4) + float current[3]; // 12 bytes (3 * 4) + float active_power[3]; // 12 bytes (3 * 4) + float apparent_power[3]; // 12 bytes (3 * 4) + float reactive_power[3]; // 12 bytes (3 * 4) + float power_factor[3]; // 12 bytes (3 * 4) + float frequency[3]; // 12 bytes (3 * 4) + + uint32_t kWhtoday_delta; // 4 bytes + uint32_t kWhtoday_offset; // 4 bytes + uint32_t kWhtoday; // 4 bytes + uint32_t kWhtotal; // 4 bytes + + uint16_t mplh_counter; // 2 bytes + uint16_t mplw_counter; // 2 bytes + + uint8_t fifth_second; // 1 byte + uint8_t command_code; // 1 byte + uint8_t data_valid[3]; // 3 bytes + uint8_t phase_count; // 1 byte + // Total: 96 bytes (32-bit aligned) +}; +``` + +--- +## Memory Management and Optimization + +### ESP8266 Memory Constraints + +The ESP8266 presents significant memory challenges that drive many architectural decisions: + +#### Memory Layout +``` +ESP8266 Memory Map: +┌─────────────────────────────────────┐ +│ Flash Memory (1MB-4MB) │ +├─────────────────────────────────────┤ +│ Program Code (~400-600KB) │ +├─────────────────────────────────────┤ +│ PROGMEM Constants (~100-200KB) │ +├─────────────────────────────────────┤ +│ Settings/Config (~16KB) │ +├─────────────────────────────────────┤ +│ File System (Optional, ~64-256KB) │ +└─────────────────────────────────────┘ + +RAM Memory (80KB total): +┌─────────────────────────────────────┐ +│ System/WiFi Stack (~35KB) │ +├─────────────────────────────────────┤ +│ Arduino Framework (~15KB) │ +├─────────────────────────────────────┤ +│ Application Heap (~25-30KB) │ +└─────────────────────────────────────┘ +``` + +#### Memory Optimization Strategies + +1. **PROGMEM Usage**: Store constants in flash memory +```c +// Strings stored in flash, not RAM +const char kWifiConfig[] PROGMEM = "WiFi configuration"; +const char* const kCommands[] PROGMEM = { + PSTR("Power"), PSTR("Status"), PSTR("Reset") +}; + +// Access via special functions +char buffer[32]; +strcpy_P(buffer, kWifiConfig); +``` + +2. **String Concatenation**: Reduce string overhead +```c +// Instead of separate strings, use concatenated format +const char kSensorTypes[] PROGMEM = "None|DHT11|DHT22|DS18B20|BME280"; + +// Extract individual strings +char sensor_name[16]; +GetTextIndexed(sensor_name, sizeof(sensor_name), sensor_type, kSensorTypes); +``` + +3. **Bitfield Packing**: Minimize boolean storage +```c +// Instead of 32 separate bool variables (32 bytes) +struct { + bool option1, option2, option3, ..., option32; +}; + +// Use bitfield (4 bytes) +union { + uint32_t data; + struct { + uint32_t option1 : 1; + uint32_t option2 : 1; + // ... up to 32 bits + }; +} options; +``` + +4. **Stack Usage Minimization** +```c +// Avoid large local arrays +void BadFunction() { + char large_buffer[1024]; // Consumes precious stack space + // ... function code +} + +// Use heap allocation or global buffers +void GoodFunction() { + char* buffer = (char*)malloc(1024); + if (buffer) { + // ... function code + free(buffer); + } +} +``` + +### Dynamic Memory Management + +#### Heap Monitoring +```c +uint32_t ESP_getFreeHeap(void) { +#ifdef ESP8266 + return ESP.getFreeHeap(); +#endif +#ifdef ESP32 + return ESP.getFreeHeap(); +#endif +} + +uint32_t ESP_getMaxAllocHeap(void) { +#ifdef ESP8266 + return ESP.getMaxFreeBlockSize(); +#endif +#ifdef ESP32 + return ESP.getMaxAllocHeap(); +#endif +} + +// Memory monitoring in main loop +void MemoryMonitor(void) { + static uint32_t last_heap_check = 0; + + if (TimeReached(last_heap_check)) { + SetNextTimeInterval(last_heap_check, 30000); // Check every 30 seconds + + uint32_t free_heap = ESP_getFreeHeap(); + if (free_heap < 8192) { // Less than 8KB free + AddLog(LOG_LEVEL_WARNING, PSTR("Low memory: %d bytes"), free_heap); + } + } +} +``` + +#### Buffer Management +```c +// Global buffers to avoid repeated allocation +struct TASMOTA_BUFFERS { + char mqtt_data[MESSZ]; // 1040 bytes - MQTT messages + char log_data[LOGSZ]; // 520 bytes - Log messages + char web_log[WEB_LOG_SIZE]; // 4000 bytes - Web interface log + char command_buffer[INPUT_BUFFER_SIZE]; // 520 bytes - Command processing + char response_buffer[TOPSZ]; // 151 bytes - JSON responses +} TasmotaBuffers; + +// Buffer reuse for temporary operations +char* GetTempBuffer(void) { + // Reuse command buffer when not processing commands + return TasmotaBuffers.command_buffer; +} +``` + +### ESP32 Memory Advantages + +The ESP32 provides significantly more memory, enabling advanced features: + +#### Memory Comparison +| Resource | ESP8266 | ESP32 | ESP32-S3 | +|----------|---------|-------|----------| +| Flash | 1-4MB | 4-16MB | 8-32MB | +| RAM | 80KB | 320KB | 512KB | +| PSRAM | None | Optional 4-8MB | Optional 8-32MB | +| Stack | 4KB | 8KB+ | 8KB+ | + +#### ESP32-Specific Optimizations +```c +#ifdef ESP32 +// Use larger buffers on ESP32 +#define MESSZ_ESP32 2048 +#define WEB_LOG_SIZE_ESP32 8000 + +// PSRAM utilization +void* ps_malloc(size_t size) { + if (psramFound()) { + return heap_caps_malloc(size, MALLOC_CAP_SPIRAM); + } + return malloc(size); +} + +// Multi-core task distribution +void CoreTaskCreate(void) { + xTaskCreatePinnedToCore( + NetworkTask, // Task function + "NetworkTask", // Task name + 4096, // Stack size + NULL, // Parameters + 1, // Priority + NULL, // Task handle + 0 // Core ID (0 or 1) + ); +} +#endif +``` + +--- + +## Security and Safety Features + +### Crash Recovery System (`support_crash_recorder.ino`) + +Tasmota implements comprehensive crash detection and recovery mechanisms: + +#### Crash Detection +```c +struct CRASH_RECORDER { + uint32_t magic; // Validation magic number + uint32_t crash_counter; // Number of crashes + uint32_t crash_time; // Last crash timestamp + uint32_t crash_restart; // Restart reason + char crash_dump[CRASH_DUMP_SIZE]; // Stack trace +} CrashRecorder; + +void CrashRecorderInit(void) { + if (CRASH_RECORDER_MAGIC != CrashRecorder.magic) { + memset(&CrashRecorder, 0, sizeof(CrashRecorder)); + CrashRecorder.magic = CRASH_RECORDER_MAGIC; + } + + // Check for crash on startup + if (ResetReason() == REASON_EXCEPTION_RST) { + CrashRecorder.crash_counter++; + CrashRecorder.crash_time = UtcTime(); + CrashDumpSave(); + } +} +``` + +#### Stack Trace Capture +```c +void CrashDumpSave(void) { + // Capture stack trace for debugging + uint32_t* stack_ptr = (uint32_t*)0x3FFFFC00; // Stack base + uint32_t stack_size = 0x400; // 1KB stack dump + + char* dump_ptr = CrashRecorder.crash_dump; + for (uint32_t i = 0; i < stack_size / 4; i++) { + snprintf(dump_ptr, 16, "%08X ", stack_ptr[i]); + dump_ptr += 9; + } + + CrashRecorderSave(); +} +``` + +### Watchdog Safety Systems + +#### Multi-Level Watchdog Protection +```c +// Level 1: Software watchdog (main loop monitoring) +void SoftwareWatchdog(void) { + static uint32_t last_loop_time = 0; + uint32_t current_time = millis(); + + if ((current_time - last_loop_time) > 30000) { // 30 second timeout + AddLog(LOG_LEVEL_ERROR, PSTR("Software watchdog timeout")); + ESP.restart(); + } + last_loop_time = current_time; +} + +// Level 2: Hardware watchdog (ESP32 built-in) +#ifdef ESP32 +void HardwareWatchdogFeed(void) { + esp_task_wdt_reset(); // Reset hardware watchdog +} +#endif + +// Level 3: External watchdog (optional hardware) +void ExternalWatchdogToggle(void) { + static bool watchdog_state = false; + if (Pin(GPIO_WATCHDOG) < 99) { + digitalWrite(Pin(GPIO_WATCHDOG), watchdog_state); + watchdog_state = !watchdog_state; + } +} +``` + +### Input Validation and Sanitization + +#### Command Parameter Validation +```c +bool ValidateNumericRange(int value, int min_val, int max_val) { + return (value >= min_val) && (value <= max_val); +} + +bool ValidateStringLength(const char* str, uint32_t max_len) { + return (str != nullptr) && (strlen(str) <= max_len); +} + +bool ValidateIPAddress(const char* ip_str) { + IPAddress ip; + return ip.fromString(ip_str) && (ip != IPAddress(0, 0, 0, 0)); +} + +// Command parameter validation example +void CmndTeleperiod(void) { + if (XdrvMailbox.data_len > 0) { + if (ValidateNumericRange(XdrvMailbox.payload, 10, 3600)) { + Settings->tele_period = XdrvMailbox.payload; + } else { + ResponseCmndError(); + return; + } + } + ResponseCmndNumber(Settings->tele_period); +} +``` + +#### Buffer Overflow Protection +```c +// Safe string operations +void SafeStringCopy(char* dest, const char* src, size_t dest_size) { + if (dest && src && dest_size > 0) { + strncpy(dest, src, dest_size - 1); + dest[dest_size - 1] = '\0'; + } +} + +// Safe JSON parsing +bool SafeJsonParse(const char* json_str, size_t max_len) { + if (!json_str || strlen(json_str) > max_len) { + return false; + } + + // Additional JSON validation + int brace_count = 0; + for (const char* p = json_str; *p; p++) { + if (*p == '{') brace_count++; + else if (*p == '}') brace_count--; + if (brace_count < 0) return false; // Malformed JSON + } + + return (brace_count == 0); +} +``` + +### Network Security + +#### MQTT Security Features +```c +// TLS/SSL support for MQTT (ESP32) +#ifdef ESP32 +bool MqttConnectTLS(void) { + if (Settings->flag4.mqtt_tls) { + WiFiClientSecure* client = new WiFiClientSecure; + if (Settings->flag4.mqtt_no_cert_verify) { + client->setInsecure(); // Skip certificate verification + } else { + client->setCACert(ca_cert); // Use CA certificate + } + MqttClient.setClient(*client); + } + return MqttClient.connect(mqtt_client, mqtt_user, mqtt_password); +} +#endif + +// MQTT topic validation +bool ValidateMqttTopic(const char* topic) { + if (!topic || strlen(topic) == 0) return false; + + // Check for invalid characters + const char* invalid_chars = "+#\0"; + for (const char* p = topic; *p; p++) { + if (strchr(invalid_chars, *p)) return false; + } + + return true; +} +``` + +#### Web Interface Security +```c +// Basic authentication for web interface +bool WebAuthenticate(void) { + if (strlen(SettingsText(SET_WEBPWD)) > 0) { + if (!WebServer->authenticate("admin", SettingsText(SET_WEBPWD))) { + WebServer->requestAuthentication(); + return false; + } + } + return true; +} + +// CORS header management +void WebSetCorsHeaders(void) { + if (strlen(SettingsText(SET_CORS)) > 0) { + WebServer->sendHeader("Access-Control-Allow-Origin", SettingsText(SET_CORS)); + WebServer->sendHeader("Access-Control-Allow-Methods", "GET, POST"); + WebServer->sendHeader("Access-Control-Allow-Headers", "Content-Type"); + } +} +``` + +--- + +## Performance Analysis + +### Loop Performance Monitoring (`support_profiling.ino`) + +Tasmota includes sophisticated performance monitoring capabilities: + +#### Loop Time Measurement +```c +struct PROFILING { + uint32_t loop_start_time; // Loop start timestamp + uint32_t loop_load_avg; // Average loop time + uint32_t loop_load_max; // Maximum loop time + uint32_t function_calls[FUNC_MAX]; // Per-function call counts + uint32_t function_time[FUNC_MAX]; // Per-function execution time +} Profiling; + +void ProfilingStart(void) { + Profiling.loop_start_time = micros(); +} + +void ProfilingEnd(void) { + uint32_t loop_time = micros() - Profiling.loop_start_time; + + // Update running average + Profiling.loop_load_avg = (Profiling.loop_load_avg * 15 + loop_time) / 16; + + // Track maximum + if (loop_time > Profiling.loop_load_max) { + Profiling.loop_load_max = loop_time; + } + + // Log performance warnings + if (loop_time > 50000) { // >50ms loop time + AddLog(LOG_LEVEL_DEBUG, PSTR("Long loop: %d µs"), loop_time); + } +} +``` + +#### Function-Level Profiling +```c +#define PROFILE_FUNCTION_START(func_id) \ + uint32_t profile_start = micros(); \ + Profiling.function_calls[func_id]++; + +#define PROFILE_FUNCTION_END(func_id) \ + Profiling.function_time[func_id] += (micros() - profile_start); + +// Usage example +void SensorRead(void) { + PROFILE_FUNCTION_START(FUNC_SENSOR_READ); + + // Sensor reading code + float temperature = ReadTemperature(); + float humidity = ReadHumidity(); + + PROFILE_FUNCTION_END(FUNC_SENSOR_READ); +} +``` + +### Memory Usage Statistics (`support_statistics.ino`) + +#### Heap Fragmentation Analysis +```c +struct MEMORY_STATS { + uint32_t heap_free; // Current free heap + uint32_t heap_min; // Minimum free heap seen + uint32_t heap_max; // Maximum free heap seen + uint32_t fragmentation; // Heap fragmentation percentage + uint32_t allocations; // Total allocations + uint32_t deallocations; // Total deallocations +} MemoryStats; + +void UpdateMemoryStats(void) { + uint32_t free_heap = ESP_getFreeHeap(); + uint32_t max_block = ESP_getMaxAllocHeap(); + + MemoryStats.heap_free = free_heap; + + if (free_heap < MemoryStats.heap_min) { + MemoryStats.heap_min = free_heap; + } + + if (free_heap > MemoryStats.heap_max) { + MemoryStats.heap_max = free_heap; + } + + // Calculate fragmentation percentage + if (free_heap > 0) { + MemoryStats.fragmentation = 100 - (max_block * 100 / free_heap); + } +} +``` + +### Network Performance Optimization + +#### WiFi Signal Quality Monitoring +```c +void WifiPerformanceMonitor(void) { + static uint32_t last_check = 0; + + if (TimeReached(last_check)) { + SetNextTimeInterval(last_check, 60000); // Check every minute + + int32_t rssi = WiFi.RSSI(); + uint8_t quality = WifiGetRssiAsQuality(rssi); + + // Log poor signal quality + if (quality < 25) { + AddLog(LOG_LEVEL_WARNING, PSTR("Poor WiFi signal: %d%% (%d dBm)"), + quality, rssi); + } + + // Trigger rescan if signal is very poor + if (quality < 10 && Settings->flag3.use_wifi_rescan) { + WifiConfig(WIFI_RETRY); + } + } +} +``` + +#### MQTT Performance Optimization +```c +// Message queuing for high-frequency updates +struct MQTT_QUEUE { + char topic[64]; + char payload[256]; + bool retain; + uint32_t timestamp; +} mqtt_queue[MQTT_QUEUE_SIZE]; + +void MqttQueueMessage(const char* topic, const char* payload, bool retain) { + static uint8_t queue_index = 0; + + // Add to queue + strlcpy(mqtt_queue[queue_index].topic, topic, sizeof(mqtt_queue[0].topic)); + strlcpy(mqtt_queue[queue_index].payload, payload, sizeof(mqtt_queue[0].payload)); + mqtt_queue[queue_index].retain = retain; + mqtt_queue[queue_index].timestamp = millis(); + + queue_index = (queue_index + 1) % MQTT_QUEUE_SIZE; +} + +void MqttProcessQueue(void) { + static uint8_t process_index = 0; + + if (MqttIsConnected() && mqtt_queue[process_index].timestamp > 0) { + MqttPublish(mqtt_queue[process_index].topic, + mqtt_queue[process_index].payload, + mqtt_queue[process_index].retain); + + mqtt_queue[process_index].timestamp = 0; // Mark as processed + process_index = (process_index + 1) % MQTT_QUEUE_SIZE; + } +} +``` + +--- + +## Development Guidelines + +### Code Organization Best Practices + +#### File Naming Conventions +``` +Support Files: +- support.ino - Core system functions +- support_*.ino - Specific subsystem support +- settings.ino - Configuration management + +Driver Files: +- xdrv_##_name.ino - Driver modules (##: 01-99) +- xsns_##_name.ino - Sensor modules (##: 01-99) +- xlgt_##_name.ino - Light driver modules +- xnrg_##_name.ino - Energy monitoring modules + +Include Files: +- tasmota.h - Main header +- tasmota_types.h - Type definitions +- tasmota_globals.h - Global variables +- tasmota_template.h - Device templates +``` + +#### Function Naming Standards +```c +// Public API functions - CamelCase +void ButtonInit(void); +bool WifiConnect(void); +uint32_t GetUptime(void); + +// Internal functions - lowercase with underscores +static void button_handler(void); +static bool wifi_scan_networks(void); +static uint32_t calculate_checksum(void); + +// Command handlers - CmndXxxxx +void CmndPower(void); +void CmndStatus(void); +void CmndRestart(void); + +// Callback functions - XxxxCallback +bool ButtonCallback(uint8_t function); +bool SensorCallback(uint8_t function); +``` + +#### Memory Management Guidelines + +1. **Minimize Dynamic Allocation** +```c +// Avoid frequent malloc/free +char* buffer = (char*)malloc(1024); // Bad in main loop +free(buffer); + +// Use static buffers or global pools +static char static_buffer[1024]; // Good for temporary use +``` + +2. **Use PROGMEM for Constants** +```c +// Store strings in flash memory +const char kErrorMessage[] PROGMEM = "Configuration error"; +const uint8_t kDefaultValues[] PROGMEM = {1, 2, 3, 4, 5}; + +// Access with special functions +char buffer[32]; +strcpy_P(buffer, kErrorMessage); +``` + +3. **Optimize Structure Packing** +```c +// Bad - wastes memory due to alignment +struct BadStruct { + uint8_t flag; // 1 byte + 3 padding + uint32_t value; // 4 bytes + uint8_t status; // 1 byte + 3 padding +}; // Total: 12 bytes + +// Good - optimized alignment +struct GoodStruct { + uint32_t value; // 4 bytes + uint8_t flag; // 1 byte + uint8_t status; // 1 byte + uint16_t padding; // 2 bytes (explicit) +}; // Total: 8 bytes +``` + +### Driver Development Framework + +#### Standard Driver Template +```c +/* + xdrv_##_mydriver.ino - My custom driver for Tasmota + + Copyright (C) 2021 Your Name + + 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. +*/ + +#ifdef USE_MY_DRIVER + +#define XDRV_## ## // Unique driver ID + +// Driver-specific constants +const char kMyDriverCommands[] PROGMEM = "|" + "MyCmd1|MyCmd2|MyCmd3"; + +void (* const MyDriverCommand[])(void) PROGMEM = { + &CmndMyCmd1, &CmndMyCmd2, &CmndMyCmd3 +}; + +// Driver initialization +void MyDriverInit(void) { + // Initialize hardware + // Set up GPIO pins + // Configure peripherals +} + +// Main driver callback +bool Xdrv##(uint8_t function) { + bool result = false; + + switch (function) { + case FUNC_INIT: + MyDriverInit(); + break; + case FUNC_EVERY_SECOND: + MyDriverEverySecond(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kMyDriverCommands, MyDriverCommand); + break; + case FUNC_JSON_APPEND: + MyDriverShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MyDriverShow(0); + break; +#endif // USE_WEBSERVER + } + return result; +} + +#endif // USE_MY_DRIVER +``` + +#### Sensor Driver Template +```c +#ifdef USE_MY_SENSOR + +#define XSNS_## ## // Unique sensor ID + +bool MySensorDetected = false; +uint8_t MySensorAddress = 0; + +void MySensorDetect(void) { + if (MySensorDetected) return; + + for (uint32_t i = 0; i < SENSOR_MAX_ADDR; i++) { + uint8_t addr = SENSOR_BASE_ADDR + i; + if (I2cActive(addr)) continue; + + if (I2cValidRead8(&sensor_id, addr, SENSOR_ID_REG)) { + if (sensor_id == EXPECTED_SENSOR_ID) { + I2cSetActiveFound(addr, "MySensor"); + MySensorDetected = true; + MySensorAddress = addr; + break; + } + } + } +} + +void MySensorEverySecond(void) { + if (!MySensorDetected) return; + + // Read sensor data + float temperature, humidity; + if (MySensorRead(&temperature, &humidity)) { + // Process readings + } +} + +void MySensorShow(bool json) { + if (!MySensorDetected) return; + + if (json) { + ResponseAppend_P(PSTR(",\"MySensor\":{\"Temperature\":%1_f,\"Humidity\":%1_f}"), + &temperature, &humidity); + } +#ifdef USE_WEBSERVER + else { + WSContentSend_PD(HTTP_SNS_TEMP, "MySensor", temperature); + WSContentSend_PD(HTTP_SNS_HUM, "MySensor", humidity); + } +#endif // USE_WEBSERVER +} + +bool Xsns##(uint8_t function) { + bool result = false; + + if (FUNC_INIT == function) { + MySensorDetect(); + } + else if (MySensorDetected) { + switch (function) { + case FUNC_EVERY_SECOND: + MySensorEverySecond(); + break; + case FUNC_JSON_APPEND: + MySensorShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + MySensorShow(0); + break; +#endif // USE_WEBSERVER + } + } + return result; +} + +#endif // USE_MY_SENSOR +``` + +### Testing and Debugging + +#### Debug Logging Levels +```c +// Log levels in order of verbosity +#define LOG_LEVEL_NONE 0 // No logging +#define LOG_LEVEL_ERROR 1 // Critical errors only +#define LOG_LEVEL_INFO 2 // Errors and important info +#define LOG_LEVEL_DEBUG 3 // Detailed debugging info +#define LOG_LEVEL_DEBUG_MORE 4 // Verbose debugging + +// Usage examples +AddLog(LOG_LEVEL_ERROR, PSTR("Critical error: %s"), error_msg); +AddLog(LOG_LEVEL_INFO, PSTR("Sensor initialized: %s"), sensor_name); +AddLog(LOG_LEVEL_DEBUG, PSTR("Reading value: %d"), sensor_value); +``` + +#### Memory Debugging +```c +#ifdef DEBUG_TASMOTA_CORE +void DebugMemoryInfo(const char* location) { + AddLog(LOG_LEVEL_DEBUG, PSTR("MEM: %s - Free: %d, Max: %d, Frag: %d%%"), + location, ESP_getFreeHeap(), ESP_getMaxAllocHeap(), + ESP_getHeapFragmentation()); +} +#else +#define DebugMemoryInfo(x) +#endif +``` + +This comprehensive analysis provides a deep understanding of Tasmota's support infrastructure, covering all major subsystems from core functionality to development guidelines. The modular architecture, memory optimization techniques, and extensive safety features make Tasmota a robust platform for IoT device development. + +--- + +## Conclusion + +Tasmota's support infrastructure represents a sophisticated embedded systems architecture that successfully balances functionality, performance, and resource constraints. The system's key strengths include: + +1. **Modular Design**: Clean separation of concerns with well-defined interfaces +2. **Memory Efficiency**: Careful optimization for ESP8266's limited resources +3. **Extensibility**: Plugin architecture supporting hundreds of devices and sensors +4. **Reliability**: Comprehensive watchdog systems and crash recovery +5. **Internationalization**: Support for 28 languages with efficient storage +6. **Developer-Friendly**: Clear patterns and extensive documentation + +The analysis reveals a mature codebase with consistent patterns, robust error handling, and thoughtful architectural decisions that enable Tasmota to run effectively on resource-constrained microcontrollers while providing rich functionality for IoT applications.