firmware: v20 — ESP8266 sin OTA/WiFi, ESP32-CAM mantiene OTA

ESP8266:
- Eliminado completamente WiFi, BearSSL, HTTPClient, ESPhttpUpdate
- Firmware pasa de 375KB a 240KB (sin stack WiFi)
- Sin interferencia de WiFi con analogWrite/PWM de motores
- Se mantiene: waitForReady(), 2 pitidos conexion, watchdog 1.5s
- Solo se actualiza por USB (el hardware cambia poco)

ESP32-CAM:
- OTA sin cambios, version -> 20
- Indicador OTA: 3 x Serial H1/H0 -> 3 flashes LED GPIO4
  (el ESP8266 estaba en waitForReady() y no procesaba Serial)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Natxo1000
2026-06-03 17:09:28 +02:00
parent ba3f927e8c
commit 0ba2f0247c
4 changed files with 47 additions and 148 deletions

View File

@@ -1,22 +1,6 @@
// FW_VERSION solo informativo — este ESP8266 no hace OTA
#define FW_VERSION 19
#include <Servo.h> #include <Servo.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecureBearSSL.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
// ================================================================
// OTA CONFIG
// ================================================================
#define FW_VERSION 19
#define GITEA_HOST "https://git.nacho.myds.me"
#define GITEA_OWNER "Natxo"
#define GITEA_REPO "Arduino-Car"
#define GITEA_BRANCH "main"
#define GITEA_TOKEN "d3b0b32eb0311c9b4ef569f22c8392c373ff3f9f"
// WiFi — mismas credenciales que usa el ESP32-CAM
#define WIFI_SSID "Natxo"
#define WIFI_PASS "Suprak1llm1ndS23S"
// ================================================================
// ==== Pines motores ==== // ==== Pines motores ====
const int ENA = D1; const int ENA = D1;
@@ -30,7 +14,7 @@ const int IN4 = D0;
const int SERVO_X_PIN = D3; const int SERVO_X_PIN = D3;
const int SERVO_Y_PIN = D4; const int SERVO_Y_PIN = D4;
// ==== Pines buzzer ==== // ==== Pin buzzer ====
const int buzzerPin = D8; const int buzzerPin = D8;
Servo servoX; Servo servoX;
@@ -40,81 +24,14 @@ int currentSpeed = 150;
int posX = 90; int posX = 90;
int posY = 90; int posY = 90;
// ── OTA: comprueba si hay firmware nuevo y actualiza si hace falta ── // ── Handshake: espera "GO\n" del ESP32-CAM antes de procesar comandos ──
static void checkOTA() { // Descarta la basura del bootloader ROM del ESP32 (115200 baud a 9600).
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
int tries = 0;
while (WiFi.status() != WL_CONNECTED && tries < 20) {
delay(500);
tries++;
}
if (WiFi.status() != WL_CONNECTED) {
WiFi.disconnect();
WiFi.mode(WIFI_OFF);
return; // sin WiFi, arranque normal
}
String base = String(GITEA_HOST) + "/" + GITEA_OWNER + "/" + GITEA_REPO
+ "/raw/branch/" + GITEA_BRANCH + "/firmware/esp8266/";
String token = "?token=" + String(GITEA_TOKEN);
// ── Bloque de scope: libera el cliente BearSSL antes de la descarga ──
// BearSSL ocupa ~20KB de heap. Si los dos clientes coexisten el heap
// se agota y ESPhttpUpdate falla silenciosamente.
// Scope block: destruye el cliente BearSSL antes de crear el de descarga
// Libera ~20KB de heap (BearSSL con buffers por defecto) para la descarga
int remoteVer = 0;
{
BearSSL::WiFiClientSecure client;
client.setInsecure();
HTTPClient http;
http.begin(client, base + "version.txt" + token);
http.setTimeout(10000);
int code = http.GET();
if (code == HTTP_CODE_OK) {
String body = http.getString();
body.trim();
remoteVer = body.toInt();
}
http.end();
} // client destruido aquí — heap recuperado
if (remoteVer > FW_VERSION) {
// 4 pitidos = ESP8266 actualizando
pinMode(buzzerPin, OUTPUT);
for (int i = 0; i < 4; i++) {
digitalWrite(buzzerPin, HIGH);
delay(150);
digitalWrite(buzzerPin, LOW);
delay(150);
}
delay(300);
// Fuerza pines de motor a LOW antes del flash para minimizar glitch de GPIO
analogWrite(ENA, 0); analogWrite(ENB, 0);
digitalWrite(IN1, LOW); digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW); digitalWrite(IN4, LOW);
BearSSL::WiFiClientSecure updateClient;
updateClient.setInsecure();
ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
ESPhttpUpdate.rebootOnUpdate(true);
ESPhttpUpdate.update(updateClient, base + "firmware.bin" + token);
// Si llega aquí, el update falló — continúa arranque normal
}
WiFi.disconnect();
WiFi.mode(WIFI_OFF); // apaga radio para ahorrar energía
}
bool espReady = false; bool espReady = false;
void waitForReady() { void waitForReady() {
delay(300); delay(300);
while (Serial.available()) Serial.read(); while (Serial.available()) Serial.read(); // descarta basura del bootloader
unsigned long timeout = millis() + 90000UL; unsigned long timeout = millis() + 90000UL;
String buf = ""; String buf = "";
while (millis() < timeout) { while (millis() < timeout) {
@@ -127,7 +44,7 @@ void waitForReady() {
delay(50); delay(50);
while (Serial.available()) Serial.read(); while (Serial.available()) Serial.read();
espReady = true; espReady = true;
// 2 pitidos: conexión entre placas lista // 2 pitidos: conexión entre placas establecida
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
digitalWrite(buzzerPin, HIGH); delay(180); digitalWrite(buzzerPin, HIGH); delay(180);
digitalWrite(buzzerPin, LOW); delay(180); digitalWrite(buzzerPin, LOW); delay(180);
@@ -145,19 +62,16 @@ void waitForReady() {
} }
void setup() { void setup() {
// 1. Pines y parada inmediata // 1. Pines de motor y parada inmediata
pinMode(ENA, OUTPUT); pinMode(ENB, OUTPUT); pinMode(ENA, OUTPUT); pinMode(ENB, OUTPUT);
pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT); pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT); pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT);
stopMotors(); stopMotors();
// 2. Serial temprano (el ESP32 puede mandar el pitido de OTA) // 2. Serial
Serial.begin(9600); Serial.begin(9600);
// 3. OTA via WiFi // 3. Periféricos
checkOTA();
// 4. Resto de periféricos
analogWriteRange(255); analogWriteRange(255);
analogWriteFreq(1000); analogWriteFreq(1000);
servoX.attach(SERVO_X_PIN); servoX.attach(SERVO_X_PIN);
@@ -166,85 +80,71 @@ void setup() {
servoX.write(posX); servoX.write(posX);
servoY.write(posY); servoY.write(posY);
// 5. Esperar "GO\n" del ESP32-CAM antes de procesar comandos // 4. Esperar señal del ESP32-CAM
waitForReady(); waitForReady();
} }
// Watchdog ligero en ESP8266: si no llega comando de movimiento en 1.5s → para // Watchdog: si no llega comando de movimiento en 1.5s → para motores
// Evita que el coche siga moviéndose si el ESP32 se cuelga o reinicia
unsigned long lastMoveMs = 0; unsigned long lastMoveMs = 0;
bool motorRunning = false; bool motorRunning = false;
void loop() { void loop() {
// Watchdog: sin comandos durante 1.5s → para motores
if (motorRunning && (millis() - lastMoveMs > 1500)) { if (motorRunning && (millis() - lastMoveMs > 1500)) {
stopMotors(); stopMotors();
motorRunning = false; motorRunning = false;
} }
if (Serial.available()) { if (!Serial.available()) return;
char c = Serial.read(); char c = Serial.read();
// Ignorar todo hasta que el handshake esté completo if (!espReady) return;
if (!espReady) return;
if (c == 'F') { forward(); lastMoveMs = millis(); motorRunning = true; } if (c == 'F') { forward(); lastMoveMs = millis(); motorRunning = true; }
else if (c == 'B') { backward(); lastMoveMs = millis(); motorRunning = true; } else if (c == 'B') { backward(); lastMoveMs = millis(); motorRunning = true; }
else if (c == 'L') { left(); lastMoveMs = millis(); motorRunning = true; } else if (c == 'L') { left(); lastMoveMs = millis(); motorRunning = true; }
else if (c == 'R') { right(); lastMoveMs = millis(); motorRunning = true; } else if (c == 'R') { right(); lastMoveMs = millis(); motorRunning = true; }
else if (c == 'S') { stopMotors(); motorRunning = false; } else if (c == 'S') { stopMotors(); motorRunning = false; }
else if (c == 'V') {
else if (c == 'V') { int val = Serial.parseInt();
int val = Serial.parseInt(); if (val >= 0 && val <= 255) currentSpeed = val;
if (val >= 0 && val <= 255) currentSpeed = val; }
} else if (c == 'X') {
else if (c == 'X') { int val = Serial.parseInt();
int val = Serial.parseInt(); if (val >= 0 && val <= 180) { posX = val; servoX.write(posX); }
if (val >= 0 && val <= 180) { posX = val; servoX.write(posX); } }
} else if (c == 'Y') {
else if (c == 'Y') { int val = Serial.parseInt();
int val = Serial.parseInt(); if (val >= 0 && val <= 180) { posY = val; servoY.write(posY); }
if (val >= 0 && val <= 180) { posY = val; servoY.write(posY); } }
} else if (c == 'H') {
else if (c == 'H') { int val = Serial.parseInt();
int val = Serial.parseInt(); digitalWrite(buzzerPin, val > 0 ? HIGH : LOW);
digitalWrite(buzzerPin, val > 0 ? HIGH : LOW);
}
} }
} }
// ==== Funciones de movimiento ==== // ==== Funciones de movimiento ====
void forward() { void forward() {
analogWrite(ENA, currentSpeed); analogWrite(ENA, currentSpeed); analogWrite(ENB, currentSpeed);
analogWrite(ENB, currentSpeed);
digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH);
digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH);
} }
void backward() { void backward() {
analogWrite(ENA, currentSpeed); analogWrite(ENA, currentSpeed); analogWrite(ENB, currentSpeed);
analogWrite(ENB, currentSpeed);
digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW);
} }
void left() { void left() {
analogWrite(ENA, currentSpeed); analogWrite(ENA, currentSpeed); analogWrite(ENB, currentSpeed);
analogWrite(ENB, currentSpeed);
digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH);
digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW);
} }
void right() { void right() {
analogWrite(ENA, currentSpeed); analogWrite(ENA, currentSpeed); analogWrite(ENB, currentSpeed);
analogWrite(ENB, currentSpeed);
digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH);
} }
void stopMotors() { void stopMotors() {
analogWrite(ENA, 0); analogWrite(ENA, 0); analogWrite(ENB, 0);
analogWrite(ENB, 0);
digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(IN1, LOW); digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW);
} }

