display.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. """ESPHome display platform driving a GxEPD2 panel via a native chunked
  2. refresh engine (see cycle_engine.h) instead of GxEPD2's own C++ classes.
  3. Panels are selected via `model:`, each backed by a hand-written (later,
  4. extracted) EpdPanelDescriptor - see panel_213_z98c.h / panel_426_gdeq0426t82.h
  5. / panel_583_gdeq0583t31.h. catalog.json/vendor/arduino_compat stay in the
  6. tree as reference material for panels not yet ported to this engine.
  7. """
  8. import esphome.codegen as cg
  9. import esphome.config_validation as cv
  10. from esphome import pins
  11. from esphome.components import display, spi
  12. from esphome.const import (
  13. CONF_BUSY_PIN,
  14. CONF_CS_PIN,
  15. CONF_DC_PIN,
  16. CONF_ID,
  17. CONF_LAMBDA,
  18. CONF_MODEL,
  19. CONF_RESET_PIN,
  20. CONF_SPI_ID,
  21. Framework,
  22. )
  23. from esphome.types import ConfigType
  24. CODEOWNERS = ["@zib"]
  25. DEPENDENCIES = ["spi"]
  26. gxepd2_ns = cg.esphome_ns.namespace("gxepd2")
  27. GxEPD2Display = gxepd2_ns.class_("GxEPD2Display", display.DisplayBuffer)
  28. # model name -> C++ descriptor global (EpdPanelDescriptor) declared in the
  29. # matching panel_*.h header, which gxepd2_display.h includes unconditionally
  30. # - so any of these names can be selected without extra include wiring here.
  31. MODELS = {
  32. "GxEPD2_213_Z98C": "PANEL_213_Z98C",
  33. "GxEPD2_426_GDEQ0426T82": "PANEL_426_GDEQ0426T82",
  34. "GxEPD2_583_GDEQ0583T31": "PANEL_583_GDEQ0583T31",
  35. "GxEPD2_290c_GDEY029F51": "PANEL_290C_GDEY029F51",
  36. "GxEPD2_579c_GDEY0579F51": "PANEL_579C_GDEY0579F51",
  37. # Debug variant: master half only, as if it were a self-contained
  38. # 396x272 panel - see panel_579c_gdey0579f51_master_only.h. Kept
  39. # around as a diagnostic tool, not the panel's real mode.
  40. "GxEPD2_579c_GDEY0579F51_MASTER_ONLY": "PANEL_579C_GDEY0579F51_MASTER_ONLY",
  41. }
  42. CONFIG_SCHEMA = cv.All(
  43. display.FULL_DISPLAY_SCHEMA.extend(
  44. {
  45. cv.GenerateID(): cv.declare_id(GxEPD2Display),
  46. cv.GenerateID(CONF_SPI_ID): cv.use_id(spi.SPIComponent),
  47. cv.Required(CONF_MODEL): cv.one_of(*MODELS),
  48. cv.Required(CONF_CS_PIN): pins.gpio_output_pin_schema,
  49. cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
  50. cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
  51. cv.Optional(CONF_BUSY_PIN): pins.gpio_input_pin_schema,
  52. }
  53. ),
  54. cv.only_with_framework(Framework.ESP_IDF),
  55. )
  56. async def to_code(config: ConfigType) -> None:
  57. var = cg.new_Pvariable(config[CONF_ID])
  58. cg.add(var.set_descriptor(cg.RawExpression(f"&esphome::gxepd2::{MODELS[config[CONF_MODEL]]}")))
  59. cg.add(var.set_cs_pin(await cg.gpio_pin_expression(config[CONF_CS_PIN])))
  60. cg.add(var.set_dc_pin(await cg.gpio_pin_expression(config[CONF_DC_PIN])))
  61. if reset_pin := config.get(CONF_RESET_PIN):
  62. cg.add(var.set_reset_pin(await cg.gpio_pin_expression(reset_pin)))
  63. if busy_pin := config.get(CONF_BUSY_PIN):
  64. cg.add(var.set_busy_pin(await cg.gpio_pin_expression(busy_pin)))
  65. spi_parent = await cg.get_variable(config[CONF_SPI_ID])
  66. cg.add(var.set_spi_parent(spi_parent))
  67. # display.register_display() only wires up `pages:` - a bare top-level
  68. # `lambda:` (what our CONFIG_SCHEMA actually supports) has to be
  69. # processed and attached via set_writer() by the platform itself, same
  70. # as every other display component's to_code() (see e.g. epaper_spi's
  71. # display.py). Missing this meant the configured lambda never ran at
  72. # all - the only thing ever drawn was auto_clear's default
  73. # fill(COLOR_OFF), which is why every real-hardware run came out solid
  74. # black regardless of any protocol-level change.
  75. if CONF_LAMBDA in config:
  76. lambda_ = await cg.process_lambda(config[CONF_LAMBDA], [(display.DisplayRef, "it")], return_type=cg.void)
  77. cg.add(var.set_writer(lambda_))
  78. await display.register_display(var, config)