[hub75] Bump esp-hub75 version to 0.2.2 (#12674)

This commit is contained in:
Stuart Parmenter
2026-01-04 12:50:10 -08:00
committed by GitHub
parent 9ae19d53dc
commit 449e478bec
5 changed files with 84 additions and 13 deletions

View File

@@ -15,6 +15,7 @@ from esphome.const import (
CONF_ID,
CONF_LAMBDA,
CONF_OE_PIN,
CONF_ROTATION,
CONF_UPDATE_INTERVAL,
)
from esphome.core import ID
@@ -134,6 +135,14 @@ CLOCK_SPEEDS = {
"20MHZ": Hub75ClockSpeed.HZ_20M,
}
Hub75Rotation = cg.global_ns.enum("Hub75Rotation", is_class=True)
ROTATIONS = {
0: Hub75Rotation.ROTATE_0,
90: Hub75Rotation.ROTATE_90,
180: Hub75Rotation.ROTATE_180,
270: Hub75Rotation.ROTATE_270,
}
HUB75Display = hub75_ns.class_("HUB75Display", cg.PollingComponent, display.Display)
Hub75Config = cg.global_ns.struct("Hub75Config")
Hub75Pins = cg.global_ns.struct("Hub75Pins")
@@ -361,6 +370,8 @@ CONFIG_SCHEMA = cv.All(
display.FULL_DISPLAY_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(HUB75Display),
# Override rotation - store Hub75Rotation directly (driver handles rotation)
cv.Optional(CONF_ROTATION): cv.enum(ROTATIONS, int=True),
# Board preset (optional - provides default pin mappings)
cv.Optional(CONF_BOARD): cv.one_of(*BOARDS.keys(), lower=True),
# Panel dimensions
@@ -378,7 +389,7 @@ CONFIG_SCHEMA = cv.All(
# Display configuration
cv.Optional(CONF_DOUBLE_BUFFER): cv.boolean,
cv.Optional(CONF_BRIGHTNESS): cv.int_range(min=0, max=255),
cv.Optional(CONF_BIT_DEPTH): cv.int_range(min=6, max=12),
cv.Optional(CONF_BIT_DEPTH): cv.int_range(min=4, max=12),
cv.Optional(CONF_GAMMA_CORRECT): cv.enum(
{"LINEAR": 0, "CIE1931": 1, "GAMMA_2_2": 2}, upper=True
),
@@ -490,10 +501,11 @@ def _build_config_struct(
Fields must be added in declaration order (see hub75_types.h) to satisfy
C++ designated initializer requirements. The order is:
1. fields_before_pins (panel_width through layout)
2. pins
3. output_clock_speed
4. min_refresh_rate
5. fields_after_min_refresh (latch_blanking through brightness)
2. rotation
3. pins
4. output_clock_speed
5. min_refresh_rate
6. fields_after_min_refresh (latch_blanking through brightness)
"""
fields_before_pins = [
(CONF_PANEL_WIDTH, "panel_width"),
@@ -516,6 +528,10 @@ def _build_config_struct(
_append_config_fields(config, fields_before_pins, config_fields)
# Rotation - config already contains Hub75Rotation enum from cv.enum
if CONF_ROTATION in config:
config_fields.append(("rotation", config[CONF_ROTATION]))
config_fields.append(("pins", pins_struct))
if CONF_CLOCK_SPEED in config:
@@ -531,7 +547,7 @@ def _build_config_struct(
async def to_code(config: ConfigType) -> None:
add_idf_component(
name="esphome/esp-hub75",
ref="0.1.7",
ref="0.2.2",
)
# Set compile-time configuration via defines
@@ -570,6 +586,11 @@ async def to_code(config: ConfigType) -> None:
pins_struct = _build_pins_struct(pin_expressions, e_pin_num)
hub75_config = _build_config_struct(config, pins_struct, min_refresh)
# Rotation is handled by the hub75 driver (config_.rotation already set above).
# Force rotation to 0 for ESPHome's Display base class to avoid double-rotation.
if CONF_ROTATION in config:
config[CONF_ROTATION] = 0
# Create display and register
var = cg.new_Pvariable(config[CONF_ID], hub75_config)
await display.register_display(var, config)

View File

@@ -92,14 +92,25 @@ void HUB75Display::fill(Color color) {
if (!this->enabled_) [[unlikely]]
return;
// Special case: black (off) - use fast hardware clear
if (!color.is_on()) {
// Start with full display rect
display::Rect fill_rect(0, 0, this->get_width_internal(), this->get_height_internal());
// Apply clipping using Rect::shrink() to intersect
display::Rect clip = this->get_clipping();
if (clip.is_set()) {
fill_rect.shrink(clip);
if (!fill_rect.is_set())
return; // Completely clipped
}
// Fast path: black filling entire display
if (!color.is_on() && fill_rect.x == 0 && fill_rect.y == 0 && fill_rect.w == this->get_width_internal() &&
fill_rect.h == this->get_height_internal()) {
driver_->clear();
return;
}
// For non-black colors, fall back to base class (pixel-by-pixel)
Display::fill(color);
driver_->fill(fill_rect.x, fill_rect.y, fill_rect.w, fill_rect.h, color.r, color.g, color.b);
}
void HOT HUB75Display::draw_pixel_at(int x, int y, Color color) {

View File

@@ -39,8 +39,8 @@ class HUB75Display : public display::Display {
protected:
// Display internal methods
int get_width_internal() override { return config_.panel_width * config_.layout_cols; }
int get_height_internal() override { return config_.panel_height * config_.layout_rows; }
int get_width_internal() override { return this->driver_ != nullptr ? this->driver_->get_width() : 0; }
int get_height_internal() override { return this->driver_ != nullptr ? this->driver_->get_height() : 0; }
// Member variables
Hub75Driver *driver_{nullptr};

View File

@@ -28,6 +28,6 @@ dependencies:
rules:
- if: "target in [esp32s2, esp32s3, esp32p4]"
esphome/esp-hub75:
version: 0.1.7
version: 0.2.2
rules:
- if: "target in [esp32, esp32s2, esp32s3, esp32p4]"

View File

@@ -0,0 +1,39 @@
display:
- platform: hub75
id: my_hub75
board: apollo-automation-rev6
panel_width: 64
panel_height: 64
layout_rows: 1
layout_cols: 2
rotation: 90
bit_depth: 4
double_buffer: true
auto_clear_enabled: true
update_interval: 16ms
latch_blanking: 1
clock_speed: 20MHz
lambda: |-
// Test clipping: 8 columns x 4 rows of 16x16 colored squares
Color colors[32] = {
Color(255, 0, 0), Color(0, 255, 0), Color(0, 0, 255), Color(255, 255, 0),
Color(255, 0, 255), Color(0, 255, 255), Color(255, 128, 0), Color(128, 0, 255),
Color(0, 128, 255), Color(255, 0, 128), Color(128, 255, 0), Color(0, 255, 128),
Color(255, 128, 128), Color(128, 255, 128), Color(128, 128, 255), Color(255, 255, 128),
Color(255, 128, 255), Color(128, 255, 255), Color(192, 64, 0), Color(64, 192, 0),
Color(0, 64, 192), Color(192, 0, 64), Color(64, 0, 192), Color(0, 192, 64),
Color(128, 64, 64), Color(64, 128, 64), Color(64, 64, 128), Color(128, 128, 64),
Color(128, 64, 128), Color(64, 128, 128), Color(255, 255, 255), Color(128, 128, 128)
};
int idx = 0;
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 8; col++) {
// Clipping mode: clip to square bounds, then fill "entire screen"
it.start_clipping(col * 16, row * 16, (col + 1) * 16, (row + 1) * 16);
it.fill(colors[idx]);
it.end_clipping();
idx++;
}
}
<<: !include common.yaml