Fix dense images on the Nano and the off image in slow mode

- Nano: B data is drawn fast enough for the serial line. A hex digit arrives
  every 87 us; with a division and four drawPixel() calls per digit a lit
  digit took about 105 us, so dense frames (all yellow, the panda bitmap)
  overflowed the 64 byte receive buffer: frames not shown or wrong, "Line
  too long", frame check repairs that failed the same way. Now the parser
  counts columns and rows and BROSE9323::drawDots() sets the 4 dots
  (about 20 us).
- ESP32: Power off and the start of the night draw the off image also when
  slow mode had the power off already (it powers on for it, once)
- Tests: drawDots() against drawPixel(), off image in slow mode
- Version 2.19.3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-10-22 00:24:14 +02:00
1 parent 41af2944bc
commit 7a28e34380
13 files changed
+130 -17

No files matched your search

+1 -1
View File
@@ -118,7 +118,7 @@ const uint32_t SETUP_AP_DELAY = 20000; // open the AP when WiFi fails thi
const uint32_t WIFI_CONNECT_TIMEOUT = 30000;
const uint32_t WIFI_RESTART_TIME = 10UL * 60 * 1000;
#define FIRMWARE_VERSION "2.19.2"
#define FIRMWARE_VERSION "2.19.3"
// Home Assistant device id (do not change: Home Assistant's entities are linked to it)
const uint8_t HA_DEVICE_ID[] = {0x34, 0x86, 0x5d, 0x3a, 0x08, 0x28};
+6 -2
View File
@@ -103,8 +103,9 @@ void DisplayController::update(uint32_t now, bool time_valid, const LocalTime &t
if (!_power_enabled)
{
if (_power.isOn())
if (_power.isOn() || _off_needed)
{
_ensureOn();
_showOnce(Screen::Off);
_off_timer.request(now, CLEAR_BEFORE_OFF_TIME + _effect_time);
}
@@ -129,8 +130,9 @@ void DisplayController::update(uint32_t now, bool time_valid, const LocalTime &t
case ScheduleMode::Night:
_slow_last = -1; // the morning starts with an update
_slow_keep = false;
if (_power.isOn())
if (_power.isOn() || _off_needed)
{
_ensureOn();
_showOnce(Screen::Off);
_off_timer.request(now, POWER_OFF_DELAY);
}
@@ -209,6 +211,7 @@ void DisplayController::_showDynamic(uint32_t now, const LocalTime &time, bool n
_dynamic_due = now + wait;
}
_screen = screen;
_off_needed = true;
_shown_hour = time.hour;
_shown_minute = time.minute;
_shown_revision = _content_revision;
@@ -232,5 +235,6 @@ void DisplayController::_showOnce(Screen screen)
break;
}
_screen = screen;
_off_needed = screen != Screen::Off;
_shown_revision = _content_revision;
}
+3
View File
@@ -109,6 +109,9 @@ private:
bool _power_enabled = true;
Screen _screen = Screen::Unknown;
// The dots show a frame other than the off screen, also while the power is off (slow
// mode): Power off and night power on to draw the off screen
bool _off_needed = false;
int _shown_hour = -1;
int _shown_minute = -1;
uint32_t _dynamic_due = 0; // millis() when a moving dynamic page is drawn again
+2 -1
View File
@@ -49,7 +49,8 @@ Arduino core, EEPROM and the display driver are replaced by fakes in
- **Nothing blocks.** Every frame (also instant ones, `D1`) is flipped from `update()` in
slices of about 1 ms, so `loop()` keeps reading the serial port. A blocking redraw used
to overflow the 64-byte receive buffer; the lost bytes shifted the rest of a bitmap
(the picture moved up and left) and nothing repaired it.
(the picture moved up and left) and nothing repaired it. Bitmap data is drawn as fast as
it arrives too (one hex digit every 87 µs); dense images used to be slower than that.
- **Every 2 minutes** (`I` command, stored in EEPROM, 0 = never) it flips every dot once
more, with one pulse, to fix dots that got stuck. This also runs in slices, only while
no frame is being received or shown, and continues where it was after a frame.
+11 -11
View File
@@ -182,28 +182,28 @@ bool CommandParser::_bitmapStart()
_error(F("Invalid bitmap height (0)"));
return false;
}
_bitmap_nibble = 0;
_bitmap_col = 0;
_bitmap_row = 0;
_bitmap_active = true;
return true;
}
// Runs for every hex digit while the line arrives (one every 87 us at 115200 baud), so it
// must be quick: no divisions, and drawDots() instead of four drawPixel() calls. Slower
// than that, a dense line (all yellow) overflowed the 64 byte receive buffer.
void CommandParser::_bitmapData(char c)
{
if (!isHex(c))
return;
uint16_t pixel = _bitmap_nibble * 4;
if (pixel >= (uint16_t)_bitmap_w * _bitmap_h)
if (_bitmap_row >= _bitmap_h)
return; // more data than the bitmap size, ignore
uint8_t b = hex2dec(c);
for (uint8_t i = 0; i < 4; i++)
_display.drawDots(_bitmap_x + _bitmap_col, _bitmap_y + _bitmap_row, hex2dec(c), _bitmap_color);
_bitmap_col += 4;
if (_bitmap_col >= _bitmap_w)
{
if (b & (0x8 >> i))
{
uint16_t p = pixel + i;
_display.drawPixel(_bitmap_x + p % _bitmap_w, _bitmap_y + p / _bitmap_w, _bitmap_color);
}
_bitmap_col = 0;
_bitmap_row++;
}
_bitmap_nibble++;
}
// Fc (c = 0/1 color)
+1 -1
View File
@@ -33,7 +33,7 @@ private:
bool _bitmap_active = false;
uint8_t _bitmap_x, _bitmap_y, _bitmap_w, _bitmap_h;
bool _bitmap_color;
uint16_t _bitmap_nibble;
uint8_t _bitmap_col, _bitmap_row; // where the next hex digit goes, in the bitmap
void _runLine();
void _error(const __FlashStringHelper *msg);