2026-10-12 23:55:15 +02:00
|
|
|
#ifndef BROSE9323_H
|
|
|
|
|
#define BROSE9323_H
|
|
|
|
|
|
|
|
|
|
#include <Adafruit_GFX.h>
|
|
|
|
|
|
2026-10-15 01:29:01 +02:00
|
|
|
#include "dot_sequence.h"
|
|
|
|
|
|
2026-10-12 23:55:15 +02:00
|
|
|
//#define FLIPDOT_PLCC_ADAPTER
|
|
|
|
|
// ESPSERIAL is set per target via build_flags in platformio.ini (-D ESPSERIAL for the ESP32)
|
|
|
|
|
|
|
|
|
|
class BROSE9323 : public Adafruit_GFX
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
uint16_t _flip_time;
|
2026-10-15 01:29:01 +02:00
|
|
|
uint16_t _flip_gap = 0; // pause after each pulse in us, lets the coil supply recover
|
|
|
|
|
uint8_t _pulses = 1; // pulses for each dot that changed
|
|
|
|
|
uint8_t _effect = EFFECT_INSTANT;
|
|
|
|
|
uint16_t _effect_time = 1500; // ms, how long an effect takes
|
|
|
|
|
uint8_t _effect_flags = 0; // EFFECT_FLAG_...
|
2026-10-12 23:55:15 +02:00
|
|
|
const uint8_t _panel_width;
|
|
|
|
|
const uint8_t _buffer_width;
|
|
|
|
|
const uint16_t _buffer_size;
|
|
|
|
|
uint8_t *_old_buffer = NULL;
|
|
|
|
|
uint8_t *_new_buffer = NULL;
|
|
|
|
|
bool _direct_mode = false;
|
|
|
|
|
|
|
|
|
|
#ifdef ESPSERIAL
|
|
|
|
|
Stream *stream;
|
|
|
|
|
#else
|
|
|
|
|
uint8_t _active_panel = 255;
|
|
|
|
|
uint8_t _active_col = 255;
|
|
|
|
|
uint8_t _active_row = 255;
|
|
|
|
|
bool _active_data;
|
2026-10-13 00:12:28 +02:00
|
|
|
// After boot the physical dot state is unknown, so the first display() redraws everything
|
|
|
|
|
bool _force_next = true;
|
2026-10-12 23:55:15 +02:00
|
|
|
|
2026-10-15 01:29:01 +02:00
|
|
|
// A running effect (display() with an effect other than EFFECT_INSTANT)
|
|
|
|
|
DotSequence _sequence;
|
|
|
|
|
bool _animating = false;
|
|
|
|
|
uint8_t _running_effect = EFFECT_INSTANT; // _effect, or the one chosen for EFFECT_RANDOM
|
|
|
|
|
uint8_t _phase = 0; // EFFECT_FLAP: 0 = to yellow, 1 = the new image
|
|
|
|
|
uint8_t _push_shift = 0; // EFFECT_PUSH: column shift 1..width
|
|
|
|
|
uint16_t _push_pos = 0; // EFFECT_PUSH: dot within the shift
|
|
|
|
|
bool _animate_all = false; // flip every dot, not only the changed ones
|
|
|
|
|
bool _drawing = false; // the buffer changed since the last display(): pause
|
|
|
|
|
uint32_t _step_time = 0; // us per step
|
|
|
|
|
uint32_t _next_step = 0; // micros() of the next step
|
|
|
|
|
|
2026-10-12 23:55:15 +02:00
|
|
|
#ifndef FLIPDOT_PLCC_ADAPTER
|
|
|
|
|
const uint8_t ENABLE = 7,
|
|
|
|
|
ADDR_0 = 3,
|
|
|
|
|
ADDR_1 = 5,
|
|
|
|
|
ADDR_2 = 6,
|
|
|
|
|
COL_DATA = 4,
|
|
|
|
|
COL_0 = 8,
|
|
|
|
|
COL_1 = 10,
|
|
|
|
|
COL_2 = 9,
|
|
|
|
|
COL_3 = 11,
|
|
|
|
|
COL_4 = 12,
|
|
|
|
|
ROW_0 = A3,
|
|
|
|
|
ROW_1 = A4,
|
|
|
|
|
ROW_2 = A2,
|
|
|
|
|
ROW_3 = A0,
|
|
|
|
|
ROW_4 = A1,
|
|
|
|
|
ROW_RESET = 13,
|
|
|
|
|
ROW_SET = A5;
|
|
|
|
|
#else
|
|
|
|
|
const uint8_t RELAY = 2,
|
|
|
|
|
ENABLE = 3,
|
|
|
|
|
ADDR_0 = 7,
|
|
|
|
|
ADDR_1 = 6,
|
|
|
|
|
ADDR_2 = 5,
|
|
|
|
|
COL_DATA = 4,
|
|
|
|
|
COL_0 = 12,
|
|
|
|
|
COL_1 = 11,
|
|
|
|
|
COL_2 = 10,
|
|
|
|
|
COL_3 = 9,
|
|
|
|
|
COL_4 = 8,
|
|
|
|
|
ROW_0 = 13, //1 A1 CHECK
|
|
|
|
|
ROW_1 = A0, //2 A2
|
|
|
|
|
ROW_2 = A1, //4 A3
|
|
|
|
|
ROW_3 = A2, //8 13 CHECK
|
|
|
|
|
ROW_4 = A3, //F A0
|
|
|
|
|
ROW_RESET = A4,
|
|
|
|
|
ROW_SET = A5;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
void _selectColumn(uint8_t);
|
|
|
|
|
void _selectPanel(uint8_t);
|
|
|
|
|
void _selectRow(uint8_t);
|
|
|
|
|
void _setData(bool);
|
|
|
|
|
void _strobe(void);
|
2026-10-15 01:29:01 +02:00
|
|
|
bool _shown(uint8_t x, uint8_t y) const;
|
|
|
|
|
bool _changed(uint8_t x, uint8_t y) const;
|
|
|
|
|
bool _wanted(uint8_t x, uint8_t y) const;
|
|
|
|
|
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);
|
2026-10-12 23:55:15 +02:00
|
|
|
#endif
|
|
|
|
|
public:
|
|
|
|
|
BROSE9323(uint8_t, uint8_t, uint8_t, uint16_t ft = 280);
|
|
|
|
|
#ifdef ESPSERIAL
|
|
|
|
|
void begin(Stream * = &Serial);
|
|
|
|
|
#endif
|
|
|
|
|
void begin(void);
|
|
|
|
|
void display(bool force = false);
|
|
|
|
|
void drawPixel(int16_t x, int16_t y, uint16_t color);
|
|
|
|
|
void fillScreen(uint16_t);
|
|
|
|
|
void setDirect(bool);
|
|
|
|
|
void setTiming(uint16_t);
|
2026-10-15 01:29:01 +02:00
|
|
|
// Pause after each flip pulse in us
|
|
|
|
|
void setGap(uint16_t);
|
|
|
|
|
// Pulses for each changed dot (a second pulse catches dots that didn't flip). Full
|
|
|
|
|
// redraws use one pulse per dot.
|
|
|
|
|
void setPulses(uint8_t);
|
|
|
|
|
// How display() shows a new frame (RefreshEffect), how long it takes in ms (pushing
|
|
|
|
|
// can take longer: it flips many dots per step) and EFFECT_FLAG_... flags.
|
|
|
|
|
// 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().
|
|
|
|
|
bool update(void);
|
|
|
|
|
// An effect is running (paused while a new frame is being drawn into the buffer)
|
|
|
|
|
bool busy(void) const;
|
|
|
|
|
#endif
|
2026-10-12 23:55:15 +02:00
|
|
|
#ifndef ESPSERIAL
|
|
|
|
|
void printBuffer(void);
|
|
|
|
|
#endif
|
|
|
|
|
size_t print(const char *ifsh);
|
2026-10-13 01:52:34 +02:00
|
|
|
// 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;
|
2026-10-12 23:55:15 +02:00
|
|
|
};
|
|
|
|
|
#endif //BROSE9323_H
|