View File

@@ -10,7 +10,7 @@
// ================================================================ // ================================================================
// OTA CONFIG // OTA CONFIG
// ================================================================ // ================================================================
#define FW_VERSION 19 #define FW_VERSION 20
#define GITEA_HOST "https://git.nacho.myds.me" #define GITEA_HOST "https://git.nacho.myds.me"
#define GITEA_OWNER "Natxo" #define GITEA_OWNER "Natxo"
#define GITEA_REPO "Arduino-Car" #define GITEA_REPO "Arduino-Car"
@@ -71,12 +71,11 @@ static void checkOTA() {
int remoteVer = body.toInt(); int remoteVer = body.toInt();
if (remoteVer <= FW_VERSION) return; // ya tenemos la última versión if (remoteVer <= FW_VERSION) return; // ya tenemos la última versión
// 3 pitidos = ESP32-CAM actualizando (via Serial al ESP8266) // 3 flashes LED = ESP32-CAM actualizando
// (ESP8266 en waitForReady(), no puede recibir Serial en este momento)
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
Serial.print("H1"); ledcWrite(LED_GPIO_NUM, 255); delay(150);
delay(150); ledcWrite(LED_GPIO_NUM, 0); delay(150);
Serial.print("H0");
delay(150);
} }
delay(300); delay(300);

Binary file not shown.

View File

@@ -1 +1 @@
19 20