Public Access
Flash the Nano over WiFi through the ESP32
- ESP32: NanoFlashBridge on TCP port 2323. A connection power cycles the Nano into its bootloader and bridges TCP <-> Serial2 at 57600 baud; the clock pauses during the session, 10 s idle timeout - tools/nano_net_upload.py: STK500v1 uploader over TCP (standard library only), with sync retries, write and read-back verification. avrdude 6.3 net: ports are broken on Windows, so avrdude is not used - New nano_ota environment: pio run -e nano_ota -t upload - README: usage, requirements, troubleshooting; TODO for hardware test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8415dee347
commit
bea5b9daa1
10 files changed
+438
-7
No files matched your search
@@ -14,6 +14,7 @@ debug output. Wiring is described in the [main README](../../README.md#wiring-be
|
||||
| `main.cpp` | setup, OTA, clock / power logic, Home Assistant callbacks |
|
||||
| `libs.h` | includes and global objects (display, MQTT, WiFi, pins, timezone) |
|
||||
| `bitmaps.h` | 1-bit bitmaps (Pikachu, thought bubble, Nintendo logo, ...) |
|
||||
| `nano_flash_bridge.h/.cpp` | WiFi to serial bridge for flashing the Nano over WiFi (TCP port 2323) |
|
||||
| `secrets.example.h` | template for `secrets.h` (WiFi and MQTT credentials, **gitignored**) |
|
||||
|
||||
## Features
|
||||
@@ -34,6 +35,9 @@ debug output. Wiring is described in the [main README](../../README.md#wiring-be
|
||||
- **Off:** the ESP stops sending and releases its TX pin (`Serial.end()` and the pin set
|
||||
to input). An idle UART line is high and would otherwise power the Nano through its RX
|
||||
pin.
|
||||
- **Nano flash bridge:** `pio run -e nano_ota -t upload` flashes the Nano through the ESP32.
|
||||
While a flash session runs, the clock is paused. See the
|
||||
[main README](../../README.md#flashing-the-nano-over-wifi).
|
||||
- **Status LED** (built-in) blinks fast when WiFi is down and slowly when connected. It is
|
||||
off at night or when the display is powered off.
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <Arduino.h>
|
||||
#include "BROSE9323.h"
|
||||
#include "bitmaps.h"
|
||||
#include "nano_flash_bridge.h"
|
||||
|
||||
// WiFi / MQTT credentials (gitignored, copy secrets.example.h -> secrets.h)
|
||||
#if __has_include("secrets.h")
|
||||
@@ -52,6 +53,12 @@ const int nano_tx = 17; // UART2 TX, wired to the Nano RX
|
||||
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;
|
||||
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
|
||||
const uint32_t NANO_POWER_OFF_TIME = 1000; // relay off time to make sure the Nano resets
|
||||
|
||||
int hour = 0;
|
||||
int minute = 0;
|
||||
|
||||
+31
-1
@@ -39,7 +39,7 @@ void relayOn()
|
||||
if (relay_on)
|
||||
return;
|
||||
|
||||
nanoSerial.begin(115200, SERIAL_8N1, nano_rx, nano_tx);
|
||||
nanoSerial.begin(NANO_BAUD, SERIAL_8N1, nano_rx, nano_tx);
|
||||
// drop old data (e.g. ERR replies) so only a new READY counts
|
||||
while (nanoSerial.available())
|
||||
nanoSerial.read();
|
||||
@@ -59,6 +59,30 @@ void relayOff()
|
||||
nanoSerial.end();
|
||||
pinMode(nano_tx, INPUT);
|
||||
}
|
||||
|
||||
// Flash session started: power cycle 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()
|
||||
{
|
||||
if (relay_on)
|
||||
relayOff();
|
||||
delay(NANO_POWER_OFF_TIME);
|
||||
|
||||
nanoSerial.begin(NANO_BOOTLOADER_BAUD, SERIAL_8N1, nano_rx, nano_tx);
|
||||
while (nanoSerial.available())
|
||||
nanoSerial.read();
|
||||
digitalWrite(relay, LOW);
|
||||
}
|
||||
|
||||
// Flash session ended: switch off, the clock powers the Nano on again with the new firmware
|
||||
void endNanoFlash()
|
||||
{
|
||||
relayOff();
|
||||
// skip the frames that were missed while flashing
|
||||
next_display = millis();
|
||||
}
|
||||
|
||||
NanoFlashBridge nanoFlashBridge(NANO_FLASH_PORT, nanoSerial, startNanoFlash, endNanoFlash);
|
||||
void turnPowerOff(uint32_t delay = 30000, bool _turn_hassio_power_off = false)
|
||||
{
|
||||
if(triggered_power_off == false)
|
||||
@@ -269,6 +293,7 @@ void setup()
|
||||
});
|
||||
|
||||
ArduinoOTA.begin();
|
||||
nanoFlashBridge.begin();
|
||||
|
||||
do
|
||||
{
|
||||
@@ -441,6 +466,11 @@ void loop()
|
||||
{
|
||||
ArduinoOTA.handle();
|
||||
mqtt.loop();
|
||||
|
||||
// While the Nano is being flashed, nothing else may talk to it
|
||||
if (nanoFlashBridge.handle())
|
||||
return;
|
||||
|
||||
updatePower();
|
||||
|
||||
wifiStatus = WiFi.waitForConnectResult() == WL_CONNECTED;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "nano_flash_bridge.h"
|
||||
|
||||
NanoFlashBridge::NanoFlashBridge(uint16_t port, HardwareSerial &serial, void (*onStart)(), void (*onEnd)())
|
||||
: _server(port), _serial(serial), _onStart(onStart), _onEnd(onEnd)
|
||||
{
|
||||
}
|
||||
|
||||
void NanoFlashBridge::begin()
|
||||
{
|
||||
_server.begin();
|
||||
_server.setNoDelay(true);
|
||||
}
|
||||
|
||||
bool NanoFlashBridge::handle()
|
||||
{
|
||||
if (!_active)
|
||||
{
|
||||
WiFiClient client = _server.available();
|
||||
if (!client)
|
||||
return false;
|
||||
|
||||
_client = client;
|
||||
_client.setNoDelay(true);
|
||||
_active = true;
|
||||
_onStart();
|
||||
_last_activity = millis();
|
||||
}
|
||||
|
||||
if (!_client.connected() || millis() - _last_activity > IDLE_TIMEOUT)
|
||||
{
|
||||
_end();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t buf[256];
|
||||
|
||||
// PC -> Nano
|
||||
int n = _client.available();
|
||||
if (n > 0)
|
||||
{
|
||||
n = _client.read(buf, min(n, (int)sizeof(buf)));
|
||||
if (n > 0)
|
||||
{
|
||||
_serial.write(buf, n);
|
||||
_last_activity = millis();
|
||||
}
|
||||
}
|
||||
|
||||
// Nano -> PC
|
||||
n = _serial.available();
|
||||
if (n > 0)
|
||||
{
|
||||
n = _serial.readBytes(buf, min(n, (int)sizeof(buf)));
|
||||
_client.write(buf, n);
|
||||
_last_activity = millis();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NanoFlashBridge::_end()
|
||||
{
|
||||
_client.stop();
|
||||
_active = false;
|
||||
_onEnd();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
// 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
|
||||
// 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)());
|
||||
|
||||
void begin();
|
||||
|
||||
// Call from loop(). Returns true while a flash session is running; the caller must not
|
||||
// send anything to the Nano then.
|
||||
bool handle();
|
||||
|
||||
private:
|
||||
// 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 (*_onEnd)();
|
||||
bool _active = false;
|
||||
uint32_t _last_activity = 0;
|
||||
|
||||
void _end();
|
||||
};
|
||||
Reference in new issue
Block a user