#pragma once #include #include "cyberpunk.h" #include "el512_helpers.h" #include "konosuba.h" #include "steins.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 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