/* █████ █████ ██████████ █████████ ███████████ ██████████ ███████████ ▒▒███ ▒▒███ ▒▒███▒▒▒▒▒█ ███▒▒▒▒▒███▒▒███▒▒▒▒▒███▒▒███▒▒▒▒▒█▒▒███▒▒▒▒▒███ ▒███ ▒███ ▒███ █ ▒ ▒███ ▒▒▒ ▒███ ▒███ ▒███ █ ▒ ▒███ ▒███ ▒███ ▒███ ▒██████ ▒▒█████████ ▒██████████ ▒██████ ▒██████████ ▒▒███ ███ ▒███▒▒█ ▒▒▒▒▒▒▒▒███ ▒███▒▒▒▒▒▒ ▒███▒▒█ ▒███▒▒▒▒▒███ ▒▒▒█████▒ ▒███ ▒ █ ███ ▒███ ▒███ ▒███ ▒ █ ▒███ ▒███ ▒▒███ ██████████▒▒█████████ █████ ██████████ █████ █████ ▒▒▒ ▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ * ═══════════════════════════════════════════════════════════════════════════════════ * Project VESPER - BELL AUTOMATION SYSTEM - Main Firmware Entry Point * ═══════════════════════════════════════════════════════════════════════════════════ * * 🔔 DESCRIPTION: * High-precision automated bell control system with multi-protocol communication, * real-time telemetry, OTA updates, and modular hardware abstraction. * * 🏗️ ARCHITECTURE: * Clean modular design with dependency injection and proper separation of concerns. * Each major system is encapsulated in its own class with well-defined interfaces. * * 🎯 KEY FEATURES: * ✅ Microsecond-precision bell timing (BellEngine) * ✅ Multi-hardware support (PCF8574, GPIO, Mock) * ✅ Dual network connectivity (Ethernet + WiFi) * ✅ Dual Communication Support (MQTT + WebSocket) * ✅ Real-time telemetry and load monitoring * ✅ Over-the-air firmware updates * ✅ SD card configuration and file management * ✅ NTP time synchronization * ✅ Comprehensive logging system * * 📡 COMMUNICATION PROTOCOLS: * • MQTT (Primary control interface) * • WebSocket (Real-time web interface) * • UDP Discovery (Auto-discovery service) * • HTTP/HTTPS (OTA updates) * * 🔧 HARDWARE ABSTRACTION: * OutputManager provides clean interface for different relay systems: * - PCF8574OutputManager: I2C GPIO expander (8 outputs, 6 on Kincony A6 Board) * - GPIOOutputManager: Direct ESP32 pins (for DIY projects) * - MockOutputManager: Testing without hardware * * ⚡ PERFORMANCE: * High-priority FreeRTOS tasks ensure microsecond timing precision. * Core 1 dedicated to BellEngine for maximum performance. * * 📋 VERSION: 1.1 * 📅 DATE: 2025-09-08 * 👨‍💻 AUTHOR: Advanced Bell Systems * ═══════════════════════════════════════════════════════════════════════════════════ */ // ═══════════════════════════════════════════════════════════════════════════════════ // SYSTEM LIBRARIES - Core ESP32 and Arduino functionality // ═══════════════════════════════════════════════════════════════════════════════════ #include // SD card file system operations #include // File system base class #include // Ethernet connectivity (W5500 support) #include // SPI communication protocol #include // Arduino core framework #include // WiFi connectivity management #include // HTTP client for OTA updates #include // Firmware update utilities #include // I2C communication protocol #include // Task watchdog timer // ═══════════════════════════════════════════════════════════════════════════════════ // NETWORKING LIBRARIES - Advanced networking and communication // ═══════════════════════════════════════════════════════════════════════════════════ #include // High-performance async MQTT client #include // WiFi configuration portal #include // Async web server for WebSocket support #include // UDP for discovery service // ═══════════════════════════════════════════════════════════════════════════════════ // DATA PROCESSING LIBRARIES - JSON parsing and data structures // ═══════════════════════════════════════════════════════════════════════════════════ #include // Efficient JSON processing #include // STL string support // ═══════════════════════════════════════════════════════════════════════════════════ // HARDWARE LIBRARIES - Peripheral device control // ═══════════════════════════════════════════════════════════════════════════════════ #include // I2C GPIO expander for relay control #include // Real-time clock functionality // ═══════════════════════════════════════════════════════════════════════════════════ // CUSTOM CLASSES - Include Custom Classes and Functions // ═══════════════════════════════════════════════════════════════════════════════════ #include "src/ConfigManager/ConfigManager.hpp" #include "src/FileManager/FileManager.hpp" #include "src/TimeKeeper/TimeKeeper.hpp" #include "src/Logging/Logging.hpp" #include "src/Telemetry/Telemetry.hpp" #include "src/OTAManager/OTAManager.hpp" #include "src/Networking/Networking.hpp" #include "src/Communication/Communication.hpp" #include "src/ClientManager/ClientManager.hpp" #include "src/Communication/ResponseBuilder.hpp" #include "src/Player/Player.hpp" #include "src/BellEngine/BellEngine.hpp" #include "src/OutputManager/OutputManager.hpp" #include "src/HealthMonitor/HealthMonitor.hpp" #include "src/FirmwareValidator/FirmwareValidator.hpp" #include "src/InputManager/InputManager.hpp" #include "src/MqttSSL/MqttSSL.hpp" // Class Constructors ConfigManager configManager; FileManager fileManager(&configManager); Timekeeper timekeeper; Telemetry telemetry; OTAManager otaManager(configManager); AsyncMqttClient mqttClient; Player player; AsyncWebServer server(80); AsyncWebSocket ws("/ws"); AsyncUDP udp; Networking networking(configManager); Communication communication(configManager, otaManager, networking, mqttClient, server, ws, udp); HealthMonitor healthMonitor; FirmwareValidator firmwareValidator; InputManager inputManager; // 🔥 OUTPUT SYSTEM - PCF8574/PCF8575 I2C Expanders Configuration // Choose one of the following configurations (with active output counts): // Option 1: Single PCF8574 (6 active outputs out of 8 max) PCF8574OutputManager outputManager(0x24, ChipType::PCF8574, 6); // Option 2: Single PCF8575 (8 active outputs out of 16 max) //PCF8574OutputManager outputManager(0x24, ChipType::PCF8575, 8); // Option 3: PCF8574 + PCF8575 (6 + 8 = 14 total virtual outputs) //PCF8574OutputManager outputManager(0x24, ChipType::PCF8574, 6, 0x21, ChipType::PCF8575, 8); // Option 4: Dual PCF8575 (8 + 8 = 16 total virtual outputs) //PCF8574OutputManager outputManager(0x24, ChipType::PCF8575, 8, 0x21, ChipType::PCF8575, 8); // Virtual Output Mapping Examples: // Option 1: Virtual outputs 0-5 → PCF8574[0x20] pins 0-5 // Option 3: Virtual outputs 0-5 → PCF8574[0x20] pins 0-5, Virtual outputs 6-13 → PCF8575[0x21] pins 0-7 // Option 4: Virtual outputs 0-7 → PCF8575[0x20] pins 0-7, Virtual outputs 8-15 → PCF8575[0x21] pins 0-7 // Legacy backward-compatible (defaults to 8 active outputs): //PCF8574OutputManager outputManager(0x20, ChipType::PCF8574); // 8/8 active outputs BellEngine bellEngine(player, configManager, telemetry, outputManager); // 🔥 THE ULTIMATE BEAST! TaskHandle_t bellEngineHandle = NULL; // Legacy - will be removed TimerHandle_t schedulerTimer; void handleFactoryReset() { if (configManager.factoryReset()) { delay(3000); ESP.restart(); } } void setup() { // Initialize Serial Communications (for debugging) & I2C Bus (for Hardware Control) Serial.begin(115200); Serial.println("Hello, VESPER System Initialized! - PontikoTest"); Wire.begin(4,15); auto& hwConfig = configManager.getHardwareConfig(); SPI.begin(hwConfig.ethSpiSck, hwConfig.ethSpiMiso, hwConfig.ethSpiMosi); delay(50); // Initialize Configuration (this loads device identity from SD card) configManager.begin(); inputManager.begin(); inputManager.setFactoryResetLongPressCallback(handleFactoryReset); // Set factory values: configManager.setDeviceUID("PV202508190002"); configManager.setHwType("BellPlus"); configManager.setHwVersion("1.0"); configManager.setFwVersion("1.1"); LOG_INFO("Device identity initialized"); // Display device information after configuration is loaded Serial.println("\n=== DEVICE IDENTITY ==="); Serial.printf("Device UID: %s\n", configManager.getDeviceUID().c_str()); Serial.printf("Hardware Type: %s\n", configManager.getHwType().c_str()); Serial.printf("Hardware Version: %s\n", configManager.getHwVersion().c_str()); Serial.printf("Firmware Version: %s\n", configManager.getFwVersion().c_str()); Serial.printf("AP SSID: %s\n", configManager.getAPSSID().c_str()); Serial.println("=====================\n"); // 🔥 CRITICAL: Initialize Health Monitor FIRST (required for firmware validation) healthMonitor.begin(); // Register all subsystems with health monitor for continuous monitoring healthMonitor.setConfigManager(&configManager); healthMonitor.setFileManager(&fileManager); // Initialize Output Manager - 🔥 THE NEW WAY! outputManager.setConfigManager(&configManager); if (!outputManager.initialize()) { LOG_ERROR("Failed to initialize OutputManager!"); // Continue anyway for now } // Register OutputManager with health monitor healthMonitor.setOutputManager(&outputManager); // Initialize BellEngine early for health validation bellEngine.begin(); healthMonitor.setBellEngine(&bellEngine); delay(100); // 🔥 BULLETPROOF: Initialize Firmware Validator and perform startup validation firmwareValidator.begin(&healthMonitor, &configManager); delay(100); // 💀 CRITICAL SAFETY CHECK: Perform startup validation // This MUST happen early before initializing other subsystems if (!firmwareValidator.performStartupValidation()) { // If we reach here, startup validation failed and rollback was triggered // The system should reboot automatically to the previous firmware LOG_ERROR("💀 STARTUP VALIDATION FAILED - SYSTEM HALTED"); while(1) { delay(1000); } // Should not reach here } LOG_INFO("✅ Firmware startup validation PASSED - proceeding with initialization"); // Initialize remaining subsystems... // SD Card initialization is now handled by ConfigManager // Initialize timekeeper with NO clock outputs timekeeper.begin(); // No parameters needed // Connect the timekeeper to dependencies (CLEAN!) timekeeper.setOutputManager(&outputManager); timekeeper.setConfigManager(&configManager); timekeeper.setNetworking(&networking); // Clock outputs now configured via ConfigManager/Communication commands // Register TimeKeeper with health monitor healthMonitor.setTimeKeeper(&timekeeper); // Initialize Telemetry telemetry.begin(); telemetry.setPlayerReference(&player.isPlaying); // 🚑 CRITICAL: Connect force stop callback for overload protection! telemetry.setForceStopCallback([]() { player.forceStop(); }); // Register Telemetry with health monitor healthMonitor.setTelemetry(&telemetry); // Initialize Networking (handles everything automatically) networking.begin(); // Register Networking with health monitor healthMonitor.setNetworking(&networking); // Initialize Player player.begin(); // Register Player with health monitor healthMonitor.setPlayer(&player); // BellEngine already initialized and registered earlier for health validation // Initialize Communication Manager communication.begin(); communication.setPlayerReference(&player); communication.setFileManagerReference(&fileManager); communication.setTimeKeeperReference(&timekeeper); communication.setFirmwareValidatorReference(&firmwareValidator); player.setDependencies(&communication, &fileManager); player.setBellEngine(&bellEngine); // Connect the beast! // Register Communication with health monitor healthMonitor.setCommunication(&communication); // 🔔 CONNECT BELLENGINE TO COMMUNICATION FOR DING NOTIFICATIONS! bellEngine.setCommunicationManager(&communication); // Set up network callbacks networking.setNetworkCallbacks( []() { communication.onNetworkConnected(); }, // onConnected []() { communication.onNetworkDisconnected(); } // onDisconnected ); // If already connected, trigger MQTT connection manually if (networking.isConnected()) { LOG_INFO("Network already connected - triggering MQTT connection"); communication.onNetworkConnected(); } delay(500); // Initialize OTA Manager and check for updates otaManager.begin(); otaManager.setFileManager(&fileManager); // 🔥 CRITICAL: Delay OTA check to avoid UDP socket race with MQTT // Both MQTT and OTA HTTP use UDP sockets, must sequence them! delay(2000); LOG_INFO("Starting OTA update check after network stabilization..."); otaManager.checkForUpdates(); communication.setupUdpDiscovery(); // Register OTA Manager with health monitor healthMonitor.setOTAManager(&otaManager); // Start the server server.begin(); // 🔥 START RUNTIME VALIDATION: All subsystems are now initialized // Begin extended runtime validation if we're in testing mode if (firmwareValidator.isInTestingMode()) { LOG_INFO("🏃 Starting runtime validation - firmware will be tested for %lu seconds", firmwareValidator.getValidationConfig().runtimeTimeoutMs / 1000); firmwareValidator.startRuntimeValidation(); } else { LOG_INFO("✅ Firmware already validated - normal operation mode"); } // ═══════════════════════════════════════════════════════════════════════════════ // INITIALIZATION COMPLETE // ═══════════════════════════════════════════════════════════════════════════════ // ✅ All automatic task creation handled by individual components: // • BellEngine creates high-priority timing task on Core 1 // • Telemetry creates monitoring task for load tracking // • Player creates duration timer for playback control // • Communication creates MQTT reconnection timers // • Networking creates connection management timers // ✅ Bell configuration automatically loaded by ConfigManager // ✅ System ready for MQTT commands, WebSocket connections, and UDP discovery } // ███████████████████████████████████████████████████████████████████████████████████ // █ MAIN LOOP █ // ███████████████████████████████████████████████████████████████████████████████████ // The main loop is intentionally kept minimal in this architecture. All critical // functionality runs in dedicated FreeRTOS tasks for optimal performance and timing. // This ensures the main loop doesn't interfere with precision bell timing. /** * @brief Main execution loop - Minimal by design * * In the new modular architecture, all heavy lifting is done by dedicated tasks: * • BellEngine: High-priority task on Core 1 for microsecond timing * • Telemetry: Background monitoring task for system health * • Player: Timer-based duration control for melody playback * • Communication: Event-driven MQTT/WebSocket handling * • Networking: Automatic connection management * * The main loop only handles lightweight operations that don't require * precise timing or could benefit from running on Core 0. * * @note This loop runs on Core 0 and should remain lightweight to avoid * interfering with the precision timing on Core 1. */ void loop() { // ═══════════════════════════════════════════════════════════════════════════════ // INTENTIONALLY MINIMAL - ALL WORK DONE BY DEDICATED TASKS // ═══════════════════════════════════════════════════════════════════════════════ // // The loop() function is kept empty by design to ensure maximum // performance for the high-precision BellEngine running on Core 1. // // All system functionality is handled by dedicated FreeRTOS tasks: // • 🔥 BellEngine: Microsecond-precision timing (Core 1, Priority 6) // • 📊 Telemetry: System monitoring (Background task) // • 🎵 Player: Duration management (FreeRTOS timers) // • 📡 Communication: MQTT/WebSocket (Event-driven) // • 🌐 Networking: Connection management (Timer-based) // // If you need to add periodic functionality, consider creating a new // dedicated task instead of putting it here. // Uncomment the line below for debugging system status: // Serial.printf("Free heap: %d bytes\n", ESP.getFreeHeap()); // Feed watchdog only during firmware validation if (firmwareValidator.isInTestingMode()) { esp_task_wdt_reset(); } else { // Remove task from watchdog if validation completed static bool taskRemoved = false; if (!taskRemoved) { esp_task_wdt_delete(NULL); // Remove current task taskRemoved = true; } } // Keep the loop responsive but not busy delay(100); // ⏱️ 100ms delay to prevent busy waiting }