ESPSomfy-RTS/src/TxBuffer.cpp
cjkas 1962778912 test: cover the radio queue rules, shade TX state, rolling codes and web command hold
Move the pieces of the radio TX path that don't need FreeRTOS or NVS out of
Somfy.cpp and Web.cpp so they build in the native test env: the TX job and
event types and job encoding (TxBuffer), the queue rules (TxQueuePolicy.h),
a shade's pending radio jobs (ShadeTxState), the next rolling code
(RollingCode.h) and the web command queue types (WebCommand.h).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:26:33 +02:00

78 lines
2.7 KiB
C++

#include "TxBuffer.h"
#include "esp_log.h"
// Logged as part of the radio code it came from.
static const char *TAG = "Somfy";
int8_t somfy_tx_queue_t::oldest() const {
// The newest index is at position 0 so the oldest is the last one in use.
for(int8_t i = MAX_TX_BUFFER - 1; i >= 0; i--) {
if(this->index[i] < MAX_TX_BUFFER) return i;
}
return -1;
}
bool somfy_tx_queue_t::peek(somfy_tx_t *tx) const {
const int8_t i = this->oldest();
if(i < 0) return false;
memcpy(tx, &this->items[this->index[i]], sizeof(somfy_tx_t));
return true;
}
bool somfy_tx_queue_t::pop(somfy_tx_t *tx) {
const int8_t i = this->oldest();
if(i < 0) return false;
uint8_t ndx = this->index[i];
memcpy(tx, &this->items[ndx], sizeof(somfy_tx_t));
this->items[ndx].clear();
if(this->length > 0) this->length--;
this->index[i] = 255;
return true;
}
void somfy_tx_queue_t::push(uint8_t hwsync, uint8_t *payload, uint8_t bit_length) {
if(this->length >= MAX_TX_BUFFER) {
// We have overflowed the buffer simply empty the last item
// in this instance we will simply throw it away.
uint8_t ndx = this->index[MAX_TX_BUFFER - 1];
if(ndx < MAX_TX_BUFFER) this->items[ndx].clear();
this->index[MAX_TX_BUFFER - 1] = 255;
this->length--;
}
uint8_t first = 0;
// Place this record in the first empty slot. There will
// be one since we cleared a space above should there
// be an overflow.
for(uint8_t i = 0; i < MAX_TX_BUFFER; i++) {
if(this->items[i].bit_length == 0) {
first = i;
this->items[i].bit_length = bit_length;
this->items[i].hwsync = hwsync;
memcpy(&this->items[i].payload, payload, sizeof(this->items[i].payload));
break;
}
}
// Move the index so that it is the at position 0. The oldest item will fall off.
for(uint8_t i = MAX_TX_BUFFER - 1; i > 0; i--) {
this->index[i] = this->index[i - 1];
}
this->length++;
// When popping from the queue we always pull from the end
this->index[0] = first;
}
void encodeTxJob(somfy_tx_job_t &job, somfy_frame_t &frame, uint8_t repeat) {
frame.encodeFrame(job.tx.payload);
job.tx.hwsync = frame.bitLength == 56 ? 2 : 12;
job.tx.bit_length = frame.bitLength;
job.frame = frame;
if(repeat > TX_MAX_REPEATS) {
ESP_LOGW(TAG, "Limiting %u repeats to %u", repeat, TX_MAX_REPEATS);
repeat = TX_MAX_REPEATS;
}
job.repeats = repeat;
}
void encodeRepeatJob(somfy_tx_job_t &job, somfy_frame_t &frame, uint8_t repeat) {
encodeTxJob(job, frame, repeat);
job.firstRepeat = frame.repeats + 2;
}
void noteRepeatQueued(somfy_frame_t &frame, const somfy_tx_job_t &job) {
// Only advance the counter for frames that will go on air.
frame.repeats += job.repeats + 1;
}