1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-01 07:31:51 +00:00

Compare commits

...

7 Commits

Author SHA1 Message Date
J. Nick Koston
7504219a2d fix 2025-10-30 23:20:27 -05:00
J. Nick Koston
6947df56d5 wip 2025-10-30 23:14:20 -05:00
J. Nick Koston
29c97686fa wip 2025-10-30 23:12:26 -05:00
J. Nick Koston
f3087abd55 [display] Optimize display writers with function pointers for stateless lambdas 2025-10-30 23:08:23 -05:00
J. Nick Koston
f55dfc7f5d wip 2025-10-30 22:57:07 -05:00
J. Nick Koston
c5a9d30362 wip 2025-10-30 22:57:00 -05:00
J. Nick Koston
fae06133d4 wip 2025-10-30 22:53:14 -05:00
13 changed files with 152 additions and 22 deletions

View File

@@ -176,7 +176,117 @@ class Display;
class DisplayPage;
class DisplayOnPageChangeTrigger;
using display_writer_t = std::function<void(Display &)>;
/** Optimized display writer that uses function pointers for stateless lambdas.
*
* Similar to TemplatableValue but specialized for display writer callbacks.
* Saves ~8 bytes per stateless lambda on 32-bit platforms (16 bytes std::function → ~8 bytes discriminator+pointer).
*
* Supports both:
* - Stateless lambdas (from YAML) → function pointer (4 bytes)
* - Stateful lambdas/std::function (from C++ code) → std::function* (heap allocated)
*
* @tparam T The display type (e.g., Display, Nextion, GPIOLCDDisplay)
*/
template<typename T> class DisplayWriter {
public:
DisplayWriter() : type_(NONE) {}
// For stateless lambdas (convertible to function pointer): use function pointer (4 bytes)
template<typename F>
DisplayWriter(F f) requires std::invocable<F, T &> && std::convertible_to<F, void (*)(T &)>
: type_(STATELESS_LAMBDA) {
this->stateless_f_ = f; // Implicit conversion to function pointer
}
// For stateful lambdas and std::function (not convertible to function pointer): use std::function* (heap allocated)
// This handles backwards compatibility with external components
template<typename F>
DisplayWriter(F f) requires std::invocable<F, T &> &&(!std::convertible_to<F, void (*)(T &)>) : type_(LAMBDA) {
this->f_ = new std::function<void(T &)>(std::move(f));
}
// Copy constructor
DisplayWriter(const DisplayWriter &other) : type_(other.type_) {
if (type_ == LAMBDA) {
this->f_ = new std::function<void(T &)>(*other.f_);
} else if (type_ == STATELESS_LAMBDA) {
this->stateless_f_ = other.stateless_f_;
}
}
// Move constructor
DisplayWriter(DisplayWriter &&other) noexcept : type_(other.type_) {
if (type_ == LAMBDA) {
this->f_ = other.f_;
other.f_ = nullptr;
} else if (type_ == STATELESS_LAMBDA) {
this->stateless_f_ = other.stateless_f_;
}
other.type_ = NONE;
}
// Assignment operators
DisplayWriter &operator=(const DisplayWriter &other) {
if (this != &other) {
this->~DisplayWriter();
new (this) DisplayWriter(other);
}
return *this;
}
DisplayWriter &operator=(DisplayWriter &&other) noexcept {
if (this != &other) {
this->~DisplayWriter();
new (this) DisplayWriter(std::move(other));
}
return *this;
}
~DisplayWriter() {
if (type_ == LAMBDA) {
delete this->f_;
}
// STATELESS_LAMBDA/NONE: no cleanup needed (function pointer or empty)
}
bool has_value() const { return this->type_ != NONE; }
void call(T &display) const {
switch (this->type_) {
case STATELESS_LAMBDA:
this->stateless_f_(display); // Direct function pointer call
break;
case LAMBDA:
(*this->f_)(display); // std::function call
break;
case NONE:
default:
break;
}
}
// Operator() for convenience
void operator()(T &display) const { this->call(display); }
// Operator* for backwards compatibility with (*writer_)(*this) pattern
DisplayWriter &operator*() { return *this; }
const DisplayWriter &operator*() const { return *this; }
protected:
enum : uint8_t {
NONE,
LAMBDA,
STATELESS_LAMBDA,
} type_;
union {
std::function<void(T &)> *f_;
void (*stateless_f_)(T &);
};
};
// Type alias for Display writer - uses optimized DisplayWriter instead of std::function
using display_writer_t = DisplayWriter<Display>;
#define LOG_DISPLAY(prefix, type, obj) \
if ((obj) != nullptr) { \
@@ -678,7 +788,7 @@ class Display : public PollingComponent {
void sort_triangle_points_by_y_(int *x1, int *y1, int *x2, int *y2, int *x3, int *y3);
DisplayRotation rotation_{DISPLAY_ROTATION_0_DEGREES};
optional<display_writer_t> writer_{};
display_writer_t writer_{};
DisplayPage *page_{nullptr};
DisplayPage *previous_page_{nullptr};
std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;

View File

@@ -2,13 +2,18 @@
#include "esphome/core/hal.h"
#include "esphome/components/lcd_base/lcd_display.h"
#include "esphome/components/display/display.h"
namespace esphome {
namespace lcd_gpio {
class GPIOLCDDisplay;
using gpio_lcd_writer_t = display::DisplayWriter<GPIOLCDDisplay>;
class GPIOLCDDisplay : public lcd_base::LCDDisplay {
public:
void set_writer(std::function<void(GPIOLCDDisplay &)> &&writer) { this->writer_ = std::move(writer); }
void set_writer(gpio_lcd_writer_t &&writer) { this->writer_ = std::move(writer); }
void setup() override;
void set_data_pins(GPIOPin *d0, GPIOPin *d1, GPIOPin *d2, GPIOPin *d3) {
this->data_pins_[0] = d0;
@@ -43,7 +48,7 @@ class GPIOLCDDisplay : public lcd_base::LCDDisplay {
GPIOPin *rw_pin_{nullptr};
GPIOPin *enable_pin_{nullptr};
GPIOPin *data_pins_[8]{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
std::function<void(GPIOLCDDisplay &)> writer_;
gpio_lcd_writer_t writer_;
};
} // namespace lcd_gpio

View File

@@ -3,13 +3,18 @@
#include "esphome/core/component.h"
#include "esphome/components/lcd_base/lcd_display.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/display/display.h"
namespace esphome {
namespace lcd_pcf8574 {
class PCF8574LCDDisplay;
using pcf8574_lcd_writer_t = display::DisplayWriter<PCF8574LCDDisplay>;
class PCF8574LCDDisplay : public lcd_base::LCDDisplay, public i2c::I2CDevice {
public:
void set_writer(std::function<void(PCF8574LCDDisplay &)> &&writer) { this->writer_ = std::move(writer); }
void set_writer(pcf8574_lcd_writer_t &&writer) { this->writer_ = std::move(writer); }
void setup() override;
void dump_config() override;
void backlight();
@@ -24,7 +29,7 @@ class PCF8574LCDDisplay : public lcd_base::LCDDisplay, public i2c::I2CDevice {
// Stores the current state of the backlight.
uint8_t backlight_value_;
std::function<void(PCF8574LCDDisplay &)> writer_;
pcf8574_lcd_writer_t writer_;
};
} // namespace lcd_pcf8574

View File

@@ -4,13 +4,14 @@
#include "esphome/core/time.h"
#include "esphome/components/spi/spi.h"
#include "esphome/components/display/display.h"
namespace esphome {
namespace max7219 {
class MAX7219Component;
using max7219_writer_t = std::function<void(MAX7219Component &)>;
using max7219_writer_t = display::DisplayWriter<MAX7219Component>;
class MAX7219Component : public PollingComponent,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
@@ -57,7 +58,7 @@ class MAX7219Component : public PollingComponent,
uint8_t num_chips_{1};
uint8_t *buffer_;
bool reverse_{false};
optional<max7219_writer_t> writer_{};
max7219_writer_t writer_{};
};
} // namespace max7219

View File

@@ -23,7 +23,7 @@ enum ScrollMode {
class MAX7219Component;
using max7219_writer_t = std::function<void(MAX7219Component &)>;
using max7219_writer_t = display::DisplayWriter<MAX7219Component>;
class MAX7219Component : public display::DisplayBuffer,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
@@ -117,7 +117,7 @@ class MAX7219Component : public display::DisplayBuffer,
uint32_t last_scroll_ = 0;
uint16_t stepsleft_;
size_t get_buffer_length_();
optional<max7219_writer_t> writer_local_{};
max7219_writer_t writer_local_{};
};
} // namespace max7219digit

View File

@@ -9,6 +9,7 @@
#include "esphome/components/uart/uart.h"
#include "nextion_base.h"
#include "nextion_component.h"
#include "esphome/components/display/display.h"
#include "esphome/components/display/display_color_utils.h"
#ifdef USE_NEXTION_TFT_UPLOAD
@@ -31,7 +32,7 @@ namespace nextion {
class Nextion;
class NextionComponentBase;
using nextion_writer_t = std::function<void(Nextion &)>;
using nextion_writer_t = display::DisplayWriter<Nextion>;
static const std::string COMMAND_DELIMITER{static_cast<char>(255), static_cast<char>(255), static_cast<char>(255)};
@@ -1471,7 +1472,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
CallbackManager<void(uint8_t, uint8_t, bool)> touch_callback_{};
CallbackManager<void()> buffer_overflow_callback_{};
optional<nextion_writer_t> writer_;
nextion_writer_t writer_;
optional<float> brightness_;
#ifdef USE_NEXTION_CONFIG_DUMP_DEVICE_INFO

View File

@@ -3,6 +3,7 @@
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/components/ble_client/ble_client.h"
#include "esphome/components/display/display.h"
#include <cinttypes>
@@ -29,7 +30,7 @@ enum UNIT {
UNIT_DEG_E, ///< show "°E"
};
using pvvx_writer_t = std::function<void(PVVXDisplay &)>;
using pvvx_writer_t = display::DisplayWriter<PVVXDisplay>;
class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent {
public:
@@ -126,7 +127,7 @@ class PVVXDisplay : public ble_client::BLEClientNode, public PollingComponent {
esp32_ble_tracker::ESPBTUUID char_uuid_ =
esp32_ble_tracker::ESPBTUUID::from_raw("00001f1f-0000-1000-8000-00805f9b34fb");
optional<pvvx_writer_t> writer_{};
pvvx_writer_t writer_{};
};
} // namespace pvvx_mithermometer

View File

@@ -9,7 +9,7 @@ namespace st7920 {
class ST7920;
using st7920_writer_t = std::function<void(ST7920 &)>;
using st7920_writer_t = display::DisplayWriter<ST7920>;
class ST7920 : public display::DisplayBuffer,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, spi::CLOCK_PHASE_TRAILING,
@@ -44,7 +44,7 @@ class ST7920 : public display::DisplayBuffer,
void end_transaction_();
int16_t width_ = 128, height_ = 64;
optional<st7920_writer_t> writer_local_{};
st7920_writer_t writer_local_{};
};
} // namespace st7920

View File

@@ -3,13 +3,14 @@
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/components/display/display.h"
namespace esphome {
namespace tm1621 {
class TM1621Display;
using tm1621_writer_t = std::function<void(TM1621Display &)>;
using tm1621_writer_t = display::DisplayWriter<TM1621Display>;
class TM1621Display : public PollingComponent {
public:
@@ -59,7 +60,7 @@ class TM1621Display : public PollingComponent {
GPIOPin *cs_pin_;
GPIOPin *read_pin_;
GPIOPin *write_pin_;
optional<tm1621_writer_t> writer_{};
tm1621_writer_t writer_{};
char row_[2][12];
uint8_t state_;
uint8_t device_;

View File

@@ -4,6 +4,7 @@
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/time.h"
#include "esphome/components/display/display.h"
#include <vector>
@@ -19,7 +20,7 @@ class TM1637Display;
class TM1637Key;
#endif
using tm1637_writer_t = std::function<void(TM1637Display &)>;
using tm1637_writer_t = display::DisplayWriter<TM1637Display>;
class TM1637Display : public PollingComponent {
public:
@@ -78,7 +79,7 @@ class TM1637Display : public PollingComponent {
uint8_t length_;
bool inverted_;
bool on_{true};
optional<tm1637_writer_t> writer_{};
tm1637_writer_t writer_{};
uint8_t buffer_[6] = {0};
#ifdef USE_BINARY_SENSOR
std::vector<TM1637Key *> tm1637_keys_{};

View File

@@ -5,6 +5,7 @@
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/time.h"
#include "esphome/components/display/display.h"
#include <vector>
@@ -18,7 +19,7 @@ class KeyListener {
class TM1638Component;
using tm1638_writer_t = std::function<void(TM1638Component &)>;
using tm1638_writer_t = display::DisplayWriter<TM1638Component>;
class TM1638Component : public PollingComponent {
public:
@@ -70,7 +71,7 @@ class TM1638Component : public PollingComponent {
GPIOPin *stb_pin_;
GPIOPin *dio_pin_;
uint8_t *buffer_ = new uint8_t[8];
optional<tm1638_writer_t> writer_{};
tm1638_writer_t writer_{};
std::vector<KeyListener *> listeners_{};
};

View File

@@ -1,5 +1,6 @@
display:
- platform: sdl
id: image_display
auto_clear_enabled: false
dimensions:
width: 480

View File

@@ -13,11 +13,14 @@ display:
binary_sensor:
- platform: sdl
sdl_id: sdl_display
id: key_up
key: SDLK_a
- platform: sdl
sdl_id: sdl_display
id: key_down
key: SDLK_d
- platform: sdl
sdl_id: sdl_display
id: key_enter
key: SDLK_s