Initial commit — firmware WireClaw ESP32-S3 fonctionnel

- Heartbeat MAVLink v1 toutes les 1s (broadcast 192.168.4.255:14553)
- Relais WiFi <-> UART (Jetson <-> Pi Zero) sur ports 14550-14553
- LED RGB WS2812 GPIO48 : rouge pulsé (attente), vert dim (idle), flash vert (heartbeat)
- MODE_AP=true : hotspot WireClaw-CAM / false : connexion Jetson
- Fix: retrait ARDUINO_USB_CDC_ON_BOOT (carte Freenove utilise CH343/UART0)
This commit is contained in:
2026-06-03 18:34:14 +02:00
commit ba3221ae4f
6 changed files with 425 additions and 0 deletions

192
src/main.cpp Normal file
View File

@ -0,0 +1,192 @@
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiUDP.h>
#include <FastLED.h>
#include "credentials.h"
#include "mavlink_heartbeat.h"
// ── Mode : true = ESP32 crée son propre hotspot false = connexion au Jetson ──
#define MODE_AP true
// ── Ports ────────────────────────────────────────────────
const uint16_t LOCAL_PORT = 14550;
const uint16_t JETSON_PORT_MAVSDK = 14551;
const uint16_t JETSON_PORT_PYMAV = 14552;
const uint16_t JETSON_PORT_HB = 14553;
// ── UART vers Pi Zero ─────────────────────────────────────
#define UART_BAUD 57600
#define UART_RX_PIN 16
#define UART_TX_PIN 17
// ── LED RGB WS2812 (GPIO48) ───────────────────────────────
#define LED_PIN 48
#define NUM_LEDS 1
CRGB leds[NUM_LEDS];
typedef enum { LED_WAITING_WIFI, LED_HEARTBEAT, LED_IDLE } LedState;
LedState currentLedState = LED_WAITING_WIFI;
unsigned long ledStateStart = 0;
// ── Variables globales ────────────────────────────────────
WiFiUDP udp;
HardwareSerial MavSerial(2);
bool wifiConnected = false;
unsigned long lastHeartbeat = 0;
uint8_t mavSeq = 0;
#if MODE_AP
const char* BROADCAST_IP = "192.168.4.255";
#else
const char* BROADCAST_IP = JETSON_IP;
#endif
// ── LED non bloquante ─────────────────────────────────────
void updateLED() {
unsigned long elapsed = millis() - ledStateStart;
switch (currentLedState) {
case LED_WAITING_WIFI:
leds[0] = CRGB((uint8_t)(128 + 127 * sin(elapsed * 0.00628f)), 0, 0);
break;
case LED_HEARTBEAT:
if (elapsed < 80) {
leds[0] = CRGB(0, 255, 0);
} else {
currentLedState = LED_IDLE;
ledStateStart = millis();
}
break;
case LED_IDLE:
leds[0] = CRGB(0, 8, 0);
break;
}
FastLED.show();
}
void setLedState(LedState s) {
currentLedState = s;
ledStateStart = millis();
}
// ── Heartbeat MAVLink ─────────────────────────────────────
void sendMavlinkHeartbeat() {
uint8_t buf[17];
uint8_t len = mavlinkBuildHeartbeat(buf, mavSeq);
udp.beginPacket(BROADCAST_IP, JETSON_PORT_HB);
udp.write(buf, len);
udp.endPacket();
setLedState(LED_HEARTBEAT);
Serial.printf("[HB] seq=%d [", mavSeq - 1);
for (uint8_t i = 0; i < len; i++) {
Serial.printf("%02X", buf[i]);
if (i < len - 1) Serial.print(" ");
}
Serial.println("]");
}
// ── Reconnexion WiFi (mode STA uniquement) ────────────────
void checkWiFi() {
#if !MODE_AP
if (WiFi.status() == WL_CONNECTED) {
if (!wifiConnected) {
wifiConnected = true;
WiFi.setSleep(false);
udp.begin(LOCAL_PORT);
setLedState(LED_IDLE);
Serial.print("WiFi connecte. IP: ");
Serial.println(WiFi.localIP());
}
return;
}
if (wifiConnected) {
wifiConnected = false;
setLedState(LED_WAITING_WIFI);
Serial.println("WiFi perdu, reconnexion...");
}
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
#endif
}
// ── Setup ─────────────────────────────────────────────────
void setup() {
Serial.begin(115200);
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(80);
setLedState(LED_WAITING_WIFI);
Serial.println("\n=== WireClaw ESP32-S3 ===");
#if MODE_AP
Serial.println("Mode: AP (hotspot autonome)");
#else
Serial.println("Mode: STA (connexion Jetson)");
#endif
MavSerial.begin(UART_BAUD, SERIAL_8N1, UART_RX_PIN, UART_TX_PIN);
Serial.printf("UART2: %d baud (RX=%d TX=%d)\n", UART_BAUD, UART_RX_PIN, UART_TX_PIN);
#if MODE_AP
WiFi.mode(WIFI_AP);
WiFi.softAP(AP_SSID, AP_PASSWORD);
delay(500);
wifiConnected = true;
udp.begin(LOCAL_PORT);
setLedState(LED_IDLE);
Serial.printf("Hotspot: %s\n", AP_SSID);
Serial.printf("IP : %s\n", WiFi.softAPIP().toString().c_str());
#else
WiFi.mode(WIFI_STA);
WiFi.setSleep(false);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.printf("Connexion a %s...\n", WIFI_SSID);
#endif
Serial.printf("UDP ecoute: port %d\n", LOCAL_PORT);
delay(500);
if (wifiConnected) sendMavlinkHeartbeat();
}
// ── Loop ──────────────────────────────────────────────────
void loop() {
updateLED();
checkWiFi();
if (wifiConnected && millis() - lastHeartbeat > 1000) {
lastHeartbeat = millis();
sendMavlinkHeartbeat();
}
if (!wifiConnected) return;
// WiFi → UART (Jetson → Pi Zero)
int packetSize = udp.parsePacket();
if (packetSize > 0) {
uint8_t buf[512];
int len = udp.read(buf, sizeof(buf));
if (len > 0) {
MavSerial.write(buf, len);
Serial.printf("[WiFi->UART] %d bytes\n", len);
}
}
// UART → WiFi (Pi Zero → Jetson)
if (MavSerial.available()) {
uint8_t buf[512];
int len = 0;
while (MavSerial.available() && len < (int)sizeof(buf)) {
buf[len++] = MavSerial.read();
}
if (len > 0) {
udp.beginPacket(BROADCAST_IP, JETSON_PORT_MAVSDK);
udp.write(buf, len);
udp.endPacket();
udp.beginPacket(BROADCAST_IP, JETSON_PORT_PYMAV);
udp.write(buf, len);
udp.endPacket();
Serial.printf("[UART->WiFi] %d bytes\n", len);
}
}
}