Files
mangavalkandClaude Opus 5.5 090c2bf8ae 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>
2026-10-16 15:20:26 +02:00

113 lines
2.8 KiB
C++

// Just enough Arduino and Adafruit GFX to run the real BROSE9323 driver (Nano build) on
// the PC, with a simulated panel: test/test_driver. The panel decodes the pin levels at
// each strobe (ENABLE low) into the dot that flips, like the Brose panel electronics.
#pragma once
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#define A0 14
#define A1 15
#define A2 16
#define A3 17
#define A4 18
#define A5 19
#define OUTPUT 1
// Every micros() call takes a microsecond, so loops that wait for the time end
inline uint32_t fake_micros = 0;
inline uint32_t micros() { return ++fake_micros; }
inline uint32_t millis() { return fake_micros / 1000; }
inline void delayMicroseconds(unsigned int us) { fake_micros += us; }
inline void pinMode(uint8_t, uint8_t) {}
struct FakePanel
{
static const int W = 84, H = 19, PANEL_W = 28;
bool dot[W][H] = {};
int flips[W][H] = {}; // strobes per dot
int strobes = 0;
bool pins[20] = {};
void clearCounts()
{
memset(flips, 0, sizeof(flips));
strobes = 0;
}
void write(uint8_t pin, bool level)
{
pins[pin] = level;
if (pin == 7 && !level) // ENABLE low: flip the addressed dot
strobe();
}
void strobe()
{
int panel = pins[3] | pins[5] << 1 | pins[6] << 2;
int col_code = pins[8] | pins[10] << 1 | pins[9] << 2 | pins[11] << 3 | pins[12] << 4;
int row_code = pins[A3] | pins[A4] << 1 | pins[A2] << 2 | pins[A0] << 3 | pins[A1] << 4;
int col = -1, row = -1;
for (int c = 0; c < PANEL_W; c++)
if (c + 1 + c / 7 == col_code)
col = c;
for (int r = 0; r < H; r++)
if (r + r / 7 == row_code)
row = r;
if (col < 0 || row < 0 || panel > 2)
{
fprintf(stderr, "bad address: panel %d col code %d row code %d\n", panel, col_code, row_code);
abort();
}
int x = panel * PANEL_W + col;
dot[x][row] = !pins[4]; // COL_DATA low sets the dot (yellow)
flips[x][row]++;
strobes++;
}
};
inline FakePanel panel;
inline void digitalWrite(uint8_t pin, int level) { panel.write(pin, level != 0); }
struct FakeSerialOut
{
void write(char) {}
void println() {}
};
inline FakeSerialOut Serial;
// Only what BROSE9323::print() uses
class String
{
public:
String(const char *s) : _s(s) {}
void replace(const char *from, const char *to)
{
size_t pos = 0, n = strlen(from);
while ((pos = _s.find(from, pos)) != std::string::npos)
{
_s.replace(pos, n, to);
pos += strlen(to);
}
}
const char *c_str() const { return _s.c_str(); }
private:
std::string _s;
};
class Adafruit_GFX
{
public:
Adafruit_GFX(int16_t w, int16_t h) : _width(w), _height(h) {}
virtual ~Adafruit_GFX() {}
virtual void drawPixel(int16_t x, int16_t y, uint16_t color) = 0;
int16_t width() const { return _width; }
int16_t height() const { return _height; }
size_t print(const char *s) { return strlen(s); }
protected:
int16_t _width, _height;
};