Added Telemetry data report to the App
This commit is contained in:
@@ -10,6 +10,7 @@
|
|||||||
#include "../../TimeKeeper/TimeKeeper.hpp"
|
#include "../../TimeKeeper/TimeKeeper.hpp"
|
||||||
#include "../../FirmwareValidator/FirmwareValidator.hpp"
|
#include "../../FirmwareValidator/FirmwareValidator.hpp"
|
||||||
#include "../../ClientManager/ClientManager.hpp"
|
#include "../../ClientManager/ClientManager.hpp"
|
||||||
|
#include "../../Telemetry/Telemetry.hpp"
|
||||||
#include "../../Logging/Logging.hpp"
|
#include "../../Logging/Logging.hpp"
|
||||||
#include "../ResponseBuilder/ResponseBuilder.hpp"
|
#include "../ResponseBuilder/ResponseBuilder.hpp"
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ CommandHandler::CommandHandler(ConfigManager& configManager, OTAManager& otaMana
|
|||||||
, _timeKeeper(nullptr)
|
, _timeKeeper(nullptr)
|
||||||
, _firmwareValidator(nullptr)
|
, _firmwareValidator(nullptr)
|
||||||
, _clientManager(nullptr)
|
, _clientManager(nullptr)
|
||||||
|
, _telemetry(nullptr)
|
||||||
, _responseCallback(nullptr) {}
|
, _responseCallback(nullptr) {}
|
||||||
|
|
||||||
CommandHandler::~CommandHandler() {}
|
CommandHandler::~CommandHandler() {}
|
||||||
@@ -45,6 +47,10 @@ void CommandHandler::setClientManagerReference(ClientManager* cm) {
|
|||||||
_clientManager = cm;
|
_clientManager = cm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandHandler::setTelemetryReference(Telemetry* telemetry) {
|
||||||
|
_telemetry = telemetry;
|
||||||
|
}
|
||||||
|
|
||||||
void CommandHandler::setResponseCallback(ResponseCallback callback) {
|
void CommandHandler::setResponseCallback(ResponseCallback callback) {
|
||||||
_responseCallback = callback;
|
_responseCallback = callback;
|
||||||
}
|
}
|
||||||
@@ -114,7 +120,15 @@ void CommandHandler::handleStatusCommand(const MessageContext& context) {
|
|||||||
projectedRunTime = _player->calculateProjectedRunTime();
|
projectedRunTime = _player->calculateProjectedRunTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
String response = ResponseBuilder::deviceStatus(playerStatus, timeElapsedMs, projectedRunTime);
|
// Collect strike counters from Telemetry
|
||||||
|
uint32_t strikeCounters[16] = {0};
|
||||||
|
if (_telemetry) {
|
||||||
|
for (uint8_t i = 0; i < 16; i++) {
|
||||||
|
strikeCounters[i] = _telemetry->getStrikeCount(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String response = ResponseBuilder::deviceStatus(playerStatus, timeElapsedMs, projectedRunTime, strikeCounters);
|
||||||
sendResponse(response, context);
|
sendResponse(response, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,10 +271,6 @@ void CommandHandler::handleSystemInfoCommand(JsonVariant contents, const Message
|
|||||||
handleGetDeviceTimeCommand(context);
|
handleGetDeviceTimeCommand(context);
|
||||||
} else if (action == "get_clock_time") {
|
} else if (action == "get_clock_time") {
|
||||||
handleGetClockTimeCommand(context);
|
handleGetClockTimeCommand(context);
|
||||||
} else if (action == "commit_firmware") {
|
|
||||||
handleCommitFirmwareCommand(context);
|
|
||||||
} else if (action == "rollback_firmware") {
|
|
||||||
handleRollbackFirmwareCommand(context);
|
|
||||||
} else if (action == "get_firmware_status") {
|
} else if (action == "get_firmware_status") {
|
||||||
handleGetFirmwareStatusCommand(context);
|
handleGetFirmwareStatusCommand(context);
|
||||||
} else if (action == "network_info") {
|
} else if (action == "network_info") {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class FileManager;
|
|||||||
class Timekeeper;
|
class Timekeeper;
|
||||||
class FirmwareValidator;
|
class FirmwareValidator;
|
||||||
class ClientManager;
|
class ClientManager;
|
||||||
|
class Telemetry;
|
||||||
|
|
||||||
class CommandHandler {
|
class CommandHandler {
|
||||||
public:
|
public:
|
||||||
@@ -65,6 +66,7 @@ public:
|
|||||||
void setTimeKeeperReference(Timekeeper* tk);
|
void setTimeKeeperReference(Timekeeper* tk);
|
||||||
void setFirmwareValidatorReference(FirmwareValidator* fv);
|
void setFirmwareValidatorReference(FirmwareValidator* fv);
|
||||||
void setClientManagerReference(ClientManager* cm);
|
void setClientManagerReference(ClientManager* cm);
|
||||||
|
void setTelemetryReference(Telemetry* telemetry);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set response callback for sending responses back
|
* @brief Set response callback for sending responses back
|
||||||
@@ -87,6 +89,7 @@ private:
|
|||||||
Timekeeper* _timeKeeper;
|
Timekeeper* _timeKeeper;
|
||||||
FirmwareValidator* _firmwareValidator;
|
FirmwareValidator* _firmwareValidator;
|
||||||
ClientManager* _clientManager;
|
ClientManager* _clientManager;
|
||||||
|
Telemetry* _telemetry;
|
||||||
ResponseCallback _responseCallback;
|
ResponseCallback _responseCallback;
|
||||||
|
|
||||||
// Response helpers
|
// Response helpers
|
||||||
|
|||||||
@@ -97,6 +97,10 @@ void CommunicationRouter::setFirmwareValidatorReference(FirmwareValidator* fv) {
|
|||||||
_commandHandler.setFirmwareValidatorReference(fv);
|
_commandHandler.setFirmwareValidatorReference(fv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommunicationRouter::setTelemetryReference(Telemetry* telemetry) {
|
||||||
|
_commandHandler.setTelemetryReference(telemetry);
|
||||||
|
}
|
||||||
|
|
||||||
void CommunicationRouter::setupUdpDiscovery() {
|
void CommunicationRouter::setupUdpDiscovery() {
|
||||||
uint16_t discoveryPort = _configManager.getNetworkConfig().discoveryPort;
|
uint16_t discoveryPort = _configManager.getNetworkConfig().discoveryPort;
|
||||||
if (_udp.listen(discoveryPort)) {
|
if (_udp.listen(discoveryPort)) {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class FileManager;
|
|||||||
class Timekeeper;
|
class Timekeeper;
|
||||||
class Networking;
|
class Networking;
|
||||||
class FirmwareValidator;
|
class FirmwareValidator;
|
||||||
|
class Telemetry;
|
||||||
|
|
||||||
class CommunicationRouter {
|
class CommunicationRouter {
|
||||||
public:
|
public:
|
||||||
@@ -64,6 +65,7 @@ public:
|
|||||||
void setFileManagerReference(FileManager* fm);
|
void setFileManagerReference(FileManager* fm);
|
||||||
void setTimeKeeperReference(Timekeeper* tk);
|
void setTimeKeeperReference(Timekeeper* tk);
|
||||||
void setFirmwareValidatorReference(FirmwareValidator* fv);
|
void setFirmwareValidatorReference(FirmwareValidator* fv);
|
||||||
|
void setTelemetryReference(Telemetry* telemetry);
|
||||||
void setupUdpDiscovery();
|
void setupUdpDiscovery();
|
||||||
|
|
||||||
// Status methods
|
// Status methods
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ String ResponseBuilder::pong() {
|
|||||||
return success("pong", "");
|
return success("pong", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
String ResponseBuilder::deviceStatus(PlayerStatus playerStatus, uint32_t timeElapsed, uint64_t projectedRunTime) {
|
String ResponseBuilder::deviceStatus(PlayerStatus playerStatus, uint32_t timeElapsed, uint64_t projectedRunTime, const uint32_t strikeCounters[16]) {
|
||||||
StaticJsonDocument<512> statusDoc; // Increased size for additional data
|
DynamicJsonDocument statusDoc(1024); // Increased size for strikeCounters array
|
||||||
|
|
||||||
statusDoc["status"] = "SUCCESS";
|
statusDoc["status"] = "SUCCESS";
|
||||||
statusDoc["type"] = "current_status";
|
statusDoc["type"] = "current_status";
|
||||||
@@ -63,6 +63,12 @@ String ResponseBuilder::deviceStatus(PlayerStatus playerStatus, uint32_t timeEla
|
|||||||
payload["time_elapsed"] = timeElapsed; // in milliseconds
|
payload["time_elapsed"] = timeElapsed; // in milliseconds
|
||||||
payload["projected_run_time"] = projectedRunTime; // NEW: total projected duration
|
payload["projected_run_time"] = projectedRunTime; // NEW: total projected duration
|
||||||
|
|
||||||
|
// Add strike counters array
|
||||||
|
JsonArray strikeCountersArray = payload.createNestedArray("strike_counters");
|
||||||
|
for (uint8_t i = 0; i < 16; i++) {
|
||||||
|
strikeCountersArray.add(strikeCounters[i]);
|
||||||
|
}
|
||||||
|
|
||||||
String result;
|
String result;
|
||||||
serializeJson(statusDoc, result);
|
serializeJson(statusDoc, result);
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public:
|
|||||||
// Specialized response builders for common scenarios
|
// Specialized response builders for common scenarios
|
||||||
static String acknowledgment(const String& commandType);
|
static String acknowledgment(const String& commandType);
|
||||||
static String pong();
|
static String pong();
|
||||||
static String deviceStatus(PlayerStatus playerStatus, uint32_t timeElapsedMs, uint64_t projectedRunTime = 0);
|
static String deviceStatus(PlayerStatus playerStatus, uint32_t timeElapsedMs, uint64_t projectedRunTime, const uint32_t strikeCounters[16]);
|
||||||
static String melodyList(const String& fileListJson);
|
static String melodyList(const String& fileListJson);
|
||||||
static String downloadResult(bool success, const String& filename = "");
|
static String downloadResult(bool success, const String& filename = "");
|
||||||
static String configUpdate(const String& configType);
|
static String configUpdate(const String& configType);
|
||||||
|
|||||||
@@ -175,6 +175,52 @@ size_t FileManager::getFileSize(const String& filePath) {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FileManager::writeJsonFile(const String& filePath, JsonDocument& doc) {
|
||||||
|
if (!initializeSD()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
File file = SD.open(filePath.c_str(), FILE_WRITE);
|
||||||
|
if (!file) {
|
||||||
|
LOG_ERROR("Failed to open file for writing: %s", filePath.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serializeJson(doc, file) == 0) {
|
||||||
|
LOG_ERROR("Failed to write JSON to file: %s", filePath.c_str());
|
||||||
|
file.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
LOG_DEBUG("JSON file written successfully: %s", filePath.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileManager::readJsonFile(const String& filePath, JsonDocument& doc) {
|
||||||
|
if (!initializeSD()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
File file = SD.open(filePath.c_str(), FILE_READ);
|
||||||
|
if (!file) {
|
||||||
|
LOG_ERROR("Failed to open file for reading: %s", filePath.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeserializationError error = deserializeJson(doc, file);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
LOG_ERROR("Failed to parse JSON from file: %s, error: %s",
|
||||||
|
filePath.c_str(), error.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("JSON file read successfully: %s", filePath.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
// HEALTH CHECK IMPLEMENTATION
|
// HEALTH CHECK IMPLEMENTATION
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ public:
|
|||||||
bool createDirectory(const String& dirPath);
|
bool createDirectory(const String& dirPath);
|
||||||
size_t getFileSize(const String& filePath);
|
size_t getFileSize(const String& filePath);
|
||||||
|
|
||||||
|
// Generic read/write for JSON data
|
||||||
|
bool writeJsonFile(const String& filePath, JsonDocument& doc);
|
||||||
|
bool readJsonFile(const String& filePath, JsonDocument& doc);
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
// HEALTH CHECK METHOD
|
// HEALTH CHECK METHOD
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "Player.hpp"
|
#include "Player.hpp"
|
||||||
#include "../Communication/CommunicationRouter/CommunicationRouter.hpp"
|
#include "../Communication/CommunicationRouter/CommunicationRouter.hpp"
|
||||||
#include "../BellEngine/BellEngine.hpp"
|
#include "../BellEngine/BellEngine.hpp"
|
||||||
|
#include "../Telemetry/Telemetry.hpp"
|
||||||
|
|
||||||
// Note: Removed global melody_steps dependency for cleaner architecture
|
// Note: Removed global melody_steps dependency for cleaner architecture
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ Player::Player(CommunicationRouter* comm, FileManager* fm)
|
|||||||
, _commManager(comm)
|
, _commManager(comm)
|
||||||
, _fileManager(fm)
|
, _fileManager(fm)
|
||||||
, _bellEngine(nullptr)
|
, _bellEngine(nullptr)
|
||||||
|
, _telemetry(nullptr)
|
||||||
, _durationTimerHandle(NULL) {
|
, _durationTimerHandle(NULL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +57,7 @@ Player::Player()
|
|||||||
, _commManager(nullptr)
|
, _commManager(nullptr)
|
||||||
, _fileManager(nullptr)
|
, _fileManager(nullptr)
|
||||||
, _bellEngine(nullptr)
|
, _bellEngine(nullptr)
|
||||||
|
, _telemetry(nullptr)
|
||||||
, _durationTimerHandle(NULL) {
|
, _durationTimerHandle(NULL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +126,12 @@ void Player::forceStop() {
|
|||||||
hardStop = true;
|
hardStop = true;
|
||||||
isPlaying = false;
|
isPlaying = false;
|
||||||
setStatus(PlayerStatus::STOPPED); // Immediate stop, notify clients
|
setStatus(PlayerStatus::STOPPED); // Immediate stop, notify clients
|
||||||
|
|
||||||
|
// Save strike counters after melody stops
|
||||||
|
if (_telemetry) {
|
||||||
|
_telemetry->saveStrikeCounters();
|
||||||
|
}
|
||||||
|
|
||||||
LOG_DEBUG("Plbck: FORCE STOP");
|
LOG_DEBUG("Plbck: FORCE STOP");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,6 +146,12 @@ void Player::stop() {
|
|||||||
// Set STOPPING status - actual stop message will be sent when BellEngine finishes
|
// Set STOPPING status - actual stop message will be sent when BellEngine finishes
|
||||||
if (isPaused) {
|
if (isPaused) {
|
||||||
setStatus(PlayerStatus::STOPPED);
|
setStatus(PlayerStatus::STOPPED);
|
||||||
|
|
||||||
|
// Save strike counters after melody stops
|
||||||
|
if (_telemetry) {
|
||||||
|
_telemetry->saveStrikeCounters();
|
||||||
|
}
|
||||||
|
|
||||||
LOG_DEBUG("Plbck: STOP from PAUSED state");
|
LOG_DEBUG("Plbck: STOP from PAUSED state");
|
||||||
} else {
|
} else {
|
||||||
setStatus(PlayerStatus::STOPPING);
|
setStatus(PlayerStatus::STOPPING);
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
// ═════════════════════════════════════════════════════════════════════════════════
|
// ═════════════════════════════════════════════════════════════════════════════════
|
||||||
class CommunicationRouter; // Command handling and communication
|
class CommunicationRouter; // Command handling and communication
|
||||||
class BellEngine; // High-precision timing engine
|
class BellEngine; // High-precision timing engine
|
||||||
|
class Telemetry; // System telemetry and monitoring
|
||||||
|
|
||||||
// ═════════════════════════════════════════════════════════════════════════════════
|
// ═════════════════════════════════════════════════════════════════════════════════
|
||||||
// PLAYER STATUS ENUMERATION
|
// PLAYER STATUS ENUMERATION
|
||||||
@@ -126,6 +127,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setBellEngine(BellEngine* engine) { _bellEngine = engine; }
|
void setBellEngine(BellEngine* engine) { _bellEngine = engine; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set Telemetry reference for strike counter persistence
|
||||||
|
* @param telemetry Pointer to Telemetry instance
|
||||||
|
*/
|
||||||
|
void setTelemetry(Telemetry* telemetry) { _telemetry = telemetry; }
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
// MELODY METADATA - Public access for compatibility
|
// MELODY METADATA - Public access for compatibility
|
||||||
// ═══════════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
@@ -241,6 +248,7 @@ private:
|
|||||||
CommunicationRouter* _commManager; // 📡 Communication system reference
|
CommunicationRouter* _commManager; // 📡 Communication system reference
|
||||||
FileManager* _fileManager; // 📁 File operations reference
|
FileManager* _fileManager; // 📁 File operations reference
|
||||||
BellEngine* _bellEngine; // 🔥 High-precision timing engine reference
|
BellEngine* _bellEngine; // 🔥 High-precision timing engine reference
|
||||||
|
Telemetry* _telemetry; // 📄 Telemetry system reference
|
||||||
|
|
||||||
std::vector<uint16_t> _melodySteps; // 🎵 Melody data owned by Player
|
std::vector<uint16_t> _melodySteps; // 🎵 Melody data owned by Player
|
||||||
TimerHandle_t _durationTimerHandle = NULL; // ⏱️ FreeRTOS timer (saves 4KB vs task!)
|
TimerHandle_t _durationTimerHandle = NULL; // ⏱️ FreeRTOS timer (saves 4KB vs task!)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "Telemetry.hpp"
|
#include "Telemetry.hpp"
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
void Telemetry::begin() {
|
void Telemetry::begin() {
|
||||||
// Initialize arrays
|
// Initialize arrays
|
||||||
@@ -10,6 +11,9 @@ void Telemetry::begin() {
|
|||||||
|
|
||||||
coolingActive = false;
|
coolingActive = false;
|
||||||
|
|
||||||
|
// Load strike counters from SD if available
|
||||||
|
loadStrikeCounters();
|
||||||
|
|
||||||
// Create the telemetry task
|
// Create the telemetry task
|
||||||
xTaskCreatePinnedToCore(telemetryTask, "TelemetryTask", 4096, this, 2, &telemetryTaskHandle, 1);
|
xTaskCreatePinnedToCore(telemetryTask, "TelemetryTask", 4096, this, 2, &telemetryTaskHandle, 1);
|
||||||
|
|
||||||
@@ -21,6 +25,11 @@ void Telemetry::setPlayerReference(bool* isPlayingPtr) {
|
|||||||
LOG_DEBUG("Player reference set");
|
LOG_DEBUG("Player reference set");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Telemetry::setFileManager(FileManager* fm) {
|
||||||
|
fileManager = fm;
|
||||||
|
LOG_DEBUG("FileManager reference set");
|
||||||
|
}
|
||||||
|
|
||||||
void Telemetry::setForceStopCallback(void (*callback)()) {
|
void Telemetry::setForceStopCallback(void (*callback)()) {
|
||||||
forceStopCallback = callback;
|
forceStopCallback = callback;
|
||||||
LOG_DEBUG("Force stop callback set");
|
LOG_DEBUG("Force stop callback set");
|
||||||
@@ -175,6 +184,62 @@ void Telemetry::telemetryTask(void* parameter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
|
// STRIKE COUNTER PERSISTENCE
|
||||||
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
void Telemetry::saveStrikeCounters() {
|
||||||
|
if (!fileManager) {
|
||||||
|
LOG_WARNING("Cannot save strike counters: FileManager not set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticJsonDocument<512> doc;
|
||||||
|
JsonArray counters = doc.createNestedArray("strikeCounters");
|
||||||
|
|
||||||
|
// Thread-safe read of strike counters
|
||||||
|
portENTER_CRITICAL(&telemetrySpinlock);
|
||||||
|
for (uint8_t i = 0; i < 16; i++) {
|
||||||
|
counters.add(strikeCounters[i]);
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&telemetrySpinlock);
|
||||||
|
|
||||||
|
if (fileManager->writeJsonFile("/telemetry_data.json", doc)) {
|
||||||
|
LOG_INFO("Strike counters saved to SD card");
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Failed to save strike counters to SD card");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Telemetry::loadStrikeCounters() {
|
||||||
|
if (!fileManager) {
|
||||||
|
LOG_WARNING("Cannot load strike counters: FileManager not set");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticJsonDocument<512> doc;
|
||||||
|
|
||||||
|
if (!fileManager->readJsonFile("/telemetry_data.json", doc)) {
|
||||||
|
LOG_INFO("No previous strike counter data found, starting fresh");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonArray counters = doc["strikeCounters"];
|
||||||
|
if (counters.isNull()) {
|
||||||
|
LOG_WARNING("Invalid telemetry data format");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread-safe write of strike counters
|
||||||
|
portENTER_CRITICAL(&telemetrySpinlock);
|
||||||
|
for (uint8_t i = 0; i < 16 && i < counters.size(); i++) {
|
||||||
|
strikeCounters[i] = counters[i].as<uint32_t>();
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&telemetrySpinlock);
|
||||||
|
|
||||||
|
LOG_INFO("Strike counters loaded from SD card");
|
||||||
|
}
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
// HEALTH CHECK IMPLEMENTATION
|
// HEALTH CHECK IMPLEMENTATION
|
||||||
// ════════════════════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════════════════════
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "../Logging/Logging.hpp"
|
#include "../Logging/Logging.hpp"
|
||||||
|
#include "../FileManager/FileManager.hpp"
|
||||||
|
|
||||||
class Telemetry {
|
class Telemetry {
|
||||||
private:
|
private:
|
||||||
@@ -71,6 +72,7 @@ private:
|
|||||||
|
|
||||||
// External references (to be set via setters)
|
// External references (to be set via setters)
|
||||||
bool* playerIsPlayingPtr = nullptr;
|
bool* playerIsPlayingPtr = nullptr;
|
||||||
|
FileManager* fileManager = nullptr;
|
||||||
|
|
||||||
// Spinlock for critical sections
|
// Spinlock for critical sections
|
||||||
portMUX_TYPE telemetrySpinlock = portMUX_INITIALIZER_UNLOCKED;
|
portMUX_TYPE telemetrySpinlock = portMUX_INITIALIZER_UNLOCKED;
|
||||||
@@ -81,6 +83,7 @@ public:
|
|||||||
|
|
||||||
// Set external references
|
// Set external references
|
||||||
void setPlayerReference(bool* isPlayingPtr);
|
void setPlayerReference(bool* isPlayingPtr);
|
||||||
|
void setFileManager(FileManager* fm);
|
||||||
|
|
||||||
// Bell strike handling (call this on every hammer strike)
|
// Bell strike handling (call this on every hammer strike)
|
||||||
void recordBellStrike(uint8_t bellIndex);
|
void recordBellStrike(uint8_t bellIndex);
|
||||||
@@ -89,6 +92,10 @@ public:
|
|||||||
uint32_t getStrikeCount(uint8_t bellIndex);
|
uint32_t getStrikeCount(uint8_t bellIndex);
|
||||||
void resetStrikeCounters(); // User-requested reset
|
void resetStrikeCounters(); // User-requested reset
|
||||||
|
|
||||||
|
// Persistence methods
|
||||||
|
void saveStrikeCounters();
|
||||||
|
void loadStrikeCounters();
|
||||||
|
|
||||||
// Bell load management
|
// Bell load management
|
||||||
uint16_t getBellLoad(uint8_t bellIndex);
|
uint16_t getBellLoad(uint8_t bellIndex);
|
||||||
void setBellMaxLoad(uint8_t bellIndex, uint16_t maxLoad);
|
void setBellMaxLoad(uint8_t bellIndex, uint16_t maxLoad);
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
* 👨💻 AUTHOR: BellSystems bonamin
|
* 👨💻 AUTHOR: BellSystems bonamin
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define FW_VERSION "1.2"
|
#define FW_VERSION "1.3"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -71,6 +71,7 @@
|
|||||||
* ═══════════════════════════════════════════════════════════════════════════════
|
* ═══════════════════════════════════════════════════════════════════════════════
|
||||||
* v0.1 - Vesper Launch Beta
|
* v0.1 - Vesper Launch Beta
|
||||||
* v1.2 - Added Log Level Configuration via App/MQTT
|
* v1.2 - Added Log Level Configuration via App/MQTT
|
||||||
|
* v1.3 - Added Telemtry Reports to App, Various Playback Fixes
|
||||||
* ═══════════════════════════════════════════════════════════════════════════════
|
* ═══════════════════════════════════════════════════════════════════════════════
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -283,10 +284,11 @@ void setup()
|
|||||||
healthMonitor.setTimeKeeper(&timekeeper);
|
healthMonitor.setTimeKeeper(&timekeeper);
|
||||||
|
|
||||||
// Initialize Telemetry
|
// Initialize Telemetry
|
||||||
telemetry.begin();
|
|
||||||
telemetry.setPlayerReference(&player.isPlaying);
|
telemetry.setPlayerReference(&player.isPlaying);
|
||||||
// 🚑 CRITICAL: Connect force stop callback for overload protection!
|
// 🚑 CRITICAL: Connect force stop callback for overload protection!
|
||||||
telemetry.setForceStopCallback([]() { player.forceStop(); });
|
telemetry.setForceStopCallback([]() { player.forceStop(); });
|
||||||
|
telemetry.setFileManager(&fileManager);
|
||||||
|
telemetry.begin();
|
||||||
|
|
||||||
// Register Telemetry with health monitor
|
// Register Telemetry with health monitor
|
||||||
healthMonitor.setTelemetry(&telemetry);
|
healthMonitor.setTelemetry(&telemetry);
|
||||||
@@ -312,9 +314,11 @@ void setup()
|
|||||||
communication.setFileManagerReference(&fileManager);
|
communication.setFileManagerReference(&fileManager);
|
||||||
communication.setTimeKeeperReference(&timekeeper);
|
communication.setTimeKeeperReference(&timekeeper);
|
||||||
communication.setFirmwareValidatorReference(&firmwareValidator);
|
communication.setFirmwareValidatorReference(&firmwareValidator);
|
||||||
|
communication.setTelemetryReference(&telemetry);
|
||||||
|
|
||||||
player.setDependencies(&communication, &fileManager);
|
player.setDependencies(&communication, &fileManager);
|
||||||
player.setBellEngine(&bellEngine); // Connect the beast!
|
player.setBellEngine(&bellEngine); // Connect the beast!
|
||||||
|
player.setTelemetry(&telemetry);
|
||||||
|
|
||||||
// Register Communication with health monitor
|
// Register Communication with health monitor
|
||||||
healthMonitor.setCommunication(&communication);
|
healthMonitor.setCommunication(&communication);
|
||||||
|
|||||||
Reference in New Issue
Block a user