This commit is contained in:
2025-10-04 10:44:42 +02:00
commit 5a7b2f9149
11 changed files with 453 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
+10
View File
@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}
+37
View File
@@ -0,0 +1,37 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the convention is to give header files names that end with `.h'.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
+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);
};
+28
View File
@@ -0,0 +1,28 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = esp8285
[env]
framework = arduino
monitor_speed = 9600
board_build.filesystem = littlefs
lib_deps =
miguelbalboa/MFRC522@^1.4.12
adafruit/Adafruit NeoPixel@^1.15.1
nanopb/Nanopb@^0.4.91
custom_nanopb_protos =
+<proto/hardware.proto>
custom_nanopb_options =
--error-on-unmatched
[env:esp8285]
platform = espressif8266
board = esp8285
+4
View File
@@ -0,0 +1,4 @@
# Nanopb options for hardware.proto
# LED configuration
hardware.FadeParams.colors max_count:5
+79
View File
@@ -0,0 +1,79 @@
syntax = "proto3";
package hardware;
// LED animation types
enum LedAnimation {
LED_ANIMATION_STATIC = 0;
LED_ANIMATION_PULSE = 1;
LED_ANIMATION_FADE = 2;
LED_ANIMATION_FLICKER = 3;
}
// Hardware configuration
message HardwareConfig {
uint32 hold_duration_ms = 1;
bool override = 2;
uint32 relay_pin = 3;
uint32 sensor_rx_pin = 4;
uint32 sensor_tx_pin = 5;
LedConfig on_open_led = 6;
LedConfig default_led = 7;
LedConfig on_invalid_led = 8;
bool enable_serial_sensor = 9;
// repeated char sensor_api_key = 10;
// bool enable_ws_sensor = 11;
}
// LED configuration
message LedConfig {
// General properties that apply to all animations
uint32 brightness = 1; // 0-255
uint32 duration_ms = 2; // 0 for indefinite
oneof animation_params {
StaticParams static_params = 3;
PulseParams pulse_params = 4;
FadeParams fade_params = 5;
FlickerParams flicker_params = 6;
}
}
// Define the specific parameters for each animation type
message StaticParams {
uint32 color = 1;
}
message PulseParams {
uint32 color = 1;
uint32 speed_ms = 2;
}
message FadeParams {
repeated uint32 colors = 1; // Fade between these colors
uint32 speed_ms = 2;
}
message FlickerParams {
uint32 color = 1;
uint32 intensity = 2; // e.g., 0-100
}
message RfidReading {
uint32 card_id = 1;
}
message SensorToControlMessage {
uint32 sensor_id = 1;
oneof payload {
RfidReading rfid_reading = 2;
// Add other sensor message types as needed
}
}
message ControlToSensorMessage {
uint32 control_id = 1;
oneof payload {
LedConfig led_config = 2;
// Add other control message types as needed
}
}
+18
View File
@@ -0,0 +1,18 @@
#include <Arduino.h>
// put function declarations here:
int myFunction(int, int);
void setup() {
// put your setup code here, to run once:
int result = myFunction(2, 3);
}
void loop() {
// put your main code here, to run repeatedly:
}
// put function definitions here:
int myFunction(int x, int y) {
return x + y;
}
+11
View File
@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html