Added UART as a communication interface option.
This commit is contained in:
@@ -41,7 +41,8 @@ public:
|
||||
// Message source identification
|
||||
enum class MessageSource {
|
||||
MQTT,
|
||||
WEBSOCKET
|
||||
WEBSOCKET,
|
||||
UART
|
||||
};
|
||||
|
||||
struct MessageContext {
|
||||
|
||||
@@ -33,6 +33,7 @@ CommunicationRouter::CommunicationRouter(ConfigManager& configManager,
|
||||
, _wsServer(webSocket, _clientManager)
|
||||
, _commandHandler(configManager, otaManager)
|
||||
, _httpHandler(server, configManager)
|
||||
, _uartHandler()
|
||||
, _settingsServer(server, configManager, networking) {}
|
||||
|
||||
CommunicationRouter::~CommunicationRouter() {}
|
||||
@@ -106,13 +107,27 @@ void CommunicationRouter::begin() {
|
||||
_settingsServer.begin();
|
||||
LOG_INFO("✅ Settings Web Server initialized at /settings");
|
||||
|
||||
// Initialize UART Command Handler
|
||||
LOG_INFO("Setting up UART Command Handler...");
|
||||
_uartHandler.begin();
|
||||
_uartHandler.setCallback([this](JsonDocument& message) {
|
||||
onUartMessage(message);
|
||||
});
|
||||
LOG_INFO("✅ UART Command Handler initialized (TX: GPIO12, RX: GPIO13)");
|
||||
|
||||
LOG_INFO("Communication Router initialized with modular architecture");
|
||||
LOG_INFO(" • MQTT: AsyncMqttClient");
|
||||
LOG_INFO(" • WebSocket: Multi-client support");
|
||||
LOG_INFO(" • HTTP REST API: /api endpoints");
|
||||
LOG_INFO(" • UART: External device control");
|
||||
LOG_INFO(" • Settings Page: /settings");
|
||||
}
|
||||
|
||||
void CommunicationRouter::loop() {
|
||||
// Process UART incoming data
|
||||
_uartHandler.loop();
|
||||
}
|
||||
|
||||
void CommunicationRouter::setPlayerReference(Player* player) {
|
||||
_player = player;
|
||||
_commandHandler.setPlayerReference(player);
|
||||
@@ -327,6 +342,20 @@ void CommunicationRouter::onWebSocketMessage(uint32_t clientId, const JsonDocume
|
||||
LOG_DEBUG("WebSocket message from client #%u processed", clientId);
|
||||
}
|
||||
|
||||
void CommunicationRouter::onUartMessage(JsonDocument& message) {
|
||||
// Extract command for logging
|
||||
String cmd = message["cmd"] | "unknown";
|
||||
LOG_INFO("🔌 UART message received: cmd=%s", cmd.c_str());
|
||||
|
||||
// Create message context for UART
|
||||
CommandHandler::MessageContext context(CommandHandler::MessageSource::UART);
|
||||
|
||||
// Forward to command handler
|
||||
_commandHandler.processCommand(message, context);
|
||||
|
||||
LOG_DEBUG("UART message processed");
|
||||
}
|
||||
|
||||
void CommunicationRouter::sendResponse(const String& response, const CommandHandler::MessageContext& context) {
|
||||
if (context.source == CommandHandler::MessageSource::MQTT) {
|
||||
LOG_DEBUG("↗️ Sending response via MQTT: %s", response.c_str());
|
||||
@@ -334,6 +363,9 @@ void CommunicationRouter::sendResponse(const String& response, const CommandHand
|
||||
} else if (context.source == CommandHandler::MessageSource::WEBSOCKET) {
|
||||
LOG_DEBUG("↗️ Sending response to WebSocket client #%u: %s", context.clientId, response.c_str());
|
||||
_wsServer.sendToClient(context.clientId, response);
|
||||
} else if (context.source == CommandHandler::MessageSource::UART) {
|
||||
LOG_DEBUG("↗️ Sending response via UART: %s", response.c_str());
|
||||
_uartHandler.send(response);
|
||||
} else {
|
||||
LOG_ERROR("❌ Unknown message source for response routing!");
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "../CommandHandler/CommandHandler.hpp"
|
||||
#include "../ResponseBuilder/ResponseBuilder.hpp"
|
||||
#include "../HTTPRequestHandler/HTTPRequestHandler.hpp"
|
||||
#include "../UARTCommandHandler/UARTCommandHandler.hpp"
|
||||
#include "../../ClientManager/ClientManager.hpp"
|
||||
#include "../../SettingsWebServer/SettingsWebServer.hpp"
|
||||
|
||||
@@ -63,6 +64,7 @@ public:
|
||||
~CommunicationRouter();
|
||||
|
||||
void begin();
|
||||
void loop(); // Must be called from main loop for UART processing
|
||||
void setPlayerReference(Player* player);
|
||||
void setFileManagerReference(FileManager* fm);
|
||||
void setTimeKeeperReference(Timekeeper* tk);
|
||||
@@ -78,6 +80,7 @@ public:
|
||||
|
||||
// Component accessors
|
||||
MQTTAsyncClient& getMQTTClient() { return _mqttClient; }
|
||||
UARTCommandHandler& getUARTHandler() { return _uartHandler; }
|
||||
|
||||
// Broadcast methods
|
||||
void broadcastStatus(const String& statusMessage);
|
||||
@@ -116,11 +119,13 @@ private:
|
||||
WebSocketServer _wsServer;
|
||||
CommandHandler _commandHandler;
|
||||
HTTPRequestHandler _httpHandler;
|
||||
UARTCommandHandler _uartHandler;
|
||||
SettingsWebServer _settingsServer;
|
||||
|
||||
// Message handlers
|
||||
void onMqttMessage(const String& topic, const String& payload);
|
||||
void onWebSocketMessage(uint32_t clientId, const JsonDocument& message);
|
||||
void onUartMessage(JsonDocument& message);
|
||||
|
||||
// Response routing
|
||||
void sendResponse(const String& response, const CommandHandler::MessageContext& context);
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* UARTCOMMANDHANDLER.CPP - UART Command Handler Implementation
|
||||
*/
|
||||
|
||||
#include "UARTCommandHandler.hpp"
|
||||
#include "../../Logging/Logging.hpp"
|
||||
|
||||
UARTCommandHandler::UARTCommandHandler(uint8_t txPin, uint8_t rxPin, uint32_t baudRate)
|
||||
: _serial(Serial2)
|
||||
, _txPin(txPin)
|
||||
, _rxPin(rxPin)
|
||||
, _baudRate(baudRate)
|
||||
, _ready(false)
|
||||
, _bufferIndex(0)
|
||||
, _messageCount(0)
|
||||
, _errorCount(0)
|
||||
, _callback(nullptr)
|
||||
{
|
||||
resetBuffer();
|
||||
}
|
||||
|
||||
UARTCommandHandler::~UARTCommandHandler() {
|
||||
_serial.end();
|
||||
}
|
||||
|
||||
void UARTCommandHandler::begin() {
|
||||
LOG_INFO("Initializing UART Command Handler");
|
||||
LOG_INFO(" TX Pin: GPIO%d", _txPin);
|
||||
LOG_INFO(" RX Pin: GPIO%d", _rxPin);
|
||||
LOG_INFO(" Baud Rate: %u", _baudRate);
|
||||
|
||||
// Initialize Serial2 with custom pins
|
||||
_serial.begin(_baudRate, SERIAL_8N1, _rxPin, _txPin);
|
||||
|
||||
// Clear any garbage in the buffer
|
||||
while (_serial.available()) {
|
||||
_serial.read();
|
||||
}
|
||||
|
||||
_ready = true;
|
||||
LOG_INFO("UART Command Handler ready");
|
||||
}
|
||||
|
||||
void UARTCommandHandler::loop() {
|
||||
if (!_ready) return;
|
||||
|
||||
// Process all available bytes
|
||||
while (_serial.available()) {
|
||||
char c = _serial.read();
|
||||
|
||||
// Check for message delimiter (newline)
|
||||
if (c == '\n' || c == '\r') {
|
||||
if (_bufferIndex > 0) {
|
||||
// Null-terminate and process
|
||||
_buffer[_bufferIndex] = '\0';
|
||||
processLine(_buffer);
|
||||
resetBuffer();
|
||||
}
|
||||
// Skip empty lines
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add character to buffer
|
||||
if (_bufferIndex < BUFFER_SIZE - 1) {
|
||||
_buffer[_bufferIndex++] = c;
|
||||
} else {
|
||||
// Buffer overflow - discard and reset
|
||||
LOG_ERROR("UART buffer overflow, discarding message");
|
||||
_errorCount++;
|
||||
resetBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UARTCommandHandler::setCallback(MessageCallback callback) {
|
||||
_callback = callback;
|
||||
}
|
||||
|
||||
void UARTCommandHandler::send(const String& response) {
|
||||
if (!_ready) {
|
||||
LOG_ERROR("UART not ready, cannot send response");
|
||||
return;
|
||||
}
|
||||
|
||||
_serial.print(response);
|
||||
_serial.print('\n'); // Newline delimiter
|
||||
_serial.flush(); // Ensure data is sent
|
||||
|
||||
LOG_DEBUG("UART TX: %s", response.c_str());
|
||||
}
|
||||
|
||||
void UARTCommandHandler::processLine(const char* line) {
|
||||
LOG_DEBUG("UART RX: %s", line);
|
||||
|
||||
// Skip empty lines or whitespace-only
|
||||
if (strlen(line) == 0) return;
|
||||
|
||||
// Parse JSON
|
||||
StaticJsonDocument<1024> doc;
|
||||
DeserializationError error = deserializeJson(doc, line);
|
||||
|
||||
if (error) {
|
||||
LOG_ERROR("UART JSON parse error: %s", error.c_str());
|
||||
_errorCount++;
|
||||
|
||||
// Send error response back
|
||||
StaticJsonDocument<256> errorDoc;
|
||||
errorDoc["status"] = "ERROR";
|
||||
errorDoc["type"] = "parse_error";
|
||||
errorDoc["payload"] = error.c_str();
|
||||
|
||||
String errorResponse;
|
||||
serializeJson(errorDoc, errorResponse);
|
||||
send(errorResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
_messageCount++;
|
||||
|
||||
// Invoke callback if set
|
||||
if (_callback) {
|
||||
_callback(doc);
|
||||
} else {
|
||||
LOG_WARNING("UART message received but no callback set");
|
||||
}
|
||||
}
|
||||
|
||||
void UARTCommandHandler::resetBuffer() {
|
||||
_bufferIndex = 0;
|
||||
memset(_buffer, 0, BUFFER_SIZE);
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* ═══════════════════════════════════════════════════════════════════════════════════
|
||||
* UARTCOMMANDHANDLER.HPP - UART Command Interface for External Control Devices
|
||||
* ═══════════════════════════════════════════════════════════════════════════════════
|
||||
*
|
||||
* 🔌 UART COMMAND HANDLER 🔌
|
||||
*
|
||||
* Enables command input from external devices (LCD panels, button controllers)
|
||||
* via UART serial communication. Uses newline-delimited JSON protocol.
|
||||
*
|
||||
* Pin Configuration:
|
||||
* • TX: GPIO12
|
||||
* • RX: GPIO13
|
||||
* • Baud: 115200 (configurable)
|
||||
*
|
||||
* Protocol:
|
||||
* • Newline-delimited JSON messages
|
||||
* • Same command format as MQTT/WebSocket
|
||||
* • Responses sent back on same UART
|
||||
*
|
||||
* 📋 VERSION: 1.0
|
||||
* 📅 DATE: 2025-01-19
|
||||
* 👨💻 AUTHOR: Advanced Bell Systems
|
||||
* ═══════════════════════════════════════════════════════════════════════════════════
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <functional>
|
||||
|
||||
class UARTCommandHandler {
|
||||
public:
|
||||
// Default pin configuration
|
||||
static constexpr uint8_t DEFAULT_TX_PIN = 12;
|
||||
static constexpr uint8_t DEFAULT_RX_PIN = 13;
|
||||
static constexpr uint32_t DEFAULT_BAUD_RATE = 115200;
|
||||
static constexpr size_t BUFFER_SIZE = 1024;
|
||||
|
||||
// Message callback type - called when a complete JSON message is received
|
||||
using MessageCallback = std::function<void(JsonDocument& message)>;
|
||||
|
||||
/**
|
||||
* @brief Construct UART handler with custom pins
|
||||
* @param txPin GPIO pin for TX (default: 12)
|
||||
* @param rxPin GPIO pin for RX (default: 13)
|
||||
* @param baudRate Baud rate (default: 115200)
|
||||
*/
|
||||
explicit UARTCommandHandler(uint8_t txPin = DEFAULT_TX_PIN,
|
||||
uint8_t rxPin = DEFAULT_RX_PIN,
|
||||
uint32_t baudRate = DEFAULT_BAUD_RATE);
|
||||
|
||||
~UARTCommandHandler();
|
||||
|
||||
/**
|
||||
* @brief Initialize the UART interface
|
||||
*/
|
||||
void begin();
|
||||
|
||||
/**
|
||||
* @brief Process incoming UART data (call from loop or task)
|
||||
* Non-blocking - processes available bytes and returns
|
||||
*/
|
||||
void loop();
|
||||
|
||||
/**
|
||||
* @brief Set callback for received messages
|
||||
* @param callback Function to call with parsed JSON
|
||||
*/
|
||||
void setCallback(MessageCallback callback);
|
||||
|
||||
/**
|
||||
* @brief Send a response back over UART
|
||||
* @param response JSON string to send (newline appended automatically)
|
||||
*/
|
||||
void send(const String& response);
|
||||
|
||||
/**
|
||||
* @brief Check if UART is initialized and ready
|
||||
*/
|
||||
bool isReady() const { return _ready; }
|
||||
|
||||
/**
|
||||
* @brief Get number of messages received since boot
|
||||
*/
|
||||
uint32_t getMessageCount() const { return _messageCount; }
|
||||
|
||||
/**
|
||||
* @brief Get number of parse errors since boot
|
||||
*/
|
||||
uint32_t getErrorCount() const { return _errorCount; }
|
||||
|
||||
private:
|
||||
HardwareSerial& _serial;
|
||||
uint8_t _txPin;
|
||||
uint8_t _rxPin;
|
||||
uint32_t _baudRate;
|
||||
bool _ready;
|
||||
|
||||
// Receive buffer
|
||||
char _buffer[BUFFER_SIZE];
|
||||
size_t _bufferIndex;
|
||||
|
||||
// Statistics
|
||||
uint32_t _messageCount;
|
||||
uint32_t _errorCount;
|
||||
|
||||
// Callback
|
||||
MessageCallback _callback;
|
||||
|
||||
/**
|
||||
* @brief Process a complete line from the buffer
|
||||
* @param line Null-terminated string containing the message
|
||||
*/
|
||||
void processLine(const char* line);
|
||||
|
||||
/**
|
||||
* @brief Reset the receive buffer
|
||||
*/
|
||||
void resetBuffer();
|
||||
};
|
||||
Reference in New Issue
Block a user