Files
hassos_config/esphome/custom_component/nixie_display/nixie_display.h
2026-03-26 12:10:21 +01:00

151 lines
4.8 KiB
C++

#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 <cstdarg>
#include <string>
namespace esphome {
namespace nixie_display {
class NixieDisplay : public display::DisplayBuffer,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST,
spi::CLOCK_POLARITY_HIGH,
spi::CLOCK_PHASE_TRAILING,
spi::DATA_RATE_200KHZ> {
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