#pragma once #include "esphome/core/component.h" #include "esphome/core/gpio.h" #include "esphome/core/helpers.h" #include "esphome/components/display/display_buffer.h" #include "esphome/components/spi/spi.h" #include "esphome/components/time/real_time_clock.h" #include #include namespace esphome { namespace nixie_display { class NixieDisplay : public display::DisplayBuffer, public spi::SPIDevice { public: enum DisplayMode : uint8_t { MODE_UNKNOWN = 0, MODE_TIME = 1, MODE_DATE = 2, }; void set_anode_pins(InternalGPIOPin *anode0, InternalGPIOPin *anode1, InternalGPIOPin *anode2, InternalGPIOPin *le_pin) { this->anode0_pin_ = anode0; this->anode1_pin_ = anode1; this->anode2_pin_ = anode2; this->le_pin_ = le_pin; } void setup() override; void dump_config() override; void update() override; void loop() override; display::DisplayType get_display_type() override { return display::DISPLAY_TYPE_BINARY; } void printf(const char *format, ...); void display_text(const char *text) { this->last_external_text_ms_ = millis(); this->set_target_string_(text, MODE_UNKNOWN); } void display_time_text(const char *text) { this->last_external_text_ms_ = millis(); this->set_target_string_(text, MODE_TIME); } void display_date_text(const char *text) { this->last_external_text_ms_ = millis(); this->set_target_string_(text, MODE_DATE); } void set_time_source(time::RealTimeClock *rtc) { this->time_source_ = rtc; } void set_debug_logging(bool enabled) { this->debug_logging_ = enabled; } void set_anti_poisoning(bool enabled) { this->anti_poisoning_ = enabled; } void set_lambda_hold_ms(uint32_t hold_ms) { this->lambda_hold_ms_ = hold_ms; } void set_auto_time(bool enabled) { this->auto_time_ = enabled; } void set_date_mode(bool enabled) { this->date_mode_ = enabled; if (enabled) { this->auto_rotate_ = false; } } bool is_date_mode() const { return this->date_mode_; } void set_auto_rotate(bool enabled) { this->auto_rotate_ = enabled; if (enabled) { this->date_mode_ = false; } } bool is_auto_rotate() const { return this->auto_rotate_; } void set_refresh_interval_us(uint32_t interval_us) { this->refresh_interval_us_ = interval_us; } float get_setup_priority() const override { return setup_priority::PROCESSOR; } protected: void draw_absolute_pixel_internal(int x, int y, Color color) override; int get_height_internal() override { return 1; } int get_width_internal() override { return NUM_TUBES; } private: InternalGPIOPin *anode0_pin_{nullptr}; InternalGPIOPin *anode1_pin_{nullptr}; InternalGPIOPin *anode2_pin_{nullptr}; InternalGPIOPin *le_pin_{nullptr}; // Bitmap for digits 0-9, 10 bits per digit (cathode pins) const uint16_t digit_bitmap_[10] = { 1022, // 0 1021, // 1 1019, // 2 1015, // 3 1007, // 4 991, // 5 959, // 6 895, // 7 767, // 8 511 // 9 }; static constexpr int NUM_TUBES = 6; static constexpr uint32_t DEFAULT_REFRESH_INTERVAL_US = 1000; uint32_t last_update_us_; uint32_t last_logic_update_ms_{0}; uint32_t refresh_interval_us_{DEFAULT_REFRESH_INTERVAL_US}; uint32_t lambda_hold_ms_{1500}; uint8_t current_anode_; std::string displayed_string_; std::string target_string_; DisplayMode current_mode_{MODE_UNKNOWN}; DisplayMode target_mode_{MODE_UNKNOWN}; bool anti_poison_active_; unsigned long anti_poison_start_; uint8_t anti_poison_counter_; HighFrequencyLoopRequester high_freq_; time::RealTimeClock *time_source_{nullptr}; bool debug_logging_{false}; bool anti_poisoning_{true}; bool auto_time_{true}; bool date_mode_{false}; bool auto_rotate_{false}; bool anti_poison_on_mode_change_{true}; bool anti_poison_pending_{false}; uint32_t last_debug_log_ms_{0}; uint32_t last_external_text_ms_{0}; int last_render_second_{-1}; bool last_effective_date_mode_{false}; // Anti-poisoning function: cycles through digits to prevent cathode buildup std::string anti_poison_transition_(const std::string &from_str, const std::string &to_str); // Update the SPI display for current tube pair and anode void update_tube_display_(const std::string &display_str); // Set active anode void set_anode_(uint8_t anode_index); // Convert character to digit (0-9) uint8_t char_to_digit_(char c); void set_target_string_(const std::string &value, DisplayMode mode); }; } // namespace nixie_display } // namespace esphome