Complete Rebuild, with Subsystems for each component. RTOS Tasks. (help by Claude)

This commit is contained in:
2025-10-01 12:42:00 +03:00
parent 104c1d04d4
commit f696984cd1
57 changed files with 11757 additions and 2290 deletions

View File

@@ -1,140 +1,408 @@
#include "logging.hpp"
#include <SD.h>
#include <FS.h>
#include <ETH.h>
#include <SPI.h>
#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <Update.h>
#include <AsyncMqttClient.h>
#include <ArduinoJson.h>
#include <string>
#include <Wire.h>
#include <Adafruit_PCF8574.h>
#include <WebServer.h>
#include <ESPAsyncWebServer.h>
#include <WiFiManager.h>
#include <AsyncUDP.h>
#include <RTClib.h>
/*
// Custom Classes
█████ █████ ██████████ █████████ ███████████ ██████████ ███████████
▒▒███ ▒▒███ ▒▒███▒▒▒▒▒█ ███▒▒▒▒▒███▒▒███▒▒▒▒▒███▒▒███▒▒▒▒▒█▒▒███▒▒▒▒▒███
▒███ ▒███ ▒███ █ ▒ ▒███ ▒▒▒ ▒███ ▒███ ▒███ █ ▒ ▒███ ▒███
▒███ ▒███ ▒██████ ▒▒█████████ ▒██████████ ▒██████ ▒██████████
▒▒███ ███ ▒███▒▒█ ▒▒▒▒▒▒▒▒███ ▒███▒▒▒▒▒▒ ▒███▒▒█ ▒███▒▒▒▒▒███
▒▒▒█████▒ ▒███ ▒ █ ███ ▒███ ▒███ ▒███ ▒ █ ▒███ ▒███
▒▒███ ██████████▒▒█████████ █████ ██████████ █████ █████
▒▒▒ ▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒
#include "timekeeper.hpp"
* ═══════════════════════════════════════════════════════════════════════════════════
* 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
* ═══════════════════════════════════════════════════════════════════════════════════
*/
// Hardware Constructors:
Adafruit_PCF8574 relays;
// Wrapper function to connect timekeeper to your relays
void relayWrite(int relayIndex, int state) {
relays.digitalWrite(relayIndex, state);
}
// ═══════════════════════════════════════════════════════════════════════════════════
// SYSTEM LIBRARIES - Core ESP32 and Arduino functionality
// ═══════════════════════════════════════════════════════════════════════════════════
#include <SD.h> // SD card file system operations
#include <FS.h> // File system base class
#include <ETH.h> // Ethernet connectivity (W5500 support)
#include <SPI.h> // SPI communication protocol
#include <Arduino.h> // Arduino core framework
#include <WiFi.h> // WiFi connectivity management
#include <HTTPClient.h> // HTTP client for OTA updates
#include <Update.h> // Firmware update utilities
#include <Wire.h> // I2C communication protocol
#include <esp_task_wdt.h> // Task watchdog timer
// SD Card Chip Select:
#define SD_CS 5
// ═══════════════════════════════════════════════════════════════════════════════════
// NETWORKING LIBRARIES - Advanced networking and communication
// ═══════════════════════════════════════════════════════════════════════════════════
#include <AsyncMqttClient.h> // High-performance async MQTT client
#include <WiFiManager.h> // WiFi configuration portal
#include <ESPAsyncWebServer.h> // Async web server for WebSocket support
#include <AsyncUDP.h> // UDP for discovery service
// Include Classes
#include "class_player.hpp"
// ═══════════════════════════════════════════════════════════════════════════════════
// DATA PROCESSING LIBRARIES - JSON parsing and data structures
// ═══════════════════════════════════════════════════════════════════════════════════
#include <ArduinoJson.h> // Efficient JSON processing
#include <string> // STL string support
// ═══════════════════════════════════════════════════════════════════════════════════
// HARDWARE LIBRARIES - Peripheral device control
// ═══════════════════════════════════════════════════════════════════════════════════
#include <Adafruit_PCF8574.h> // I2C GPIO expander for relay control
#include <RTClib.h> // 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;
std::vector<uint16_t> melody_steps; // holds the steps of the melody. Should move into bell Engine.
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
AsyncWebSocketClient* activeClient = nullptr;
AsyncUDP udp;
constexpr uint16_t DISCOVERY_PORT = 32101;
uint32_t strikeCounters[16] = {0};
uint16_t bellLoad[16] = {0};
bool coolingActive = false;
Networking networking(configManager);
Communication communication(configManager, otaManager, networking, mqttClient, server, ws, udp);
HealthMonitor healthMonitor;
FirmwareValidator firmwareValidator;
InputManager inputManager;
#include "config.h"
#include "ota.hpp"
#include "functions.hpp"
#include "commands_handling.hpp"
#include "MQTT_Functions.hpp"
#include "MQTT_Connection_Handling.hpp"
#include "WebSocket_Functions.hpp"
#include "PlaybackControls.hpp"
#include "bellEngine.hpp"
#include "dataLogging.hpp"
// 🔥 OUTPUT SYSTEM - PCF8574/PCF8575 I2C Expanders Configuration
// Choose one of the following configurations (with active output counts):
TaskHandle_t bellEngineHandle = NULL;
// 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 & I2C Bus (for debugging)
// Initialize Serial Communications (for debugging) & I2C Bus (for Hardware Control)
Serial.begin(115200);
Serial.println("Hello, VESPER System Initialized! - PontikoTest");
Wire.begin(4,15);
SPI.begin(ETH_SPI_SCK, ETH_SPI_MISO, ETH_SPI_MOSI);
auto& hwConfig = configManager.getHardwareConfig();
SPI.begin(hwConfig.ethSpiSck, hwConfig.ethSpiMiso, hwConfig.ethSpiMosi);
delay(50);
// Initialize PCF8574 and Relays
relays.begin(PCF8574_ADDR, &Wire);
for (uint8_t p=0; p<6; p++){
relays.pinMode(p, OUTPUT);
relays.digitalWrite(p, HIGH);
// 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
}
// Initialize SD Card
if (!SD.begin(SD_CS)) {
Serial.println("SD card not found. Using defaults.");
} else {
// do nothing
}
// 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 your relay controller
timekeeper.setRelayWriteFunction(relayWrite);
timekeeper.setClockOutputs(5, 4);
// 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 and MQTT
Network.onEvent(NetworkEvent);
ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_CS, ETH_PHY_IRQ, ETH_PHY_RST, SPI);
InitMqtt();
WiFiManager wm;
//wm.resetSettings(); // Only for Debugging.
bool res;
res = wm.autoConnect(ap_ssid.c_str(),ap_pass.c_str());
if(!res) {
LOG_ERROR("Failed to connect to WiFi");
}
else {
LOG_INFO("Connected to WiFi");
// 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(100);
delay(500);
checkForUpdates(); // checks for updates online
setupUdpDiscovery();
// 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();
delay(100);
// Register OTA Manager with health monitor
healthMonitor.setOTAManager(&otaManager);
// WebSocket setup
ws.onEvent(onWebSocketEvent);
server.addHandler(&ws);
// Start the server
server.begin();
// Start the server
server.begin();
// Tasks and Timers
xTaskCreatePinnedToCore(bellEngine,"bellEngine", 8192, NULL, 1, &bellEngineHandle, 1);
xTaskCreatePinnedToCore(durationTimer, "durationTimer", 8192, NULL, 2, NULL, 1);
xTaskCreatePinnedToCore(relayControlTask, "Relay Control", 2048, NULL, 2, NULL, 1);
//xTaskCreatePinnedToCore(dataLogging, "dataLogging", 2048, NULL, 2, NULL, 1);
// 🔥 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");
}
loadRelayTimings();
// ═══════════════════════════════════════════════════════════════════════════════
// 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()
{
//Serial.printf("b1:%d - b2:%d - Bell 1 Load: %d \n",strikeCounters[0], strikeCounters[1], bellLoad[0]);
// ═══════════════════════════════════════════════════════════════════════════════
// 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
}