Power-cycle-friendly Nano boot and ESP relay handling

Nano / driver:
- No full on/off flip at boot; the first display() after boot is a forced
  full redraw instead, since the physical dot state is unknown
- Send READY when booted

ESP32:
- relayOn() only switches and waits when the relay was off, and waits for
  the Nano's READY (3 s timeout) instead of a fixed 3 s on every frame
- relayOff() ends Serial and releases the TX pin so the idle-high line no
  longer back-powers the Nano through its RX pin
- No display commands are sent while the relay is off
- Switching the relay on cancels a pending power-off countdown

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-10-13 00:12:28 +02:00
1 parent f7ffcf4ab3
commit 6e4d5e152b
8 files changed
+158 -68

No files matched your search

+115 -54
View File
@@ -5,6 +5,60 @@ void toggleLED(void *parameter);
uint32_t power_off_delay;
int triggered_power_off = false;
int turn_hassio_power_off = false;
// Wait until the Nano reports READY after booting, or until NANO_BOOT_TIMEOUT
void waitForNano()
{
char line[16];
uint8_t len = 0;
uint32_t start = millis();
while (millis() - start < NANO_BOOT_TIMEOUT)
{
while (Serial.available())
{
char c = Serial.read();
if (c == '\n')
{
line[len] = '\0';
if (strstr(line, "READY"))
return;
len = 0;
}
else if (len < sizeof(line) - 1)
line[len++] = c;
}
delay(10);
}
}
void relayOn()
{
// cancel a pending power off countdown
triggered_power_off = false;
if (relay_on)
return;
Serial.begin(115200);
// drop old data (e.g. ERR replies) so only a new READY counts
while (Serial.available())
Serial.read();
digitalWrite(relay, LOW);
waitForNano();
relay_on = true;
}
void relayOff()
{
digitalWrite(relay, HIGH);
relay_on = false;
// An idle UART TX line is high and would power the Nano through its RX pin
Serial.flush();
Serial.end();
pinMode(nano_tx, INPUT);
}
void turnPowerOff(uint32_t delay = 30000, bool _turn_hassio_power_off = false)
{
if(triggered_power_off == false)
@@ -25,7 +79,7 @@ void updatePower()
triggered_power_off = false;
// OFF
digitalWrite(relay, HIGH);
relayOff();
if(turn_hassio_power_off)
{
@@ -46,10 +100,13 @@ void onPowerSwitchStateChanged(bool state, HASwitch *s)
if(power == false)
{
display.fillScreen(0);
display.display();
if (relay_on)
{
display.fillScreen(0);
display.display();
delay(1000);
delay(1000);
}
turnPowerOff(0);
@@ -230,9 +287,7 @@ void setup()
otaPanicMode = false;
pinMode(relay, OUTPUT);
digitalWrite(relay, LOW);
delay(4000);
relayOn();
display.begin(&Serial);
// display.setTiming(200);
@@ -379,11 +434,7 @@ void drawRotatedBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, i
void turnPowerOn(bool overide = false)
{
if(power == true || overide == true)
{
// ON
digitalWrite(relay, LOW);
delay(3000);
}
relayOn();
}
void loop()
@@ -434,8 +485,11 @@ void loop()
if(hour <= 7 || (hour >= 23 && minute > 30))
{
display.fillScreen(0);
display.display();
if (relay_on)
{
display.fillScreen(0);
display.display();
}
turnPowerOff();
}
@@ -446,28 +500,32 @@ void loop()
{
turnPowerOn();
int xoffset = 3;
// power can be off (Home Assistant power switch): then nothing is sent
if (relay_on)
{
int xoffset = 3;
display.fillScreen(0);
// display.setTextSize(2);
display.setTextSize(1);
// display.setCursor(7, 3);
display.setCursor(xoffset + 0, 19 - 2);
display.setTextWrap(false);
// display.print(t);
display.fillScreen(0);
// display.setTextSize(2);
display.setTextSize(1);
// display.setCursor(7, 3);
display.setCursor(xoffset + 0, 19 - 2);
display.setTextWrap(false);
// display.print(t);
// if(minute % 2 == 0)
if(hour >= 17 || hour <= 6)
display.drawBitmap(xoffset + 0, 0, yawning_pikachu_bitmap_yawning_pikachu_black_border, 35, 19, 1);
else
display.drawBitmap(xoffset + 0, 0, pikachu_bitmap_pikachu_black_border, 35, 19, 1);
// if(minute % 2 == 0)
if(hour >= 17 || hour <= 6)
display.drawBitmap(xoffset + 0, 0, yawning_pikachu_bitmap_yawning_pikachu_black_border, 35, 19, 1);
else
display.drawBitmap(xoffset + 0, 0, pikachu_bitmap_pikachu_black_border, 35, 19, 1);
display.drawBitmap(xoffset + 35, 0, clock_thought_bubble_bitmap_clock_thought_bubble, 46, 19, 1);
display.drawBitmap(xoffset + 35, 0, clock_thought_bubble_bitmap_clock_thought_bubble, 46, 19, 1);
display.setCursor(xoffset + 43, (19/2) - 5);
display.print(timeStringBuff);
display.setCursor(xoffset + 43, (19/2) - 5);
display.print(timeStringBuff);
display.display();
display.display();
}
}
else
{
@@ -476,34 +534,37 @@ void loop()
}
else
{
display.fillScreen(0);
display.drawBitmap(0, 0, nintendo_bitmap_Nintendo_new, 84, 19, 1);
if (relay_on)
{
display.fillScreen(0);
display.drawBitmap(0, 0, nintendo_bitmap_Nintendo_new, 84, 19, 1);
// display.drawBitmap(0, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
// display.drawBitmap(18, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
// display.drawBitmap(18*2, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
// display.drawBitmap(18*3, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
// display.drawBitmap(18*4, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
// display.drawBitmap(0, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
// display.drawBitmap(18, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
// display.drawBitmap(18*2, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
// display.drawBitmap(18*3, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
// display.drawBitmap(18*4, 0, nintendo_bitmap_panda_inverted, 17, 18, 1);
// drawRotatedBitmap(0, 0, nintendo_bitmap_Nintendo_new, 17, 18, 1, 90);
// drawRotatedBitmap(0, 0, nintendo_bitmap_Nintendo_new, 17, 18, 1, 90);
// Text inside border.
// display.drawBitmap(0, 0, nintendo_bitmap_nintendo_border, 84, 19, 1);
// display.setFont();
// display.setTextSize(1);
// display.setTextWrap(false);
// display.setCursor(5, 4);
// display.print("Nintendo");
// Text inside border.
// display.drawBitmap(0, 0, nintendo_bitmap_nintendo_border, 84, 19, 1);
// display.setFont();
// display.setTextSize(1);
// display.setTextWrap(false);
// display.setCursor(5, 4);
// display.print("Nintendo");
// display.setFont();
// // display.setTextSize(2);
// display.setTextSize(1);
// display.setCursor(0, 0);
// display.setTextWrap(true);
// // display.print(t);
// display.print("Carla & Jan Kragt");
// display.setFont();
// // display.setTextSize(2);
// display.setTextSize(1);
// display.setCursor(0, 0);
// display.setTextWrap(true);
// // display.print(t);
// display.print("Carla & Jan Kragt");
display.display();
display.display();
}
turnPowerOff();
}