This commit is contained in:
2025-10-04 10:44:42 +02:00
commit 5a7b2f9149
11 changed files with 453 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into the executable file.
The source code of each library should be placed in a separate directory
("lib/your_library_name/[Code]").
For example, see the structure of the following example libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
Example contents of `src/main.c` using Foo and Bar:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
The PlatformIO Library Dependency Finder will find automatically dependent
libraries by scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html
+128
View File
@@ -0,0 +1,128 @@
#include "hardware_serial.hpp"
ProtoSerial::ProtoSerial()
: serial(nullptr), initialized(false), currentState(WAITING_FOR_LENGTH),
payloadLength(0), messageAvailable(false) {
receivedMessage = INCOMING_MESSAGE_INIT;
}
ProtoSerial::~ProtoSerial() {
if (serial) {
delete serial;
}
}
void ProtoSerial::begin(int rxPin, int txPin, long baud) {
if (serial) {
delete serial;
}
#ifdef ESP32
serial = new HardwareSerial(1); // Use UART 1
serial->begin(baud, SERIAL_8N1, rxPin, txPin);
#elif defined(ESP8266)
serial = new SoftwareSerial(rxPin, txPin);
serial->begin(baud);
#endif
initialized = true;
currentState = WAITING_FOR_LENGTH;
payloadLength = 0;
messageAvailable = false;
}
bool ProtoSerial::sendMessage(const OutgoingMessage& message) {
if (!initialized || !serial) {
return false;
}
uint8_t buffer[OUTGOING_MESSAGE_SIZE];
// Create a stream that writes to our buffer
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
// Encode the message
if (!pb_encode(&stream, OUTGOING_MESSAGE_FIELDS, &message)) {
// Encoding failed
return false;
}
uint16_t messageLength = stream.bytes_written;
// Send the length first (as two bytes, little-endian)
serial->write((uint8_t*)&messageLength, sizeof(messageLength));
// Send the actual payload
serial->write(buffer, messageLength);
// Ensure data is sent
serial->flush();
return true;
}
void ProtoSerial::update() {
if (!initialized || !serial) {
return;
}
switch (currentState) {
case WAITING_FOR_LENGTH: {
if (serial->available() >= sizeof(payloadLength)) {
// Read the 2-byte length
serial->readBytes((uint8_t*)&payloadLength, sizeof(payloadLength));
// Protect against absurdly large lengths
if (payloadLength > sizeof(payloadBuffer) || payloadLength == 0) {
// Invalid length, reset
payloadLength = 0;
currentState = WAITING_FOR_LENGTH;
} else {
currentState = READING_PAYLOAD;
}
}
break;
}
case READING_PAYLOAD: {
if (serial->available() >= payloadLength) {
// Read the payload
serial->readBytes(payloadBuffer, payloadLength);
// Process the message
processReceivedMessage(payloadBuffer, payloadLength);
// Reset for the next message
currentState = WAITING_FOR_LENGTH;
payloadLength = 0;
}
break;
}
}
}
bool ProtoSerial::hasMessage() const {
return messageAvailable;
}
const IncomingMessage& ProtoSerial::getMessage() const {
return receivedMessage;
}
void ProtoSerial::clearMessage() {
messageAvailable = false;
receivedMessage = INCOMING_MESSAGE_INIT;
}
void ProtoSerial::processReceivedMessage(uint8_t* buffer, uint16_t length) {
// Reset the message
receivedMessage = INCOMING_MESSAGE_INIT;
// Create a stream from the buffer
pb_istream_t stream = pb_istream_from_buffer(buffer, length);
// Decode the message
if (pb_decode(&stream, INCOMING_MESSAGE_FIELDS, &receivedMessage)) {
messageAvailable = true;
} else {
// Decoding failed, do not set messageAvailable
}
}
+87
View File
@@ -0,0 +1,87 @@
#pragma once
#ifdef ESP32
#include <HardwareSerial.h>
typedef HardwareSerial SerialType;
#define SERIAL_TYPE HardwareSerial
#elif defined(ESP8266)
#include <SoftwareSerial.h>
typedef SoftwareSerial SerialType;
#define SERIAL_TYPE SoftwareSerial
#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_default
#define OUTGOING_MESSAGE_INIT hardware_ControlToSensorMessage_init_default
#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_default
#define OUTGOING_MESSAGE_INIT hardware_SensorToControlMessage_init_default
#else
#error "Must define either HARDWARE_SERIAL_ROLE_CONTROL or HARDWARE_SERIAL_ROLE_SENSOR"
#endif
class ProtoSerial {
public:
ProtoSerial();
~ProtoSerial();
// Initialize with RX and TX pins
void begin(int rxPin, int txPin, long baud = 9600);
// Send a message
bool sendMessage(const OutgoingMessage& message);
// Check for incoming messages (non-blocking)
void update();
// Check if a message has been received
bool hasMessage() const;
// Get the received message (only valid if hasMessage() returns true)
const IncomingMessage& getMessage() const;
// Clear the received message
void clearMessage();
private:
SerialType* serial;
bool initialized;
// Receive state machine
enum RxState {
WAITING_FOR_LENGTH,
READING_PAYLOAD
};
RxState currentState;
uint16_t payloadLength;
uint8_t payloadBuffer[64]; // Max message size + some buffer
IncomingMessage receivedMessage;
bool messageAvailable;
// Helper methods
void processReceivedMessage(uint8_t* buffer, uint16_t length);
};