Add ESPHome display component; ring-buffer driver shared with PlatformIO

Driver (components/el512/el512_panel.*, ESP-IDF only):
- DMA ring of 8 x 4 lines (17 KB) refilled from the I2S EOF interrupt
  (IRAM) instead of a 142 KB full-frame stream; ~107 KB heap freed.
- 256-entry lookup table encodes 8 pixels per step.
- Back buffer + displayed buffer, swapped at the start of a frame.
- PlatformIO builds it via lib_extra_dirs (EL512Core); lib/EL512 keeps
  the Adafruit GFX front end.

ESPHome external component (platform: el512):
- DisplayBuffer with optional ENABLE / _LOWPWR pins, set_enabled(),
  set_low_power(), helper functions for page lambdas.
- still_alive_page: plays the Portal end-credits show, then returns to
  the previous page.

Still Alive engine moved to components/el512/still_alive.* with a built-in
5x7 font (from Adafruit GFX, BSD); the PlatformIO demo uses the same code.
The importer writes components/el512/still_alive_data.h and stops at 200 s
(--until-ms), matching the hand-trimmed data.

esphome/el512-panel.yaml: 8 pages (clock, forecast, agenda, dashboard,
now playing, Plex, solar, Still Alive), page select, rotation, display
and low-power switches, message text. home_assistant/packages/el512_panel.yaml:
helper template sensors for forecast, agenda, now playing and Plex streams.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-10-18 20:20:13 +02:00
1 parent 410885de24
commit 963590cdfe
27 files changed
+2110 -828

No files matched your search

+110
View File
@@ -0,0 +1,110 @@
// Portal-style end-credits show for the EL512.256-H: timed song text typed into
// a console on the left, credits typed top right, ASCII art bottom right.
//
// Framework-agnostic: draws straight into an EL512Panel with the built-in 5x7
// font, so the PlatformIO demo (src/still_alive) and the ESPHome component use
// the same code. The content is in still_alive_data.h, generated by
// tools/import_still_alive.py.
#pragma once
#include <stddef.h>
#include <stdint.h>
class EL512Panel;
namespace el512sa {
enum : uint8_t {
STEP_NEWLINE = 1 << 0, // start a new line after the text is typed
STEP_CLEAR = 1 << 1, // clear the console before typing
};
// One timed piece of console text. Steps are sorted by startMs.
struct LyricStep {
uint32_t startMs; // from the start of the show
uint16_t durMs; // the text is typed out evenly over this time
uint8_t flags;
int8_t art; // picture to show (index into ARTS), or ART_KEEP
const char *text;
};
struct Art {
const char *const *lines;
uint8_t count;
};
static constexpr int8_t ART_KEEP = -1; // leave the current picture
static constexpr int8_t ART_NONE = 0; // ARTS[0] is always the empty picture
// Fixed-size character console in the 5x7 font (6x8 cells), optionally inside a
// dashed frame. Text wraps at the right edge and scrolls up past the bottom row.
class TextBox {
public:
static constexpr uint8_t MAX_COLS = 48;
static constexpr uint8_t MAX_ROWS = 32;
static constexpr int16_t PAD = 4;
static constexpr int16_t CHAR_W = 6;
static constexpr int16_t CHAR_H = 8;
TextBox(int16_t x, int16_t y, int16_t w, int16_t h);
uint8_t cols() const { return cols_; }
uint8_t rows() const { return rows_; }
void clear();
void put(char c);
void print(const char *s);
void draw(EL512Panel &panel, bool cursor, bool frame = true) const;
private:
void newLine();
int16_t x_, y_, w_, h_;
uint8_t cols_, rows_;
uint8_t row_ = 0, col_ = 0;
char text_[MAX_ROWS][MAX_COLS + 1];
};
class StillAlivePlayer {
public:
static constexpr uint32_t START_DELAY_MS = 1000; // after start()
static constexpr uint32_t CURSOR_BLINK_MS = 300;
static constexpr uint32_t HOLD_MS = 10000; // after the last step, before finished()
StillAlivePlayer();
// Restart the show; it begins START_DELAY_MS after nowMs.
void start(uint32_t nowMs);
// Advance to nowMs; true if the screen changed (text, picture or cursor).
bool update(uint32_t nowMs);
// Draw the whole show; the caller clears the panel first and shows it after.
void draw(EL512Panel &panel) const;
// True once the last step has been typed and HOLD_MS has passed.
bool finished(uint32_t nowMs) const;
// When to start the music, relative to start().
static uint32_t songStartMs();
private:
bool updateLyrics(uint32_t t, uint32_t nowMs);
bool updateCredits(uint32_t t);
void showArt(int8_t id);
TextBox lyrics_;
TextBox credits_;
TextBox art_;
uint32_t showStart_ = 0;
size_t step_ = 0;
size_t charIdx_ = 0;
bool stepStarted_ = false;
bool done_ = false;
uint32_t doneAt_ = 0;
size_t creditIdx_ = 0;
size_t creditChar_ = 0;
bool creditLineOpen_ = false;
bool creditsDone_ = false;
uint32_t creditLineAt_ = 0;
bool cursor_ = false;
bool creditsCursor_ = false;
};
} // namespace el512sa