Fix shifted pictures: non-blocking Nano drawing, frame check, refresh settings

The Nano's periodic full redraw blocked for ~0.8 s. A frame that arrived
meanwhile overflowed its 64-byte serial buffer; lost bytes in a bitmap
shift the rest of the image (picture moved up), and diff frames never
repaired it.

- Nano: all drawing (frames, effects, the dot refresh) runs from update()
  in ~1 ms slices; the refresh pauses while a frame is received or shown
- New commands: C (reply with the image checksum) and I (refresh minutes,
  EEPROM)
- ESP32: NanoMonitor asks for the checksum after frames and sends the whole
  image again on a mismatch, missing answer or Nano restart; counts ERR
  replies. Periodic whole-image resync.
- Settings: "Dot refresh" and "Image resync" intervals (settings v5,
  v4 records are migrated)
- System tab: loop timing, free memory, frame check counts
- test_driver: the real driver against a simulated panel

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-10-16 15:20:26 +02:00
1 parent 58a7cb03c2
commit 090c2bf8ae
45 files changed
+1265 -157

No files matched your search

+25 -4
View File
@@ -3,6 +3,7 @@
#include <Adafruit_GFX.h>
#include "buffer_crc.h"
#include "dot_sequence.h"
#ifdef ESPSERIAL
#include "frame_commands.h"
@@ -29,6 +30,7 @@ private:
#ifdef ESPSERIAL
Stream *stream;
uint32_t _frames = 0; // frames sent (display() and showBitmap() calls that sent something)
#else
uint8_t _active_panel = 255;
uint8_t _active_col = 255;
@@ -49,6 +51,11 @@ private:
uint32_t _step_time = 0; // us per step
uint32_t _next_step = 0; // micros() of the next step
// The full redraw (startRefresh()): one pulse for every dot, column by column, while
// no frame is being received or drawn
bool _refreshing = false;
uint16_t _refresh_pos = 0;
#ifndef FLIPDOT_PLCC_ADAPTER
const uint8_t ENABLE = 7,
ADDR_0 = 3,
@@ -99,8 +106,8 @@ private:
void _flipTo(uint8_t x, uint8_t y, bool on, uint8_t pulses);
void _flipDot(uint8_t x, uint8_t y, uint8_t pulses);
bool _pushDot(void);
void _drawAll(bool all);
void _startEffect(bool all);
void _startEffect(bool all, bool instant);
void _refreshStep(uint32_t start);
#endif
public:
BROSE9323(uint8_t, uint8_t, uint8_t, uint16_t ft = 280);
@@ -111,10 +118,16 @@ public:
// (frame_commands.h); otherwise the whole screen. Returns the characters sent (0:
// nothing changed, nothing sent).
size_t showBitmap(const uint8_t *bitmap, bool changes_only, bool instant = false);
// Frames sent so far (display() and showBitmap() calls that sent something)
uint32_t framesSent(void) const { return _frames; }
// How often the Nano flips every dot again in minutes, 0 = never (I command)
void setRefreshInterval(uint16_t minutes);
#endif
void begin(void);
// Shows the buffer: the changed dots, all with force. instant: without the refresh
// effect (frames of moving content, e.g. scrolling text). ESP32: sends D or D1.
// Nano: never blocks, the dots are flipped from update() (also for instant frames,
// as fast as possible), so the serial port is read while they flip.
void display(bool force = false, bool instant = false);
void drawPixel(int16_t x, int16_t y, uint16_t color);
void fillScreen(uint16_t);
@@ -130,10 +143,15 @@ public:
// Effects run in steps from update(), so call that often (every loop).
void setEffect(uint8_t effect, uint16_t time_ms, uint8_t flags = 0);
#ifndef ESPSERIAL
// Runs the next steps of an effect. Returns busy().
// Flips the next dots of a frame or of the full redraw, at most about 1 ms per call
// (plus one dot), so loop() keeps reading the serial port. Returns busy().
bool update(void);
// An effect is running (paused while a new frame is being drawn into the buffer)
// A frame is being shown (paused while a new frame is being drawn into the buffer)
bool busy(void) const;
// Flip every dot once more (fixes dots that didn't flip), from update() whenever no
// frame is being received or shown. Starts again when one is running.
void startRefresh(void);
bool refreshing(void) const { return _refreshing; }
#endif
#ifndef ESPSERIAL
void printBuffer(void);
@@ -141,5 +159,8 @@ public:
size_t print(const char *ifsh);
// State of a dot as of the last display() call (ESP32: shadow copy for the web UI)
bool shownPixel(int16_t x, int16_t y) const;
// bufferCrc() of the drawn image (Nano: what it received, the C command; ESP32: what
// it sent)
uint16_t crc(void) const { return bufferCrc(_new_buffer, width(), height()); }
};
#endif //BROSE9323_H