Public Access
Support both Nano bootloaders for flashing over WiFi
- Uploader sends 'FLASH <baud>' first; the ESP32 restarts the Nano and bridges at that baud rate, then answers OK - --baud=auto (default) tries 57600 (old ATmegaBOOT) then 115200 (Optiboot) - Optional nano_reset GPIO (NPN to Nano RESET) pulses reset after power on, needed for Optiboot which skips the bootloader after a power cycle - Clear errors for outdated ESP32 firmware and missing wires - README: bootloader table, reset wire, troubleshooting Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bea5b9daa1
commit
cefb76c1ad
8 files changed
+220
-77
No files matched your search
+6
-1
@@ -50,6 +50,9 @@ const int relay = 13; // Pin of the relay, powers the panels and the Nano (LOW =
|
||||
HardwareSerial &nanoSerial = Serial2;
|
||||
const int nano_rx = 16; // UART2 RX, wired to the Nano TX (through a 5V -> 3.3V divider)
|
||||
const int nano_tx = 17; // UART2 TX, wired to the Nano RX
|
||||
// Optional: GPIO that resets the Nano through an NPN transistor (HIGH = reset), -1 = not wired.
|
||||
// Needed to flash a Nano with the Optiboot bootloader, which doesn't start after a power cycle.
|
||||
const int nano_reset = -1;
|
||||
bool relay_on = false;
|
||||
// Max wait for the Nano's READY after power on (also the wait when Nano TX is not wired to our RX)
|
||||
const uint32_t NANO_BOOT_TIMEOUT = 3000;
|
||||
@@ -57,8 +60,10 @@ const uint32_t NANO_BAUD = 115200;
|
||||
|
||||
// Flashing the Nano over WiFi (see README.md, pio run -e nano_ota -t upload)
|
||||
const uint16_t NANO_FLASH_PORT = 2323;
|
||||
const uint32_t NANO_BOOTLOADER_BAUD = 57600; // old Nano bootloader
|
||||
// The bootloader baud rate is chosen by the uploader (57600 old bootloader, 115200 Optiboot)
|
||||
const uint32_t NANO_POWER_OFF_TIME = 1000; // relay off time to make sure the Nano resets
|
||||
const uint32_t NANO_RESET_DELAY = 200; // after power on, before the reset pulse
|
||||
const uint32_t NANO_RESET_PULSE = 50;
|
||||
|
||||
int hour = 0;
|
||||
int minute = 0;
|
||||
|
||||
+20
-4
@@ -60,18 +60,29 @@ void relayOff()
|
||||
pinMode(nano_tx, INPUT);
|
||||
}
|
||||
|
||||
// Flash session started: power cycle the Nano so its bootloader runs, and talk to the
|
||||
// Flash session started: restart the Nano so its bootloader runs, and talk to the
|
||||
// bootloader at its baud rate. The PC uploader keeps sending sync bytes until it answers.
|
||||
void startNanoFlash()
|
||||
// The old bootloader also starts after a power cycle; Optiboot only after a reset pulse.
|
||||
void startNanoFlash(uint32_t baud)
|
||||
{
|
||||
if (relay_on)
|
||||
relayOff();
|
||||
delay(NANO_POWER_OFF_TIME);
|
||||
|
||||
nanoSerial.begin(NANO_BOOTLOADER_BAUD, SERIAL_8N1, nano_rx, nano_tx);
|
||||
nanoSerial.begin(baud, SERIAL_8N1, nano_rx, nano_tx);
|
||||
digitalWrite(relay, LOW);
|
||||
|
||||
if (nano_reset >= 0)
|
||||
{
|
||||
delay(NANO_RESET_DELAY);
|
||||
digitalWrite(nano_reset, HIGH);
|
||||
delay(NANO_RESET_PULSE);
|
||||
digitalWrite(nano_reset, LOW);
|
||||
}
|
||||
|
||||
// drop anything the Nano sent before its bootloader started
|
||||
while (nanoSerial.available())
|
||||
nanoSerial.read();
|
||||
digitalWrite(relay, LOW);
|
||||
}
|
||||
|
||||
// Flash session ended: switch off, the clock powers the Nano on again with the new firmware
|
||||
@@ -312,6 +323,11 @@ void setup()
|
||||
otaPanicMode = false;
|
||||
|
||||
pinMode(relay, OUTPUT);
|
||||
if (nano_reset >= 0)
|
||||
{
|
||||
digitalWrite(nano_reset, LOW);
|
||||
pinMode(nano_reset, OUTPUT);
|
||||
}
|
||||
relayOn();
|
||||
|
||||
display.begin(&nanoSerial);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "nano_flash_bridge.h"
|
||||
|
||||
NanoFlashBridge::NanoFlashBridge(uint16_t port, HardwareSerial &serial, void (*onStart)(), void (*onEnd)())
|
||||
NanoFlashBridge::NanoFlashBridge(uint16_t port, HardwareSerial &serial, void (*onStart)(uint32_t baud), void (*onEnd)())
|
||||
: _server(port), _serial(serial), _onStart(onStart), _onEnd(onEnd)
|
||||
{
|
||||
}
|
||||
@@ -19,10 +19,19 @@ bool NanoFlashBridge::handle()
|
||||
if (!client)
|
||||
return false;
|
||||
|
||||
uint32_t baud = _readHeader(client);
|
||||
if (!baud)
|
||||
{
|
||||
client.print("ERR expected FLASH <baud>\n");
|
||||
client.stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
_client = client;
|
||||
_client.setNoDelay(true);
|
||||
_active = true;
|
||||
_onStart();
|
||||
_onStart(baud);
|
||||
_client.print("OK\n");
|
||||
_last_activity = millis();
|
||||
}
|
||||
|
||||
@@ -58,6 +67,34 @@ bool NanoFlashBridge::handle()
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read the "FLASH <baud>" line. Returns the baud rate, or 0 when it is missing or invalid.
|
||||
uint32_t NanoFlashBridge::_readHeader(WiFiClient &client)
|
||||
{
|
||||
char line[32];
|
||||
uint8_t len = 0;
|
||||
uint32_t start = millis();
|
||||
while (millis() - start < HEADER_TIMEOUT && client.connected())
|
||||
{
|
||||
if (!client.available())
|
||||
{
|
||||
delay(1);
|
||||
continue;
|
||||
}
|
||||
char c = client.read();
|
||||
if (c == '\n')
|
||||
{
|
||||
line[len] = '\0';
|
||||
unsigned long baud;
|
||||
if (sscanf(line, "FLASH %lu", &baud) == 1 && baud >= 9600 && baud <= 1000000)
|
||||
return baud;
|
||||
return 0;
|
||||
}
|
||||
if (c != '\r' && len < sizeof(line) - 1)
|
||||
line[len++] = c;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NanoFlashBridge::_end()
|
||||
{
|
||||
_client.stop();
|
||||
|
||||
@@ -5,14 +5,16 @@
|
||||
|
||||
// WiFi to serial bridge for flashing the Nano from the PC (pio run -e nano_ota -t upload).
|
||||
//
|
||||
// When a TCP client connects, onStart() is called to restart the Nano into its bootloader.
|
||||
// After that, bytes are copied between the TCP connection and the Nano's serial port
|
||||
// The uploader (tools/nano_net_upload.py) connects and first sends one line:
|
||||
// FLASH <baud>\n
|
||||
// with the baud rate of the Nano's bootloader (57600 old bootloader, 115200 Optiboot).
|
||||
// onStart(baud) then restarts the Nano into its bootloader, the bridge answers "OK\n",
|
||||
// and from then on bytes are copied between the TCP connection and the Nano's serial port
|
||||
// until the client disconnects or is idle too long. onEnd() is then called.
|
||||
// The uploader on the PC side is tools/nano_net_upload.py.
|
||||
class NanoFlashBridge
|
||||
{
|
||||
public:
|
||||
NanoFlashBridge(uint16_t port, HardwareSerial &serial, void (*onStart)(), void (*onEnd)());
|
||||
NanoFlashBridge(uint16_t port, HardwareSerial &serial, void (*onStart)(uint32_t baud), void (*onEnd)());
|
||||
|
||||
void begin();
|
||||
|
||||
@@ -21,16 +23,19 @@ public:
|
||||
bool handle();
|
||||
|
||||
private:
|
||||
// Max wait for the FLASH line after connecting
|
||||
static const uint32_t HEADER_TIMEOUT = 2000;
|
||||
// Give up when no data moves in either direction for this long
|
||||
static const uint32_t IDLE_TIMEOUT = 10000;
|
||||
|
||||
WiFiServer _server;
|
||||
WiFiClient _client;
|
||||
HardwareSerial &_serial;
|
||||
void (*_onStart)();
|
||||
void (*_onStart)(uint32_t baud);
|
||||
void (*_onEnd)();
|
||||
bool _active = false;
|
||||
uint32_t _last_activity = 0;
|
||||
|
||||
uint32_t _readHeader(WiFiClient &client);
|
||||
void _end();
|
||||
};
|
||||
Reference in new issue
Block a user