Snapshot iniziale: bridge telemetria CRSF->LTM/MAVLink (ESP-NOW)

Stato di partenza prima dei fix. Il repo git era inizializzato a meta'
(config.lock + objects/ mancante, zero commit): riparato.
my_config.h (UID reale, tutti e 3 i sottoprogetti) escluso via .gitignore;
aggiunto my_config.h.example. .pio/ escluso.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Francesco Zanin
2026-06-23 19:32:11 +02:00
commit e22a47fce1
284 changed files with 132664 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
#include "EspNowCrsf.h"
#include <WiFi.h>
#include <esp_wifi.h>
#include <esp_now.h>
namespace EspNowCrsf {
// ---- Ring buffer dei frame CRSF ----------------------------
static const uint8_t RING_SLOTS = 8;
static const uint16_t SLOT_SIZE = 64;
struct Slot { uint8_t data[SLOT_SIZE]; uint16_t len; };
static Slot ring[RING_SLOTS];
static volatile uint8_t head = 0; // scritto dalla callback
static volatile uint8_t tail = 0; // letto dal loop
static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
static volatile uint32_t s_received = 0;
static volatile uint32_t s_dropped = 0;
static uint8_t s_uid[6];
// ---- Callback ricezione (firma arduino-esp32 core 2.x) -----
static void onRecv(const uint8_t* mac, const uint8_t* in, int len) {
if (len <= 8 || in == nullptr) return;
// keep-alive: type/payload a zero subito dopo l'header di 8 byte
// (len > 9 prima degli accessi: con len==9 leggere in[9] sarebbe OOB)
if (len > 9 && in[8] == 0 && in[9] == 0) return;
uint16_t crsfLen = len - 8;
if (crsfLen > SLOT_SIZE) crsfLen = SLOT_SIZE;
portENTER_CRITICAL_ISR(&mux);
uint8_t next = (head + 1) % RING_SLOTS;
if (next == tail) { // pieno -> scarta il piu' vecchio
tail = (tail + 1) % RING_SLOTS;
s_dropped++;
}
memcpy(ring[head].data, in + 8, crsfLen);
ring[head].len = crsfLen;
head = next;
s_received++;
portEXIT_CRITICAL_ISR(&mux);
}
void begin(const uint8_t uid[6]) {
memcpy(s_uid, uid, 6);
s_uid[0] &= ~0x01; // MAC unicast
WiFi.mode(WIFI_STA);
WiFi.disconnect();
esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11B);
esp_wifi_set_mac(WIFI_IF_STA, s_uid);
esp_wifi_start();
esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE);
if (esp_now_init() != ESP_OK) return;
esp_now_register_recv_cb(onRecv);
// Peer broadcast per l'invio sperimentale (uplink)
esp_now_peer_info_t peer = {};
memset(peer.peer_addr, 0xFF, 6); // broadcast
peer.channel = 1;
peer.encrypt = false;
esp_now_add_peer(&peer);
}
bool poll(uint8_t* out, uint16_t* outLen, uint16_t maxLen) {
bool got = false;
portENTER_CRITICAL(&mux);
if (tail != head) {
uint16_t n = ring[tail].len;
if (n > maxLen) n = maxLen;
memcpy(out, ring[tail].data, n);
*outLen = n;
tail = (tail + 1) % RING_SLOTS;
got = true;
}
portEXIT_CRITICAL(&mux);
return got;
}
uint32_t framesReceived() { return s_received; }
uint32_t framesDropped() { return s_dropped; }
bool sendRaw(const uint8_t* data, uint16_t len) {
static const uint8_t bcast[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
return esp_now_send(bcast, data, len) == ESP_OK;
}
#ifdef UNIT_TEST
void test_injectRaw(const uint8_t* data, uint16_t len) { onRecv(nullptr, data, len); }
void test_reset() {
portENTER_CRITICAL(&mux);
head = 0;
tail = 0;
s_received = 0;
s_dropped = 0;
portEXIT_CRITICAL(&mux);
}
#endif
} // namespace EspNowCrsf