Public Access
Megumin's daily explosion, the Adventurers' Guild quest board (agenda, weather, solar), Megumin's adventurer card and a party battle against a giant toad with a live-data battle log. Original 1-bit pixel art lives in tools/konosuba_sprites.py, which generates konosuba_sprites.h and can render a preview PNG. Home data and drawing helpers shared by the themed pages move to el512_pages.h; the display gains dither_mask() for a dissolve-in intro. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
100 lines
4.0 KiB
C++
100 lines
4.0 KiB
C++
#pragma once
|
|
|
|
#include <vector>
|
|
|
|
#include "cyberpunk.h"
|
|
#include "el512_helpers.h"
|
|
#include "konosuba.h"
|
|
#include "el512_panel.h"
|
|
#include "esphome/components/display/display_buffer.h"
|
|
#include "esphome/core/component.h"
|
|
#include "still_alive.h"
|
|
|
|
namespace esphome::el512 {
|
|
|
|
// ESPHome display for the Planar EL512.256-H. Drawing goes into the panel
|
|
// driver's back buffer; every update() hands it to the refresh in one piece.
|
|
class EL512Display : public display::DisplayBuffer {
|
|
public:
|
|
void set_pins(int vclk, int vid, int tvid, int hs, int vs, int enable, int low_power) {
|
|
this->pins_ = {(int8_t) vclk, (int8_t) vid, (int8_t) tvid, (int8_t) hs,
|
|
(int8_t) vs, (int8_t) enable, (int8_t) low_power};
|
|
}
|
|
|
|
void setup() override;
|
|
void loop() override;
|
|
void update() override;
|
|
void dump_config() override;
|
|
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
|
|
|
|
void fill(Color color) override;
|
|
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_BINARY; }
|
|
|
|
// Display on/off (ENABLE) and low-power mode (_LOWPWR), e.g. from template switches.
|
|
void set_enabled(bool on);
|
|
void set_low_power(bool on);
|
|
bool is_enabled() const { return this->enabled_; }
|
|
bool is_low_power() const { return this->low_power_; }
|
|
bool has_enable_pin() const { return this->pins_.enable >= 0; }
|
|
bool has_low_power_pin() const { return this->pins_.lowpwr_n >= 0; }
|
|
|
|
// Portal end-credits show. Showing this page restarts it; while it is the
|
|
// active page the display redraws as the show advances, and when it ends the
|
|
// previous page comes back. Draw it from that page's lambda with
|
|
// draw_still_alive().
|
|
void set_still_alive_page(display::DisplayPage *page) { this->still_alive_page_ = page; }
|
|
void draw_still_alive();
|
|
// Show the Still Alive page and start the show from the top (also when it is
|
|
// already playing).
|
|
void play_still_alive();
|
|
bool is_still_alive_playing() const {
|
|
return this->still_alive_page_ != nullptr && this->get_active_page() == this->still_alive_page_;
|
|
}
|
|
// When to start the music, in ms after the page was shown.
|
|
uint32_t still_alive_song_start_ms() const { return el512sa::StillAlivePlayer::songStartMs(); }
|
|
|
|
// Animated pages redraw every animation interval while they are shown,
|
|
// instead of once per update_interval. Their lambdas can use page_age_ms()
|
|
// as the animation clock.
|
|
void add_animated_page(display::DisplayPage *page) { this->animated_pages_.push_back(page); }
|
|
void set_animation_interval(uint32_t ms) { this->animation_interval_ = ms; }
|
|
// Time since the active page was shown, in ms.
|
|
uint32_t page_age_ms();
|
|
// millis() when the active page was shown (as seen by page_age_ms()).
|
|
uint32_t page_shown_at() const { return this->age_start_; }
|
|
|
|
// Effects on the drawing buffer, for use after drawing a page: shift rows
|
|
// y..y+height-1 right by dx pixels (wrapping around), invert a rectangle.
|
|
void shift_rows(int y, int height, int dx);
|
|
void invert_rect(int x, int y, int width, int height);
|
|
// Keep only the pixels whose 4x4 Bayer threshold is below level (0..16):
|
|
// 0 clears everything, 16 keeps everything. Steps of it dissolve a page in.
|
|
void dither_mask(int level);
|
|
|
|
protected:
|
|
void draw_absolute_pixel_internal(int x, int y, Color color) override;
|
|
void check_refresh_health_();
|
|
int get_width_internal() override { return EL512Panel::WIDTH; }
|
|
int get_height_internal() override { return EL512Panel::HEIGHT; }
|
|
|
|
EL512Pins pins_{18, 19, 21, 22, 23, -1, -1};
|
|
EL512Panel *panel_{nullptr};
|
|
bool enabled_{true};
|
|
bool low_power_{false};
|
|
uint32_t health_checked_{0};
|
|
uint32_t health_logged_{0};
|
|
|
|
display::DisplayPage *still_alive_page_{nullptr};
|
|
display::DisplayPage *still_alive_return_{nullptr};
|
|
bool still_alive_was_active_{false};
|
|
el512sa::StillAlivePlayer still_alive_;
|
|
|
|
std::vector<display::DisplayPage *> animated_pages_;
|
|
uint32_t animation_interval_{66};
|
|
uint32_t animated_at_{0};
|
|
const display::DisplayPage *age_page_{nullptr};
|
|
uint32_t age_start_{0};
|
|
};
|
|
|
|
} // namespace esphome::el512
|