Files
2025-10-06 18:27:50 +02:00

171 lines
6.0 KiB
C++

#pragma once
#ifdef ESP32
#include <HardwareSerial.h>
typedef HardwareSerial SerialType;
#define SERIAL_TYPE HardwareSerial
#elif defined(ESP8266)
#include <HardwareSerial.h>
typedef HardwareSerial SerialType;
#define SERIAL_TYPE HardwareSerial
#else
#error "Unsupported platform. Only ESP32 and ESP8266 are supported."
#endif
#include <pb_encode.h>
#include <pb_decode.h>
#include "hardware.pb.h"
// Define the role of this device
// Uncomment one of the following defines to set the role
#define HARDWARE_SERIAL_ROLE_CONTROL
// #define HARDWARE_SERIAL_ROLE_SENSOR
// Based on role, define the message types
#if defined(HARDWARE_SERIAL_ROLE_CONTROL)
typedef hardware_SensorToControlMessage IncomingMessage;
typedef hardware_ControlToSensorMessage OutgoingMessage;
#define INCOMING_MESSAGE_SIZE hardware_SensorToControlMessage_size
#define OUTGOING_MESSAGE_SIZE hardware_ControlToSensorMessage_size
#define INCOMING_MESSAGE_FIELDS hardware_SensorToControlMessage_fields
#define OUTGOING_MESSAGE_FIELDS hardware_ControlToSensorMessage_fields
#define INCOMING_MESSAGE_INIT hardware_SensorToControlMessage_init_zero
#define OUTGOING_MESSAGE_INIT hardware_ControlToSensorMessage_init_zero
#elif defined(HARDWARE_SERIAL_ROLE_SENSOR)
typedef hardware_ControlToSensorMessage IncomingMessage;
typedef hardware_SensorToControlMessage OutgoingMessage;
#define INCOMING_MESSAGE_SIZE hardware_ControlToSensorMessage_size
#define OUTGOING_MESSAGE_SIZE hardware_SensorToControlMessage_size
#define INCOMING_MESSAGE_FIELDS hardware_ControlToSensorMessage_fields
#define OUTGOING_MESSAGE_FIELDS hardware_SensorToControlMessage_fields
#define INCOMING_MESSAGE_INIT hardware_ControlToSensorMessage_init_zero
#define OUTGOING_MESSAGE_INIT hardware_ControlToSensorMessage_init_zero
#else
#error "Must define either HARDWARE_SERIAL_ROLE_CONTROL or HARDWARE_SERIAL_ROLE_SENSOR"
#endif
/**
* @class ProtoSerial
* @brief Manages serial communication with Protocol Buffers for ESP32/ESP8266.
* Supports length-prefixed messages with CRC-8 checksum and sync bytes.
* Configured for SENSOR or CONTROL role via HARDWARE_SERIAL_ROLE_* macros.
* On ESP8266, uses UART0 (Serial, RX=GPIO3, TX=GPIO1). On ESP32, uses caller-provided UART.
*/
class ProtoSerial {
public:
using Callback = void (*)(const IncomingMessage&); ///< Callback for received messages.
/**
* @brief Constructor.
*/
ProtoSerial();
/**
* @brief Destructor. No-op (serial is managed by caller).
*/
~ProtoSerial();
/**
* @brief Initializes serial communication with a caller-provided serial port.
* @param serialPort Reference to a configured SerialType (e.g., Serial for ESP8266, HardwareSerial for ESP32).
*/
void begin(SerialType& serialPort);
/**
* @brief Sends a Protocol Buffers message with sync bytes and CRC-8.
* @param message The message to send (OutgoingMessage type).
* @return True if sent successfully, false otherwise.
*/
bool sendMessage(const OutgoingMessage& message);
/**
* @brief Checks for incoming messages (non-blocking).
*/
void update();
/**
* @brief Checks if a message is available.
* @return True if a message is ready to be read.
*/
bool hasMessage() const;
/**
* @brief Gets the received message.
* @return Reference to the received message (valid only if hasMessage() is true).
*/
const IncomingMessage& getMessage() const;
/**
* @brief Clears the received message.
*/
void clearMessage();
/**
* @brief Sets the callback for received messages.
* @param cb Callback function to handle incoming messages.
*/
void setCallback(Callback cb);
/**
* @brief Checks if the serial interface is initialized.
* @return True if initialized.
*/
bool isInitialized() const { return initialized; }
/**
* @brief Gets the last error message (for debugging).
* @return Last error string or empty if none.
*/
const char* getLastError() const { return lastError; }
private:
SerialType* serial; ///< Serial interface (pointer to caller-provided SerialType).
bool initialized; ///< Tracks initialization state.
char lastError[64]; ///< Last error message (fixed-size to avoid heap issues).
Callback callback; ///< Callback for received messages.
// Framing constants
static const uint8_t SYNC_START = 0xAA; ///< Start byte for message framing.
static const uint8_t SYNC_END = 0xBB; ///< End byte for message framing.
// Receive state machine
enum RxState {
WAITING_FOR_SYNC_START, ///< Waiting for start byte (0xAA).
WAITING_FOR_LENGTH, ///< Waiting for 2-byte length.
READING_PAYLOAD, ///< Reading payload bytes.
READING_CRC ///< Reading CRC-8 byte.
};
RxState currentState; ///< Current state of receive state machine.
uint16_t payloadLength; ///< Length of incoming payload.
uint8_t payloadBuffer[INCOMING_MESSAGE_SIZE + 10]; ///< Buffer for payload (plus overhead).
IncomingMessage receivedMessage; ///< Decoded incoming message.
bool messageAvailable; ///< True if a message is ready.
unsigned long lastUpdate; ///< Timestamp of last update (for throttling).
unsigned long lastByteTime; ///< Timestamp of last byte received (for timeout).
// Helper methods
/**
* @brief Processes a received payload and decodes it.
* @param buffer Payload buffer.
* @param length Payload length.
*/
void processReceivedMessage(uint8_t* buffer, uint16_t length);
/**
* @brief Calculates CRC-8 for a buffer.
* @param data Buffer to compute CRC over.
* @param len Length of buffer.
* @return CRC-8 value.
*/
uint8_t calculate_crc8(const uint8_t* data, uint16_t len);
/**
* @brief Logs a hex dump of a buffer (debug only).
* @param data Buffer to dump.
* @param len Length of buffer.
* @param label Label for the dump.
*/
void dumpHex(const uint8_t* data, uint16_t len, const char* label);
};
extern ProtoSerial pserial;