mirror of
https://github.com/esphome/esphome.git
synced 2025-10-09 03:38:43 +00:00
Compare commits
2 Commits
release_cl
...
base64_cha
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7bd8b1d137 | ||
![]() |
960a65e2f3 |
33
CODEOWNERS
33
CODEOWNERS
@@ -66,7 +66,7 @@ esphome/components/binary_sensor/* @esphome/core
|
||||
esphome/components/bk72xx/* @kuba2k2
|
||||
esphome/components/bl0906/* @athom-tech @jesserockz @tarontop
|
||||
esphome/components/bl0939/* @ziceva
|
||||
esphome/components/bl0940/* @dan-s-github @tobias-
|
||||
esphome/components/bl0940/* @tobias-
|
||||
esphome/components/bl0942/* @dbuezas @dwmw2
|
||||
esphome/components/ble_client/* @buxtronix @clydebarrow
|
||||
esphome/components/bluetooth_proxy/* @bdraco @jesserockz
|
||||
@@ -88,7 +88,7 @@ esphome/components/bp1658cj/* @Cossid
|
||||
esphome/components/bp5758d/* @Cossid
|
||||
esphome/components/button/* @esphome/core
|
||||
esphome/components/bytebuffer/* @clydebarrow
|
||||
esphome/components/camera/* @bdraco @DT-art1
|
||||
esphome/components/camera/* @DT-art1 @bdraco
|
||||
esphome/components/camera_encoder/* @DT-art1
|
||||
esphome/components/canbus/* @danielschramm @mvturnho
|
||||
esphome/components/cap1188/* @mreditor97
|
||||
@@ -145,9 +145,9 @@ esphome/components/es8156/* @kbx81
|
||||
esphome/components/es8311/* @kahrendt @kroimon
|
||||
esphome/components/es8388/* @P4uLT
|
||||
esphome/components/esp32/* @esphome/core
|
||||
esphome/components/esp32_ble/* @bdraco @jesserockz @Rapsssito
|
||||
esphome/components/esp32_ble/* @Rapsssito @bdraco @jesserockz
|
||||
esphome/components/esp32_ble_client/* @bdraco @jesserockz
|
||||
esphome/components/esp32_ble_server/* @clydebarrow @jesserockz @Rapsssito
|
||||
esphome/components/esp32_ble_server/* @Rapsssito @clydebarrow @jesserockz
|
||||
esphome/components/esp32_ble_tracker/* @bdraco
|
||||
esphome/components/esp32_camera_web_server/* @ayufan
|
||||
esphome/components/esp32_can/* @Sympatron
|
||||
@@ -167,7 +167,7 @@ esphome/components/ezo_pmp/* @carlos-sarmiento
|
||||
esphome/components/factory_reset/* @anatoly-savchenkov
|
||||
esphome/components/fastled_base/* @OttoWinter
|
||||
esphome/components/feedback/* @ianchi
|
||||
esphome/components/fingerprint_grow/* @alexborro @loongyh @OnFreund
|
||||
esphome/components/fingerprint_grow/* @OnFreund @alexborro @loongyh
|
||||
esphome/components/font/* @clydebarrow @esphome/core
|
||||
esphome/components/fs3000/* @kahrendt
|
||||
esphome/components/ft5x06/* @clydebarrow
|
||||
@@ -203,7 +203,7 @@ esphome/components/heatpumpir/* @rob-deutsch
|
||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||
esphome/components/hm3301/* @freekode
|
||||
esphome/components/hmac_md5/* @dwmw2
|
||||
esphome/components/homeassistant/* @esphome/core @OttoWinter
|
||||
esphome/components/homeassistant/* @OttoWinter @esphome/core
|
||||
esphome/components/homeassistant/number/* @landonr
|
||||
esphome/components/homeassistant/switch/* @Links2004
|
||||
esphome/components/honeywell_hih_i2c/* @Benichou34
|
||||
@@ -228,13 +228,13 @@ esphome/components/iaqcore/* @yozik04
|
||||
esphome/components/ili9xxx/* @clydebarrow @nielsnl68
|
||||
esphome/components/improv_base/* @esphome/core
|
||||
esphome/components/improv_serial/* @esphome/core
|
||||
esphome/components/ina226/* @latonita @Sergio303
|
||||
esphome/components/ina226/* @Sergio303 @latonita
|
||||
esphome/components/ina260/* @mreditor97
|
||||
esphome/components/ina2xx_base/* @latonita
|
||||
esphome/components/ina2xx_i2c/* @latonita
|
||||
esphome/components/ina2xx_spi/* @latonita
|
||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
||||
esphome/components/inkplate/* @jesserockz @JosipKuci
|
||||
esphome/components/inkplate6/* @jesserockz
|
||||
esphome/components/integration/* @OttoWinter
|
||||
esphome/components/internal_temperature/* @Mat931
|
||||
esphome/components/interval/* @esphome/core
|
||||
@@ -277,8 +277,8 @@ esphome/components/max7219digit/* @rspaargaren
|
||||
esphome/components/max9611/* @mckaymatthew
|
||||
esphome/components/mcp23008/* @jesserockz
|
||||
esphome/components/mcp23017/* @jesserockz
|
||||
esphome/components/mcp23s08/* @jesserockz @SenexCrenshaw
|
||||
esphome/components/mcp23s17/* @jesserockz @SenexCrenshaw
|
||||
esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz
|
||||
esphome/components/mcp23s17/* @SenexCrenshaw @jesserockz
|
||||
esphome/components/mcp23x08_base/* @jesserockz
|
||||
esphome/components/mcp23x17_base/* @jesserockz
|
||||
esphome/components/mcp23xxx_base/* @jesserockz
|
||||
@@ -299,7 +299,6 @@ esphome/components/mics_4514/* @jesserockz
|
||||
esphome/components/midea/* @dudanov
|
||||
esphome/components/midea_ir/* @dudanov
|
||||
esphome/components/mipi_dsi/* @clydebarrow
|
||||
esphome/components/mipi_rgb/* @clydebarrow
|
||||
esphome/components/mipi_spi/* @clydebarrow
|
||||
esphome/components/mitsubishi/* @RubyBailey
|
||||
esphome/components/mixer/speaker/* @kahrendt
|
||||
@@ -343,7 +342,7 @@ esphome/components/ota/* @esphome/core
|
||||
esphome/components/output/* @esphome/core
|
||||
esphome/components/packet_transport/* @clydebarrow
|
||||
esphome/components/pca6416a/* @Mat931
|
||||
esphome/components/pca9554/* @bdraco @clydebarrow @hwstar
|
||||
esphome/components/pca9554/* @clydebarrow @hwstar
|
||||
esphome/components/pcf85063/* @brogon
|
||||
esphome/components/pcf8563/* @KoenBreeman
|
||||
esphome/components/pi4ioe5v6408/* @jesserockz
|
||||
@@ -354,9 +353,9 @@ esphome/components/pm2005/* @andrewjswan
|
||||
esphome/components/pmsa003i/* @sjtrny
|
||||
esphome/components/pmsx003/* @ximex
|
||||
esphome/components/pmwcs3/* @SeByDocKy
|
||||
esphome/components/pn532/* @jesserockz @OttoWinter
|
||||
esphome/components/pn532_i2c/* @jesserockz @OttoWinter
|
||||
esphome/components/pn532_spi/* @jesserockz @OttoWinter
|
||||
esphome/components/pn532/* @OttoWinter @jesserockz
|
||||
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
||||
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
||||
esphome/components/pn7150/* @jesserockz @kbx81
|
||||
esphome/components/pn7150_i2c/* @jesserockz @kbx81
|
||||
esphome/components/pn7160/* @jesserockz @kbx81
|
||||
@@ -365,7 +364,7 @@ esphome/components/pn7160_spi/* @jesserockz @kbx81
|
||||
esphome/components/power_supply/* @esphome/core
|
||||
esphome/components/preferences/* @esphome/core
|
||||
esphome/components/psram/* @esphome/core
|
||||
esphome/components/pulse_meter/* @cstaahl @stevebaxter @TrentHouliston
|
||||
esphome/components/pulse_meter/* @TrentHouliston @cstaahl @stevebaxter
|
||||
esphome/components/pvvx_mithermometer/* @pasiz
|
||||
esphome/components/pylontech/* @functionpointer
|
||||
esphome/components/qmp6988/* @andrewpc
|
||||
@@ -406,7 +405,7 @@ esphome/components/sensirion_common/* @martgras
|
||||
esphome/components/sensor/* @esphome/core
|
||||
esphome/components/sfa30/* @ghsensdev
|
||||
esphome/components/sgp40/* @SenexCrenshaw
|
||||
esphome/components/sgp4x/* @martgras @SenexCrenshaw
|
||||
esphome/components/sgp4x/* @SenexCrenshaw @martgras
|
||||
esphome/components/shelly_dimmer/* @edge90 @rnauber
|
||||
esphome/components/sht3xd/* @mrtoy-me
|
||||
esphome/components/sht4x/* @sjtrny
|
||||
|
2
Doxyfile
2
Doxyfile
@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 2025.9.3
|
||||
PROJECT_NUMBER = 2025.9.0-dev
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
@@ -15,11 +15,9 @@ import argcomplete
|
||||
|
||||
from esphome import const, writer, yaml_util
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.mqtt import CONF_DISCOVER_IP
|
||||
from esphome.config import iter_component_configs, read_config, strip_default_ids
|
||||
from esphome.const import (
|
||||
ALLOWED_NAME_CHARS,
|
||||
CONF_API,
|
||||
CONF_BAUD_RATE,
|
||||
CONF_BROKER,
|
||||
CONF_DEASSERT_RTS_DTR,
|
||||
@@ -45,7 +43,6 @@ from esphome.const import (
|
||||
SECRETS_FILES,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError, coroutine
|
||||
from esphome.enum import StrEnum
|
||||
from esphome.helpers import get_bool_env, indent, is_ip_address
|
||||
from esphome.log import AnsiFore, color, setup_log
|
||||
from esphome.types import ConfigType
|
||||
@@ -109,15 +106,13 @@ def choose_prompt(options, purpose: str = None):
|
||||
return options[opt - 1][1]
|
||||
|
||||
|
||||
class Purpose(StrEnum):
|
||||
UPLOADING = "uploading"
|
||||
LOGGING = "logging"
|
||||
|
||||
|
||||
def choose_upload_log_host(
|
||||
default: list[str] | str | None,
|
||||
check_default: str | None,
|
||||
purpose: Purpose,
|
||||
show_ota: bool,
|
||||
show_mqtt: bool,
|
||||
show_api: bool,
|
||||
purpose: str | None = None,
|
||||
) -> list[str]:
|
||||
# Convert to list for uniform handling
|
||||
defaults = [default] if isinstance(default, str) else default or []
|
||||
@@ -137,30 +132,13 @@ def choose_upload_log_host(
|
||||
]
|
||||
resolved.append(choose_prompt(options, purpose=purpose))
|
||||
elif device == "OTA":
|
||||
# ensure IP adresses are used first
|
||||
if is_ip_address(CORE.address) and (
|
||||
(purpose == Purpose.LOGGING and has_api())
|
||||
or (purpose == Purpose.UPLOADING and has_ota())
|
||||
if CORE.address and (
|
||||
(show_ota and "ota" in CORE.config)
|
||||
or (show_api and "api" in CORE.config)
|
||||
):
|
||||
resolved.append(CORE.address)
|
||||
|
||||
if purpose == Purpose.LOGGING:
|
||||
if has_api() and has_mqtt_ip_lookup():
|
||||
resolved.append("MQTTIP")
|
||||
|
||||
if has_mqtt_logging():
|
||||
resolved.append("MQTT")
|
||||
|
||||
if has_api() and has_non_ip_address():
|
||||
resolved.append(CORE.address)
|
||||
|
||||
elif purpose == Purpose.UPLOADING:
|
||||
if has_ota() and has_mqtt_ip_lookup():
|
||||
resolved.append("MQTTIP")
|
||||
|
||||
if has_ota() and has_non_ip_address():
|
||||
resolved.append(CORE.address)
|
||||
|
||||
elif show_mqtt and has_mqtt_logging():
|
||||
resolved.append("MQTT")
|
||||
else:
|
||||
resolved.append(device)
|
||||
if not resolved:
|
||||
@@ -171,111 +149,39 @@ def choose_upload_log_host(
|
||||
options = [
|
||||
(f"{port.path} ({port.description})", port.path) for port in get_serial_ports()
|
||||
]
|
||||
|
||||
if purpose == Purpose.LOGGING:
|
||||
if has_mqtt_logging():
|
||||
mqtt_config = CORE.config[CONF_MQTT]
|
||||
options.append((f"MQTT ({mqtt_config[CONF_BROKER]})", "MQTT"))
|
||||
|
||||
if has_api():
|
||||
if has_resolvable_address():
|
||||
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
||||
if has_mqtt_ip_lookup():
|
||||
options.append(("Over The Air (MQTT IP lookup)", "MQTTIP"))
|
||||
|
||||
elif purpose == Purpose.UPLOADING and has_ota():
|
||||
if has_resolvable_address():
|
||||
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
||||
if has_mqtt_ip_lookup():
|
||||
options.append(("Over The Air (MQTT IP lookup)", "MQTTIP"))
|
||||
if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config):
|
||||
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
||||
if show_mqtt and has_mqtt_logging():
|
||||
mqtt_config = CORE.config[CONF_MQTT]
|
||||
options.append((f"MQTT ({mqtt_config[CONF_BROKER]})", "MQTT"))
|
||||
|
||||
if check_default is not None and check_default in [opt[1] for opt in options]:
|
||||
return [check_default]
|
||||
return [choose_prompt(options, purpose=purpose)]
|
||||
|
||||
|
||||
def has_mqtt_logging() -> bool:
|
||||
"""Check if MQTT logging is available."""
|
||||
if CONF_MQTT not in CORE.config:
|
||||
return False
|
||||
|
||||
mqtt_config = CORE.config[CONF_MQTT]
|
||||
|
||||
# enabled by default
|
||||
if CONF_LOG_TOPIC not in mqtt_config:
|
||||
return True
|
||||
|
||||
def mqtt_logging_enabled(mqtt_config):
|
||||
log_topic = mqtt_config[CONF_LOG_TOPIC]
|
||||
if log_topic is None:
|
||||
return False
|
||||
|
||||
if CONF_TOPIC not in log_topic:
|
||||
return False
|
||||
|
||||
return log_topic.get(CONF_LEVEL, None) != "NONE"
|
||||
|
||||
|
||||
def has_mqtt() -> bool:
|
||||
"""Check if MQTT is available."""
|
||||
return CONF_MQTT in CORE.config
|
||||
|
||||
|
||||
def has_api() -> bool:
|
||||
"""Check if API is available."""
|
||||
return CONF_API in CORE.config
|
||||
|
||||
|
||||
def has_ota() -> bool:
|
||||
"""Check if OTA is available."""
|
||||
return CONF_OTA in CORE.config
|
||||
|
||||
|
||||
def has_mqtt_ip_lookup() -> bool:
|
||||
"""Check if MQTT is available and IP lookup is supported."""
|
||||
if CONF_MQTT not in CORE.config:
|
||||
return False
|
||||
# Default Enabled
|
||||
if CONF_DISCOVER_IP not in CORE.config[CONF_MQTT]:
|
||||
return True
|
||||
return CORE.config[CONF_MQTT][CONF_DISCOVER_IP]
|
||||
|
||||
|
||||
def has_mdns() -> bool:
|
||||
"""Check if MDNS is available."""
|
||||
return CONF_MDNS not in CORE.config or not CORE.config[CONF_MDNS][CONF_DISABLED]
|
||||
|
||||
|
||||
def has_non_ip_address() -> bool:
|
||||
"""Check if CORE.address is set and is not an IP address."""
|
||||
return CORE.address is not None and not is_ip_address(CORE.address)
|
||||
|
||||
|
||||
def has_ip_address() -> bool:
|
||||
"""Check if CORE.address is a valid IP address."""
|
||||
return CORE.address is not None and is_ip_address(CORE.address)
|
||||
|
||||
|
||||
def has_resolvable_address() -> bool:
|
||||
"""Check if CORE.address is resolvable (via mDNS or is an IP address)."""
|
||||
return has_mdns() or has_ip_address()
|
||||
|
||||
|
||||
def mqtt_get_ip(config: ConfigType, username: str, password: str, client_id: str):
|
||||
from esphome import mqtt
|
||||
|
||||
return mqtt.get_esphome_device_ip(config, username, password, client_id)
|
||||
|
||||
|
||||
_PORT_TO_PORT_TYPE = {
|
||||
"MQTT": "MQTT",
|
||||
"MQTTIP": "MQTTIP",
|
||||
}
|
||||
def has_mqtt_logging() -> bool:
|
||||
"""Check if MQTT logging is available."""
|
||||
return (mqtt_config := CORE.config.get(CONF_MQTT)) and mqtt_logging_enabled(
|
||||
mqtt_config
|
||||
)
|
||||
|
||||
|
||||
def get_port_type(port: str) -> str:
|
||||
if port.startswith("/") or port.startswith("COM"):
|
||||
return "SERIAL"
|
||||
return _PORT_TO_PORT_TYPE.get(port, "NETWORK")
|
||||
if port == "MQTT":
|
||||
return "MQTT"
|
||||
return "NETWORK"
|
||||
|
||||
|
||||
def run_miniterm(config: ConfigType, port: str, args) -> int:
|
||||
@@ -320,9 +226,7 @@ def run_miniterm(config: ConfigType, port: str, args) -> int:
|
||||
.replace(b"\n", b"")
|
||||
.decode("utf8", "backslashreplace")
|
||||
)
|
||||
time_ = datetime.now()
|
||||
nanoseconds = time_.microsecond // 1000
|
||||
time_str = f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}.{nanoseconds:03}]"
|
||||
time_str = datetime.now().time().strftime("[%H:%M:%S]")
|
||||
safe_print(parser.parse_line(line, time_str))
|
||||
|
||||
backtrace_state = platformio_api.process_stacktrace(
|
||||
@@ -492,29 +396,27 @@ def check_permissions(port: str):
|
||||
)
|
||||
|
||||
|
||||
def upload_program(
|
||||
config: ConfigType, args: ArgsProtocol, devices: list[str]
|
||||
) -> tuple[int, str | None]:
|
||||
host = devices[0]
|
||||
def upload_program(config: ConfigType, args: ArgsProtocol, host: str) -> int | str:
|
||||
try:
|
||||
module = importlib.import_module("esphome.components." + CORE.target_platform)
|
||||
if getattr(module, "upload_program")(config, args, host):
|
||||
return 0, host
|
||||
return 0
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if get_port_type(host) == "SERIAL":
|
||||
check_permissions(host)
|
||||
|
||||
exit_code = 1
|
||||
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
|
||||
file = getattr(args, "file", None)
|
||||
exit_code = upload_using_esptool(config, host, file, args.upload_speed)
|
||||
elif CORE.target_platform == PLATFORM_RP2040 or CORE.is_libretiny:
|
||||
exit_code = upload_using_platformio(config, host)
|
||||
# else: Unknown target platform, exit_code remains 1
|
||||
return upload_using_esptool(config, host, file, args.upload_speed)
|
||||
|
||||
return exit_code, host if exit_code == 0 else None
|
||||
if CORE.target_platform in (PLATFORM_RP2040):
|
||||
return upload_using_platformio(config, host)
|
||||
|
||||
if CORE.is_libretiny:
|
||||
return upload_using_platformio(config, host)
|
||||
|
||||
return 1 # Unknown target platform
|
||||
|
||||
ota_conf = {}
|
||||
for ota_item in config.get(CONF_OTA, []):
|
||||
@@ -531,23 +433,31 @@ def upload_program(
|
||||
|
||||
remote_port = int(ota_conf[CONF_PORT])
|
||||
password = ota_conf.get(CONF_PASSWORD, "")
|
||||
binary = args.file if getattr(args, "file", None) is not None else CORE.firmware_bin
|
||||
|
||||
# MQTT address resolution
|
||||
if get_port_type(host) in ("MQTT", "MQTTIP"):
|
||||
devices = mqtt_get_ip(config, args.username, args.password, args.client_id)
|
||||
# Check if we should use MQTT for address resolution
|
||||
# This happens when no device was specified, or the current host is "MQTT"/"OTA"
|
||||
devices: list[str] = args.device or []
|
||||
if (
|
||||
CONF_MQTT in config # pylint: disable=too-many-boolean-expressions
|
||||
and (not devices or host in ("MQTT", "OTA"))
|
||||
and (
|
||||
((config[CONF_MDNS][CONF_DISABLED]) and not is_ip_address(CORE.address))
|
||||
or get_port_type(host) == "MQTT"
|
||||
)
|
||||
):
|
||||
from esphome import mqtt
|
||||
|
||||
return espota2.run_ota(devices, remote_port, password, binary)
|
||||
host = mqtt.get_esphome_device_ip(
|
||||
config, args.username, args.password, args.client_id
|
||||
)
|
||||
|
||||
if getattr(args, "file", None) is not None:
|
||||
return espota2.run_ota(host, remote_port, password, args.file)
|
||||
|
||||
return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
|
||||
|
||||
|
||||
def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int | None:
|
||||
try:
|
||||
module = importlib.import_module("esphome.components." + CORE.target_platform)
|
||||
if getattr(module, "show_logs")(config, args, devices):
|
||||
return 0
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if "logger" not in config:
|
||||
raise EsphomeError("Logger is not configured!")
|
||||
|
||||
@@ -556,28 +466,20 @@ def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int
|
||||
if get_port_type(port) == "SERIAL":
|
||||
check_permissions(port)
|
||||
return run_miniterm(config, port, args)
|
||||
if get_port_type(port) == "NETWORK" and "api" in config:
|
||||
addresses_to_use = devices
|
||||
if config[CONF_MDNS][CONF_DISABLED] and CONF_MQTT in config:
|
||||
from esphome import mqtt
|
||||
|
||||
port_type = get_port_type(port)
|
||||
|
||||
# Check if we should use API for logging
|
||||
if has_api():
|
||||
addresses_to_use: list[str] | None = None
|
||||
|
||||
if port_type == "NETWORK" and (has_mdns() or is_ip_address(port)):
|
||||
addresses_to_use = devices
|
||||
elif port_type in ("NETWORK", "MQTT", "MQTTIP") and has_mqtt_ip_lookup():
|
||||
# Only use MQTT IP lookup if the first condition didn't match
|
||||
# (for MQTT/MQTTIP types, or for NETWORK when mdns/ip check fails)
|
||||
addresses_to_use = mqtt_get_ip(
|
||||
mqtt_address = mqtt.get_esphome_device_ip(
|
||||
config, args.username, args.password, args.client_id
|
||||
)
|
||||
)[0]
|
||||
addresses_to_use = [mqtt_address]
|
||||
|
||||
if addresses_to_use is not None:
|
||||
from esphome.components.api.client import run_logs
|
||||
from esphome.components.api.client import run_logs
|
||||
|
||||
return run_logs(config, addresses_to_use)
|
||||
|
||||
if port_type in ("NETWORK", "MQTT") and has_mqtt_logging():
|
||||
return run_logs(config, addresses_to_use)
|
||||
if get_port_type(port) in ("NETWORK", "MQTT") and "mqtt" in config:
|
||||
from esphome import mqtt
|
||||
|
||||
return mqtt.show_logs(
|
||||
@@ -643,14 +545,23 @@ def command_upload(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
devices = choose_upload_log_host(
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
purpose=Purpose.UPLOADING,
|
||||
show_ota=True,
|
||||
show_mqtt=False,
|
||||
show_api=False,
|
||||
purpose="uploading",
|
||||
)
|
||||
|
||||
exit_code, _ = upload_program(config, args, devices)
|
||||
if exit_code == 0:
|
||||
_LOGGER.info("Successfully uploaded program.")
|
||||
else:
|
||||
_LOGGER.warning("Failed to upload to %s", devices)
|
||||
# Try each device until one succeeds
|
||||
exit_code = 1
|
||||
for device in devices:
|
||||
_LOGGER.info("Uploading to %s", device)
|
||||
exit_code = upload_program(config, args, device)
|
||||
if exit_code == 0:
|
||||
_LOGGER.info("Successfully uploaded program.")
|
||||
return 0
|
||||
if len(devices) > 1:
|
||||
_LOGGER.warning("Failed to upload to %s", device)
|
||||
|
||||
return exit_code
|
||||
|
||||
|
||||
@@ -668,7 +579,10 @@ def command_logs(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
devices = choose_upload_log_host(
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
purpose=Purpose.LOGGING,
|
||||
show_ota=False,
|
||||
show_mqtt=True,
|
||||
show_api=True,
|
||||
purpose="logging",
|
||||
)
|
||||
return show_logs(config, args, devices)
|
||||
|
||||
@@ -694,14 +608,25 @@ def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
devices = choose_upload_log_host(
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
purpose=Purpose.UPLOADING,
|
||||
show_ota=True,
|
||||
show_mqtt=False,
|
||||
show_api=True,
|
||||
purpose="uploading",
|
||||
)
|
||||
|
||||
exit_code, successful_device = upload_program(config, args, devices)
|
||||
if exit_code == 0:
|
||||
_LOGGER.info("Successfully uploaded program.")
|
||||
else:
|
||||
_LOGGER.warning("Failed to upload to %s", devices)
|
||||
# Try each device for upload until one succeeds
|
||||
successful_device: str | None = None
|
||||
for device in devices:
|
||||
_LOGGER.info("Uploading to %s", device)
|
||||
exit_code = upload_program(config, args, device)
|
||||
if exit_code == 0:
|
||||
_LOGGER.info("Successfully uploaded program.")
|
||||
successful_device = device
|
||||
break
|
||||
if len(devices) > 1:
|
||||
_LOGGER.warning("Failed to upload to %s", device)
|
||||
|
||||
if successful_device is None:
|
||||
return exit_code
|
||||
|
||||
if args.no_logs:
|
||||
@@ -711,7 +636,10 @@ def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
|
||||
devices = choose_upload_log_host(
|
||||
default=successful_device,
|
||||
check_default=successful_device,
|
||||
purpose=Purpose.LOGGING,
|
||||
show_ota=False,
|
||||
show_mqtt=True,
|
||||
show_api=True,
|
||||
purpose="logging",
|
||||
)
|
||||
return show_logs(config, args, devices)
|
||||
|
||||
|
@@ -11,8 +11,15 @@ from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
)
|
||||
from esphome.config_helpers import filter_source_files_from_platform
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER, PLATFORM_ESP8266
|
||||
from esphome.const import (
|
||||
CONF_ANALOG,
|
||||
CONF_INPUT,
|
||||
CONF_NUMBER,
|
||||
PLATFORM_ESP8266,
|
||||
PlatformFramework,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
@@ -266,3 +273,21 @@ def validate_adc_pin(value):
|
||||
)(value)
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
{
|
||||
"adc_sensor_esp32.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP32_IDF,
|
||||
},
|
||||
"adc_sensor_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO},
|
||||
"adc_sensor_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO},
|
||||
"adc_sensor_libretiny.cpp": {
|
||||
PlatformFramework.BK72XX_ARDUINO,
|
||||
PlatformFramework.RTL87XX_ARDUINO,
|
||||
PlatformFramework.LN882X_ARDUINO,
|
||||
},
|
||||
"adc_sensor_zephyr.cpp": {PlatformFramework.NRF52_ZEPHYR},
|
||||
}
|
||||
)
|
||||
|
@@ -241,8 +241,6 @@ float ADCSensor::sample_autorange_() {
|
||||
cali_config.bitwidth = ADC_BITWIDTH_DEFAULT;
|
||||
|
||||
err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
|
||||
ESP_LOGVV(TAG, "Autorange atten=%d: Calibration handle creation %s (err=%d)", atten,
|
||||
(err == ESP_OK) ? "SUCCESS" : "FAILED", err);
|
||||
#else
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = this->adc_unit_,
|
||||
@@ -253,14 +251,10 @@ float ADCSensor::sample_autorange_() {
|
||||
#endif
|
||||
};
|
||||
err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
|
||||
ESP_LOGVV(TAG, "Autorange atten=%d: Calibration handle creation %s (err=%d)", atten,
|
||||
(err == ESP_OK) ? "SUCCESS" : "FAILED", err);
|
||||
#endif
|
||||
|
||||
int raw;
|
||||
err = adc_oneshot_read(this->adc_handle_, this->channel_, &raw);
|
||||
ESP_LOGVV(TAG, "Autorange atten=%d: Raw ADC read %s, value=%d (err=%d)", atten,
|
||||
(err == ESP_OK) ? "SUCCESS" : "FAILED", raw, err);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "ADC read failed in autorange with error %d", err);
|
||||
@@ -281,10 +275,8 @@ float ADCSensor::sample_autorange_() {
|
||||
err = adc_cali_raw_to_voltage(handle, raw, &voltage_mv);
|
||||
if (err == ESP_OK) {
|
||||
voltage = voltage_mv / 1000.0f;
|
||||
ESP_LOGVV(TAG, "Autorange atten=%d: CALIBRATED - raw=%d -> %dmV -> %.6fV", atten, raw, voltage_mv, voltage);
|
||||
} else {
|
||||
voltage = raw * 3.3f / 4095.0f;
|
||||
ESP_LOGVV(TAG, "Autorange atten=%d: UNCALIBRATED FALLBACK - raw=%d -> %.6fV (3.3V ref)", atten, raw, voltage);
|
||||
}
|
||||
// Clean up calibration handle
|
||||
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
|
||||
@@ -295,7 +287,6 @@ float ADCSensor::sample_autorange_() {
|
||||
#endif
|
||||
} else {
|
||||
voltage = raw * 3.3f / 4095.0f;
|
||||
ESP_LOGVV(TAG, "Autorange atten=%d: NO CALIBRATION - raw=%d -> %.6fV (3.3V ref)", atten, raw, voltage);
|
||||
}
|
||||
|
||||
return {raw, voltage};
|
||||
@@ -333,32 +324,18 @@ float ADCSensor::sample_autorange_() {
|
||||
}
|
||||
|
||||
const int adc_half = 2048;
|
||||
const uint32_t c12 = std::min(raw12, adc_half);
|
||||
|
||||
const int32_t c6_signed = adc_half - std::abs(raw6 - adc_half);
|
||||
const uint32_t c6 = (c6_signed > 0) ? c6_signed : 0; // Clamp to prevent underflow
|
||||
|
||||
const int32_t c2_signed = adc_half - std::abs(raw2 - adc_half);
|
||||
const uint32_t c2 = (c2_signed > 0) ? c2_signed : 0; // Clamp to prevent underflow
|
||||
|
||||
const uint32_t c0 = std::min(4095 - raw0, adc_half);
|
||||
const uint32_t csum = c12 + c6 + c2 + c0;
|
||||
|
||||
ESP_LOGVV(TAG, "Autorange summary:");
|
||||
ESP_LOGVV(TAG, " Raw readings: 12db=%d, 6db=%d, 2.5db=%d, 0db=%d", raw12, raw6, raw2, raw0);
|
||||
ESP_LOGVV(TAG, " Voltages: 12db=%.6f, 6db=%.6f, 2.5db=%.6f, 0db=%.6f", mv12, mv6, mv2, mv0);
|
||||
ESP_LOGVV(TAG, " Coefficients: c12=%u, c6=%u, c2=%u, c0=%u, sum=%u", c12, c6, c2, c0, csum);
|
||||
uint32_t c12 = std::min(raw12, adc_half);
|
||||
uint32_t c6 = adc_half - std::abs(raw6 - adc_half);
|
||||
uint32_t c2 = adc_half - std::abs(raw2 - adc_half);
|
||||
uint32_t c0 = std::min(4095 - raw0, adc_half);
|
||||
uint32_t csum = c12 + c6 + c2 + c0;
|
||||
|
||||
if (csum == 0) {
|
||||
ESP_LOGE(TAG, "Invalid weight sum in autorange calculation");
|
||||
return NAN;
|
||||
}
|
||||
|
||||
const float final_result = (mv12 * c12 + mv6 * c6 + mv2 * c2 + mv0 * c0) / csum;
|
||||
ESP_LOGV(TAG, "Autorange final: (%.6f*%u + %.6f*%u + %.6f*%u + %.6f*%u)/%u = %.6fV", mv12, c12, mv6, c6, mv2, c2, mv0,
|
||||
c0, csum, final_result);
|
||||
|
||||
return final_result;
|
||||
return (mv12 * c12 + mv6 * c6 + mv2 * c2 + mv0 * c0) / csum;
|
||||
}
|
||||
|
||||
} // namespace adc
|
||||
|
@@ -9,7 +9,6 @@ from esphome.components.zephyr import (
|
||||
zephyr_add_prj_conf,
|
||||
zephyr_add_user,
|
||||
)
|
||||
from esphome.config_helpers import filter_source_files_from_platform
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ATTENUATION,
|
||||
@@ -21,7 +20,6 @@ from esphome.const import (
|
||||
PLATFORM_NRF52,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_VOLT,
|
||||
PlatformFramework,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
@@ -176,21 +174,3 @@ async def to_code(config):
|
||||
}};
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
{
|
||||
"adc_sensor_esp32.cpp": {
|
||||
PlatformFramework.ESP32_ARDUINO,
|
||||
PlatformFramework.ESP32_IDF,
|
||||
},
|
||||
"adc_sensor_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO},
|
||||
"adc_sensor_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO},
|
||||
"adc_sensor_libretiny.cpp": {
|
||||
PlatformFramework.BK72XX_ARDUINO,
|
||||
PlatformFramework.RTL87XX_ARDUINO,
|
||||
PlatformFramework.LN882X_ARDUINO,
|
||||
},
|
||||
"adc_sensor_zephyr.cpp": {PlatformFramework.NRF52_ZEPHYR},
|
||||
}
|
||||
)
|
||||
|
@@ -113,7 +113,7 @@ void ADE7880::update() {
|
||||
if (this->channel_a_ != nullptr) {
|
||||
auto *chan = this->channel_a_;
|
||||
this->update_sensor_from_s24zp_register16_(chan->current, AIRMS, [](float val) { return val / 100000.0f; });
|
||||
this->update_sensor_from_s24zp_register16_(chan->voltage, AVRMS, [](float val) { return val / 10000.0f; });
|
||||
this->update_sensor_from_s24zp_register16_(chan->voltage, BVRMS, [](float val) { return val / 10000.0f; });
|
||||
this->update_sensor_from_s24zp_register16_(chan->active_power, AWATT, [](float val) { return val / 100.0f; });
|
||||
this->update_sensor_from_s24zp_register16_(chan->apparent_power, AVA, [](float val) { return val / 100.0f; });
|
||||
this->update_sensor_from_s16_register16_(chan->power_factor, APF,
|
||||
|
@@ -193,7 +193,6 @@ async def to_code(config):
|
||||
if key := encryption_config.get(CONF_KEY):
|
||||
decoded = base64.b64decode(key)
|
||||
cg.add(var.set_noise_psk(list(decoded)))
|
||||
cg.add_define("USE_API_NOISE_PSK_FROM_YAML")
|
||||
else:
|
||||
# No key provided, but encryption desired
|
||||
# This will allow a plaintext client to provide a noise key,
|
||||
|
@@ -27,6 +27,9 @@ service APIConnection {
|
||||
rpc subscribe_logs (SubscribeLogsRequest) returns (void) {}
|
||||
rpc subscribe_homeassistant_services (SubscribeHomeassistantServicesRequest) returns (void) {}
|
||||
rpc subscribe_home_assistant_states (SubscribeHomeAssistantStatesRequest) returns (void) {}
|
||||
rpc get_time (GetTimeRequest) returns (GetTimeResponse) {
|
||||
option (needs_authentication) = false;
|
||||
}
|
||||
rpc execute_service (ExecuteServiceRequest) returns (void) {}
|
||||
rpc noise_encryption_set_key (NoiseEncryptionSetKeyRequest) returns (NoiseEncryptionSetKeyResponse) {}
|
||||
|
||||
@@ -806,16 +809,15 @@ message HomeAssistantStateResponse {
|
||||
// ==================== IMPORT TIME ====================
|
||||
message GetTimeRequest {
|
||||
option (id) = 36;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (source) = SOURCE_BOTH;
|
||||
}
|
||||
|
||||
message GetTimeResponse {
|
||||
option (id) = 37;
|
||||
option (source) = SOURCE_CLIENT;
|
||||
option (source) = SOURCE_BOTH;
|
||||
option (no_delay) = true;
|
||||
|
||||
fixed32 epoch_seconds = 1;
|
||||
string timezone = 2;
|
||||
}
|
||||
|
||||
// ==================== USER-DEFINES SERVICES ====================
|
||||
|
@@ -42,8 +42,6 @@ static constexpr uint8_t MAX_PING_RETRIES = 60;
|
||||
static constexpr uint16_t PING_RETRY_INTERVAL = 1000;
|
||||
static constexpr uint32_t KEEPALIVE_DISCONNECT_TIMEOUT = (KEEPALIVE_TIMEOUT_MS * 5) / 2;
|
||||
|
||||
static constexpr auto ESPHOME_VERSION_REF = StringRef::from_lit(ESPHOME_VERSION);
|
||||
|
||||
static const char *const TAG = "api.connection";
|
||||
#ifdef USE_CAMERA
|
||||
static const int CAMERA_STOP_STREAM = 5000;
|
||||
@@ -1072,17 +1070,17 @@ void APIConnection::camera_image(const CameraImageRequest &msg) {
|
||||
|
||||
#ifdef USE_HOMEASSISTANT_TIME
|
||||
void APIConnection::on_get_time_response(const GetTimeResponse &value) {
|
||||
if (homeassistant::global_homeassistant_time != nullptr) {
|
||||
if (homeassistant::global_homeassistant_time != nullptr)
|
||||
homeassistant::global_homeassistant_time->set_epoch_time(value.epoch_seconds);
|
||||
#ifdef USE_TIME_TIMEZONE
|
||||
if (!value.timezone.empty() && value.timezone != homeassistant::global_homeassistant_time->get_timezone()) {
|
||||
homeassistant::global_homeassistant_time->set_timezone(value.timezone);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool APIConnection::send_get_time_response(const GetTimeRequest &msg) {
|
||||
GetTimeResponse resp;
|
||||
resp.epoch_seconds = ::time(nullptr);
|
||||
return this->send_message(resp, GetTimeResponse::MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIConnection::subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||
bluetooth_proxy::global_bluetooth_proxy->subscribe_api_connection(this, msg.flags);
|
||||
@@ -1372,8 +1370,9 @@ bool APIConnection::send_hello_response(const HelloRequest &msg) {
|
||||
HelloResponse resp;
|
||||
resp.api_version_major = 1;
|
||||
resp.api_version_minor = 12;
|
||||
// Send only the version string - the client only logs this for debugging and doesn't use it otherwise
|
||||
resp.set_server_info(ESPHOME_VERSION_REF);
|
||||
// Temporary string for concatenation - will be valid during send_message call
|
||||
std::string server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
||||
resp.set_server_info(StringRef(server_info));
|
||||
resp.set_name(StringRef(App.get_name()));
|
||||
|
||||
#ifdef USE_API_PASSWORD
|
||||
@@ -1420,6 +1419,8 @@ bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) {
|
||||
std::string mac_address = get_mac_address_pretty();
|
||||
resp.set_mac_address(StringRef(mac_address));
|
||||
|
||||
// Compile-time StringRef constants
|
||||
static constexpr auto ESPHOME_VERSION_REF = StringRef::from_lit(ESPHOME_VERSION);
|
||||
resp.set_esphome_version(ESPHOME_VERSION_REF);
|
||||
|
||||
resp.set_compilation_time(App.get_compilation_time_ref());
|
||||
|
@@ -219,6 +219,7 @@ class APIConnection final : public APIServerConnection {
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override;
|
||||
#endif
|
||||
bool send_get_time_response(const GetTimeRequest &msg) override;
|
||||
#ifdef USE_API_SERVICES
|
||||
void execute_service(const ExecuteServiceRequest &msg) override;
|
||||
#endif
|
||||
|
@@ -10,10 +10,6 @@
|
||||
#include <cstring>
|
||||
#include <cinttypes>
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
#include <pgmspace.h>
|
||||
#endif
|
||||
|
||||
namespace esphome::api {
|
||||
|
||||
static const char *const TAG = "api.plaintext";
|
||||
@@ -201,20 +197,11 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
||||
// We must send at least 3 bytes to be read, so we add
|
||||
// a message after the indicator byte to ensures its long
|
||||
// enough and can aid in debugging.
|
||||
static constexpr uint8_t INDICATOR_MSG_SIZE = 19;
|
||||
#ifdef USE_ESP8266
|
||||
static const char MSG_PROGMEM[] PROGMEM = "\x00"
|
||||
"Bad indicator byte";
|
||||
char msg[INDICATOR_MSG_SIZE];
|
||||
memcpy_P(msg, MSG_PROGMEM, INDICATOR_MSG_SIZE);
|
||||
const char msg[] = "\x00"
|
||||
"Bad indicator byte";
|
||||
iov[0].iov_base = (void *) msg;
|
||||
#else
|
||||
static const char MSG[] = "\x00"
|
||||
"Bad indicator byte";
|
||||
iov[0].iov_base = (void *) MSG;
|
||||
#endif
|
||||
iov[0].iov_len = INDICATOR_MSG_SIZE;
|
||||
this->write_raw_(iov, 1, INDICATOR_MSG_SIZE);
|
||||
iov[0].iov_len = 19;
|
||||
this->write_raw_(iov, 1, 19);
|
||||
}
|
||||
return aerr;
|
||||
}
|
||||
|
@@ -901,16 +901,6 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
bool GetTimeResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2:
|
||||
this->timezone = value.as_string();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 1:
|
||||
@@ -921,6 +911,8 @@ bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void GetTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->epoch_seconds); }
|
||||
void GetTimeResponse::calculate_size(ProtoSize &size) const { size.add_fixed32(1, this->epoch_seconds); }
|
||||
#ifdef USE_API_SERVICES
|
||||
void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->name_ref_);
|
||||
|
@@ -1174,19 +1174,19 @@ class GetTimeRequest final : public ProtoMessage {
|
||||
class GetTimeResponse final : public ProtoDecodableMessage {
|
||||
public:
|
||||
static constexpr uint8_t MESSAGE_TYPE = 37;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 14;
|
||||
static constexpr uint8_t ESTIMATED_SIZE = 5;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
const char *message_name() const override { return "get_time_response"; }
|
||||
#endif
|
||||
uint32_t epoch_seconds{0};
|
||||
std::string timezone{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
};
|
||||
#ifdef USE_API_SERVICES
|
||||
class ListEntitiesServicesArgument final : public ProtoMessage {
|
||||
|
@@ -1110,11 +1110,7 @@ void HomeAssistantStateResponse::dump_to(std::string &out) const {
|
||||
}
|
||||
#endif
|
||||
void GetTimeRequest::dump_to(std::string &out) const { out.append("GetTimeRequest {}"); }
|
||||
void GetTimeResponse::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "GetTimeResponse");
|
||||
dump_field(out, "epoch_seconds", this->epoch_seconds);
|
||||
dump_field(out, "timezone", this->timezone);
|
||||
}
|
||||
void GetTimeResponse::dump_to(std::string &out) const { dump_field(out, "epoch_seconds", this->epoch_seconds); }
|
||||
#ifdef USE_API_SERVICES
|
||||
void ListEntitiesServicesArgument::dump_to(std::string &out) const {
|
||||
MessageDumpHelper helper(out, "ListEntitiesServicesArgument");
|
||||
|
@@ -160,6 +160,15 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case GetTimeRequest::MESSAGE_TYPE: {
|
||||
GetTimeRequest msg;
|
||||
// Empty message: no decode needed
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_get_time_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_get_time_request(msg);
|
||||
break;
|
||||
}
|
||||
case GetTimeResponse::MESSAGE_TYPE: {
|
||||
GetTimeResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
@@ -647,6 +656,11 @@ void APIServerConnection::on_subscribe_home_assistant_states_request(const Subsc
|
||||
}
|
||||
}
|
||||
#endif
|
||||
void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
|
||||
if (this->check_connection_setup_() && !this->send_get_time_response(msg)) {
|
||||
this->on_fatal_error();
|
||||
}
|
||||
}
|
||||
#ifdef USE_API_SERVICES
|
||||
void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) {
|
||||
if (this->check_authenticated_()) {
|
||||
|
@@ -71,7 +71,7 @@ class APIServerConnectionBase : public ProtoService {
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
virtual void on_home_assistant_state_response(const HomeAssistantStateResponse &value){};
|
||||
#endif
|
||||
|
||||
virtual void on_get_time_request(const GetTimeRequest &value){};
|
||||
virtual void on_get_time_response(const GetTimeResponse &value){};
|
||||
|
||||
#ifdef USE_API_SERVICES
|
||||
@@ -226,6 +226,7 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
|
||||
#endif
|
||||
virtual bool send_get_time_response(const GetTimeRequest &msg) = 0;
|
||||
#ifdef USE_API_SERVICES
|
||||
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
|
||||
#endif
|
||||
@@ -347,6 +348,7 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
|
||||
#endif
|
||||
void on_get_time_request(const GetTimeRequest &msg) override;
|
||||
#ifdef USE_API_SERVICES
|
||||
void on_execute_service_request(const ExecuteServiceRequest &msg) override;
|
||||
#endif
|
||||
|
@@ -37,14 +37,12 @@ void APIServer::setup() {
|
||||
|
||||
this->noise_pref_ = global_preferences->make_preference<SavedNoisePsk>(hash, true);
|
||||
|
||||
#ifndef USE_API_NOISE_PSK_FROM_YAML
|
||||
// Only load saved PSK if not set from YAML
|
||||
SavedNoisePsk noise_pref_saved{};
|
||||
if (this->noise_pref_.load(&noise_pref_saved)) {
|
||||
ESP_LOGD(TAG, "Loaded saved Noise PSK");
|
||||
|
||||
this->set_noise_psk(noise_pref_saved.psk);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Schedule reboot if no clients connect within timeout
|
||||
@@ -411,12 +409,6 @@ void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeo
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
|
||||
#ifdef USE_API_NOISE_PSK_FROM_YAML
|
||||
// When PSK is set from YAML, this function should never be called
|
||||
// but if it is, reject the change
|
||||
ESP_LOGW(TAG, "Key set in YAML");
|
||||
return false;
|
||||
#else
|
||||
auto &old_psk = this->noise_ctx_->get_psk();
|
||||
if (std::equal(old_psk.begin(), old_psk.end(), psk.begin())) {
|
||||
ESP_LOGW(TAG, "New PSK matches old");
|
||||
@@ -445,7 +437,6 @@ bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
|
||||
});
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@@ -62,11 +62,9 @@ async def async_run_logs(config: dict[str, Any], addresses: list[str]) -> None:
|
||||
time_ = datetime.now()
|
||||
message: bytes = msg.message
|
||||
text = message.decode("utf8", "backslashreplace")
|
||||
nanoseconds = time_.microsecond // 1000
|
||||
timestamp = (
|
||||
f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}.{nanoseconds:03}]"
|
||||
)
|
||||
for parsed_msg in parse_log_message(text, timestamp):
|
||||
for parsed_msg in parse_log_message(
|
||||
text, f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]"
|
||||
):
|
||||
print(parsed_msg.replace("\033", "\\033") if dashboard else parsed_msg)
|
||||
|
||||
stop = await async_run(cli, on_log, name=name)
|
||||
|
@@ -16,7 +16,6 @@ from esphome.const import (
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_POWER_FACTOR,
|
||||
DEVICE_CLASS_REACTIVE_POWER,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_CURRENT_AC,
|
||||
ICON_LIGHTBULB,
|
||||
@@ -79,7 +78,6 @@ CONFIG_SCHEMA = (
|
||||
unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
|
||||
icon=ICON_LIGHTBULB,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_REACTIVE_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
|
||||
|
@@ -17,12 +17,10 @@ from esphome.const import (
|
||||
CONF_REACTIVE_POWER,
|
||||
CONF_REVERSE_ACTIVE_ENERGY,
|
||||
CONF_VOLTAGE,
|
||||
DEVICE_CLASS_APPARENT_POWER,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_POWER_FACTOR,
|
||||
DEVICE_CLASS_REACTIVE_POWER,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
@@ -102,13 +100,13 @@ ATM90E32_PHASE_SCHEMA = cv.Schema(
|
||||
unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
|
||||
icon=ICON_LIGHTBULB,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_REACTIVE_POWER,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_APPARENT_POWER): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT_AMPS,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_APPARENT_POWER,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
|
||||
|
@@ -493,7 +493,7 @@ void BedJetHub::dump_config() {
|
||||
" ble_client.app_id: %d\n"
|
||||
" ble_client.conn_id: %d",
|
||||
this->get_name().c_str(), this->parent()->app_id, this->parent()->get_conn_id());
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_UPDATE_INTERVAL(this)
|
||||
ESP_LOGCONFIG(TAG, " Child components (%d):", this->children_.size());
|
||||
for (auto *child : this->children_) {
|
||||
ESP_LOGCONFIG(TAG, " - %s", child->describe().c_str());
|
||||
|
@@ -1,6 +1 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
CODEOWNERS = ["@tobias-", "@dan-s-github"]
|
||||
|
||||
CONF_BL0940_ID = "bl0940_id"
|
||||
bl0940_ns = cg.esphome_ns.namespace("bl0940")
|
||||
CODEOWNERS = ["@tobias-"]
|
||||
|
@@ -7,26 +7,28 @@ namespace bl0940 {
|
||||
|
||||
static const char *const TAG = "bl0940";
|
||||
|
||||
static const uint8_t BL0940_READ_COMMAND = 0x50; // 0x58 according to documentation
|
||||
static const uint8_t BL0940_FULL_PACKET = 0xAA;
|
||||
static const uint8_t BL0940_PACKET_HEADER = 0x55; // 0x58 according to en doc but 0x55 in cn doc
|
||||
static const uint8_t BL0940_PACKET_HEADER = 0x55; // 0x58 according to documentation
|
||||
|
||||
static const uint8_t BL0940_WRITE_COMMAND = 0xA0; // 0xA8 according to documentation
|
||||
static const uint8_t BL0940_REG_I_FAST_RMS_CTRL = 0x10;
|
||||
static const uint8_t BL0940_REG_MODE = 0x18;
|
||||
static const uint8_t BL0940_REG_SOFT_RESET = 0x19;
|
||||
static const uint8_t BL0940_REG_USR_WRPROT = 0x1A;
|
||||
static const uint8_t BL0940_REG_TPS_CTRL = 0x1B;
|
||||
|
||||
static const uint8_t BL0940_INIT[5][5] = {
|
||||
const uint8_t BL0940_INIT[5][6] = {
|
||||
// Reset to default
|
||||
{BL0940_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38},
|
||||
{BL0940_WRITE_COMMAND, BL0940_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38},
|
||||
// Enable User Operation Write
|
||||
{BL0940_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0},
|
||||
{BL0940_WRITE_COMMAND, BL0940_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0},
|
||||
// 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS
|
||||
{BL0940_REG_MODE, 0x00, 0x10, 0x00, 0x37},
|
||||
{BL0940_WRITE_COMMAND, BL0940_REG_MODE, 0x00, 0x10, 0x00, 0x37},
|
||||
// 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS
|
||||
{BL0940_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE},
|
||||
{BL0940_WRITE_COMMAND, BL0940_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE},
|
||||
// 0x181C = Half cycle, Fast RMS threshold 6172
|
||||
{BL0940_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}};
|
||||
{BL0940_WRITE_COMMAND, BL0940_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}};
|
||||
|
||||
void BL0940::loop() {
|
||||
DataPacket buffer;
|
||||
@@ -34,8 +36,8 @@ void BL0940::loop() {
|
||||
return;
|
||||
}
|
||||
if (read_array((uint8_t *) &buffer, sizeof(buffer))) {
|
||||
if (this->validate_checksum_(&buffer)) {
|
||||
this->received_package_(&buffer);
|
||||
if (validate_checksum(&buffer)) {
|
||||
received_package_(&buffer);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Junk on wire. Throwing away partial message");
|
||||
@@ -44,151 +46,35 @@ void BL0940::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
bool BL0940::validate_checksum_(DataPacket *data) {
|
||||
uint8_t checksum = this->read_command_;
|
||||
bool BL0940::validate_checksum(const DataPacket *data) {
|
||||
uint8_t checksum = BL0940_READ_COMMAND;
|
||||
// Whole package but checksum
|
||||
uint8_t *raw = (uint8_t *) data;
|
||||
for (uint32_t i = 0; i < sizeof(*data) - 1; i++) {
|
||||
checksum += raw[i];
|
||||
for (uint32_t i = 0; i < sizeof(data->raw) - 1; i++) {
|
||||
checksum += data->raw[i];
|
||||
}
|
||||
checksum ^= 0xFF;
|
||||
if (checksum != data->checksum) {
|
||||
ESP_LOGW(TAG, "Invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
|
||||
ESP_LOGW(TAG, "BL0940 invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
|
||||
}
|
||||
return checksum == data->checksum;
|
||||
}
|
||||
|
||||
void BL0940::update() {
|
||||
this->flush();
|
||||
this->write_byte(this->read_command_);
|
||||
this->write_byte(BL0940_READ_COMMAND);
|
||||
this->write_byte(BL0940_FULL_PACKET);
|
||||
}
|
||||
|
||||
void BL0940::setup() {
|
||||
#ifdef USE_NUMBER
|
||||
// add calibration callbacks
|
||||
if (this->voltage_calibration_number_ != nullptr) {
|
||||
this->voltage_calibration_number_->add_on_state_callback(
|
||||
[this](float state) { this->voltage_calibration_callback_(state); });
|
||||
if (this->voltage_calibration_number_->has_state()) {
|
||||
this->voltage_calibration_callback_(this->voltage_calibration_number_->state);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->current_calibration_number_ != nullptr) {
|
||||
this->current_calibration_number_->add_on_state_callback(
|
||||
[this](float state) { this->current_calibration_callback_(state); });
|
||||
if (this->current_calibration_number_->has_state()) {
|
||||
this->current_calibration_callback_(this->current_calibration_number_->state);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->power_calibration_number_ != nullptr) {
|
||||
this->power_calibration_number_->add_on_state_callback(
|
||||
[this](float state) { this->power_calibration_callback_(state); });
|
||||
if (this->power_calibration_number_->has_state()) {
|
||||
this->power_calibration_callback_(this->power_calibration_number_->state);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->energy_calibration_number_ != nullptr) {
|
||||
this->energy_calibration_number_->add_on_state_callback(
|
||||
[this](float state) { this->energy_calibration_callback_(state); });
|
||||
if (this->energy_calibration_number_->has_state()) {
|
||||
this->energy_calibration_callback_(this->energy_calibration_number_->state);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// calculate calibrated reference values
|
||||
this->voltage_reference_cal_ = this->voltage_reference_ / this->voltage_cal_;
|
||||
this->current_reference_cal_ = this->current_reference_ / this->current_cal_;
|
||||
this->power_reference_cal_ = this->power_reference_ / this->power_cal_;
|
||||
this->energy_reference_cal_ = this->energy_reference_ / this->energy_cal_;
|
||||
|
||||
for (auto *i : BL0940_INIT) {
|
||||
this->write_byte(this->write_command_), this->write_array(i, 5);
|
||||
this->write_array(i, 6);
|
||||
delay(1);
|
||||
}
|
||||
this->flush();
|
||||
}
|
||||
|
||||
float BL0940::calculate_power_reference_() {
|
||||
// calculate power reference based on voltage and current reference
|
||||
return this->voltage_reference_cal_ * this->current_reference_cal_ * 4046 / 324004 / 79931;
|
||||
}
|
||||
|
||||
float BL0940::calculate_energy_reference_() {
|
||||
// formula: 3600000 * 4046 * RL * R1 * 1000 / (1638.4 * 256) / Vref² / (R1 + R2)
|
||||
// or: power_reference_ * 3600000 / (1638.4 * 256)
|
||||
return this->power_reference_cal_ * 3600000 / (1638.4 * 256);
|
||||
}
|
||||
|
||||
float BL0940::calculate_calibration_value_(float state) { return (100 + state) / 100; }
|
||||
|
||||
void BL0940::reset_calibration() {
|
||||
#ifdef USE_NUMBER
|
||||
if (this->current_calibration_number_ != nullptr && this->current_cal_ != 1) {
|
||||
this->current_calibration_number_->make_call().set_value(0).perform();
|
||||
}
|
||||
if (this->voltage_calibration_number_ != nullptr && this->voltage_cal_ != 1) {
|
||||
this->voltage_calibration_number_->make_call().set_value(0).perform();
|
||||
}
|
||||
if (this->power_calibration_number_ != nullptr && this->power_cal_ != 1) {
|
||||
this->power_calibration_number_->make_call().set_value(0).perform();
|
||||
}
|
||||
if (this->energy_calibration_number_ != nullptr && this->energy_cal_ != 1) {
|
||||
this->energy_calibration_number_->make_call().set_value(0).perform();
|
||||
}
|
||||
#endif
|
||||
ESP_LOGD(TAG, "external calibration values restored to initial state");
|
||||
}
|
||||
|
||||
void BL0940::current_calibration_callback_(float state) {
|
||||
this->current_cal_ = this->calculate_calibration_value_(state);
|
||||
ESP_LOGV(TAG, "update current calibration state: %f", this->current_cal_);
|
||||
this->recalibrate_();
|
||||
}
|
||||
void BL0940::voltage_calibration_callback_(float state) {
|
||||
this->voltage_cal_ = this->calculate_calibration_value_(state);
|
||||
ESP_LOGV(TAG, "update voltage calibration state: %f", this->voltage_cal_);
|
||||
this->recalibrate_();
|
||||
}
|
||||
void BL0940::power_calibration_callback_(float state) {
|
||||
this->power_cal_ = this->calculate_calibration_value_(state);
|
||||
ESP_LOGV(TAG, "update power calibration state: %f", this->power_cal_);
|
||||
this->recalibrate_();
|
||||
}
|
||||
void BL0940::energy_calibration_callback_(float state) {
|
||||
this->energy_cal_ = this->calculate_calibration_value_(state);
|
||||
ESP_LOGV(TAG, "update energy calibration state: %f", this->energy_cal_);
|
||||
this->recalibrate_();
|
||||
}
|
||||
|
||||
void BL0940::recalibrate_() {
|
||||
ESP_LOGV(TAG, "Recalibrating reference values");
|
||||
this->voltage_reference_cal_ = this->voltage_reference_ / this->voltage_cal_;
|
||||
this->current_reference_cal_ = this->current_reference_ / this->current_cal_;
|
||||
|
||||
if (this->voltage_cal_ != 1 || this->current_cal_ != 1) {
|
||||
this->power_reference_ = this->calculate_power_reference_();
|
||||
}
|
||||
this->power_reference_cal_ = this->power_reference_ / this->power_cal_;
|
||||
|
||||
if (this->voltage_cal_ != 1 || this->current_cal_ != 1 || this->power_cal_ != 1) {
|
||||
this->energy_reference_ = this->calculate_energy_reference_();
|
||||
}
|
||||
this->energy_reference_cal_ = this->energy_reference_ / this->energy_cal_;
|
||||
|
||||
ESP_LOGD(TAG,
|
||||
"Recalibrated reference values:\n"
|
||||
"Voltage: %f\n, Current: %f\n, Power: %f\n, Energy: %f\n",
|
||||
this->voltage_reference_cal_, this->current_reference_cal_, this->power_reference_cal_,
|
||||
this->energy_reference_cal_);
|
||||
}
|
||||
|
||||
float BL0940::update_temp_(sensor::Sensor *sensor, uint16_le_t temperature) const {
|
||||
auto tb = (float) temperature;
|
||||
float BL0940::update_temp_(sensor::Sensor *sensor, ube16_t temperature) const {
|
||||
auto tb = (float) (temperature.h << 8 | temperature.l);
|
||||
float converted_temp = ((float) 170 / 448) * (tb / 2 - 32) - 45;
|
||||
if (sensor != nullptr) {
|
||||
if (sensor->has_state() && std::abs(converted_temp - sensor->get_state()) > max_temperature_diff_) {
|
||||
@@ -201,40 +87,33 @@ float BL0940::update_temp_(sensor::Sensor *sensor, uint16_le_t temperature) cons
|
||||
return converted_temp;
|
||||
}
|
||||
|
||||
void BL0940::received_package_(DataPacket *data) {
|
||||
void BL0940::received_package_(const DataPacket *data) const {
|
||||
// Bad header
|
||||
if (data->frame_header != BL0940_PACKET_HEADER) {
|
||||
ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header);
|
||||
return;
|
||||
}
|
||||
|
||||
// cf_cnt is only 24 bits, so track overflows
|
||||
uint32_t cf_cnt = (uint24_t) data->cf_cnt;
|
||||
cf_cnt |= this->prev_cf_cnt_ & 0xff000000;
|
||||
if (cf_cnt < this->prev_cf_cnt_) {
|
||||
cf_cnt += 0x1000000;
|
||||
}
|
||||
this->prev_cf_cnt_ = cf_cnt;
|
||||
float v_rms = (float) to_uint32_t(data->v_rms) / voltage_reference_;
|
||||
float i_rms = (float) to_uint32_t(data->i_rms) / current_reference_;
|
||||
float watt = (float) to_int32_t(data->watt) / power_reference_;
|
||||
uint32_t cf_cnt = to_uint32_t(data->cf_cnt);
|
||||
float total_energy_consumption = (float) cf_cnt / energy_reference_;
|
||||
|
||||
float v_rms = (uint24_t) data->v_rms / this->voltage_reference_cal_;
|
||||
float i_rms = (uint24_t) data->i_rms / this->current_reference_cal_;
|
||||
float watt = (int24_t) data->watt / this->power_reference_cal_;
|
||||
float total_energy_consumption = cf_cnt / this->energy_reference_cal_;
|
||||
float tps1 = update_temp_(internal_temperature_sensor_, data->tps1);
|
||||
float tps2 = update_temp_(external_temperature_sensor_, data->tps2);
|
||||
|
||||
float tps1 = update_temp_(this->internal_temperature_sensor_, data->tps1);
|
||||
float tps2 = update_temp_(this->external_temperature_sensor_, data->tps2);
|
||||
|
||||
if (this->voltage_sensor_ != nullptr) {
|
||||
this->voltage_sensor_->publish_state(v_rms);
|
||||
if (voltage_sensor_ != nullptr) {
|
||||
voltage_sensor_->publish_state(v_rms);
|
||||
}
|
||||
if (this->current_sensor_ != nullptr) {
|
||||
this->current_sensor_->publish_state(i_rms);
|
||||
if (current_sensor_ != nullptr) {
|
||||
current_sensor_->publish_state(i_rms);
|
||||
}
|
||||
if (this->power_sensor_ != nullptr) {
|
||||
this->power_sensor_->publish_state(watt);
|
||||
if (power_sensor_ != nullptr) {
|
||||
power_sensor_->publish_state(watt);
|
||||
}
|
||||
if (this->energy_sensor_ != nullptr) {
|
||||
this->energy_sensor_->publish_state(total_energy_consumption);
|
||||
if (energy_sensor_ != nullptr) {
|
||||
energy_sensor_->publish_state(total_energy_consumption);
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "BL0940: U %fV, I %fA, P %fW, Cnt %" PRId32 ", ∫P %fkWh, T1 %f°C, T2 %f°C", v_rms, i_rms, watt, cf_cnt,
|
||||
@@ -242,27 +121,7 @@ void BL0940::received_package_(DataPacket *data) {
|
||||
}
|
||||
|
||||
void BL0940::dump_config() { // NOLINT(readability-function-cognitive-complexity)
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"BL0940:\n"
|
||||
" LEGACY MODE: %s\n"
|
||||
" READ CMD: 0x%02X\n"
|
||||
" WRITE CMD: 0x%02X\n"
|
||||
" ------------------\n"
|
||||
" Current reference: %f\n"
|
||||
" Energy reference: %f\n"
|
||||
" Power reference: %f\n"
|
||||
" Voltage reference: %f\n",
|
||||
TRUEFALSE(this->legacy_mode_enabled_), this->read_command_, this->write_command_,
|
||||
this->current_reference_, this->energy_reference_, this->power_reference_, this->voltage_reference_);
|
||||
#ifdef USE_NUMBER
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"BL0940:\n"
|
||||
" Current calibration: %f\n"
|
||||
" Energy calibration: %f\n"
|
||||
" Power calibration: %f\n"
|
||||
" Voltage calibration: %f\n",
|
||||
this->current_cal_, this->energy_cal_, this->power_cal_, this->voltage_cal_);
|
||||
#endif
|
||||
ESP_LOGCONFIG(TAG, "BL0940:");
|
||||
LOG_SENSOR("", "Voltage", this->voltage_sensor_);
|
||||
LOG_SENSOR("", "Current", this->current_sensor_);
|
||||
LOG_SENSOR("", "Power", this->power_sensor_);
|
||||
@@ -271,5 +130,9 @@ void BL0940::dump_config() { // NOLINT(readability-function-cognitive-complexit
|
||||
LOG_SENSOR("", "External temperature", this->external_temperature_sensor_);
|
||||
}
|
||||
|
||||
uint32_t BL0940::to_uint32_t(ube24_t input) { return input.h << 16 | input.m << 8 | input.l; }
|
||||
|
||||
int32_t BL0940::to_int32_t(sbe24_t input) { return input.h << 16 | input.m << 8 | input.l; }
|
||||
|
||||
} // namespace bl0940
|
||||
} // namespace esphome
|
||||
|
@@ -1,48 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/datatypes.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_BUTTON
|
||||
#include "esphome/components/button/button.h"
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
#include "esphome/components/number/number.h"
|
||||
#endif
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace bl0940 {
|
||||
|
||||
static const float BL0940_PREF = 1430;
|
||||
static const float BL0940_UREF = 33000;
|
||||
static const float BL0940_IREF = 275000; // 2750 from tasmota. Seems to generate values 100 times too high
|
||||
|
||||
// Measured to 297J per click according to power consumption of 5 minutes
|
||||
// Converted to kWh (3.6MJ per kwH). Used to be 256 * 1638.4
|
||||
static const float BL0940_EREF = 3.6e6 / 297;
|
||||
|
||||
struct ube24_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align)
|
||||
uint8_t l;
|
||||
uint8_t m;
|
||||
uint8_t h;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ube16_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align)
|
||||
uint8_t l;
|
||||
uint8_t h;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sbe24_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align)
|
||||
uint8_t l;
|
||||
uint8_t m;
|
||||
int8_t h;
|
||||
} __attribute__((packed));
|
||||
|
||||
// Caveat: All these values are big endian (low - middle - high)
|
||||
struct DataPacket {
|
||||
uint8_t frame_header; // Packet header (0x58 in EN docs, 0x55 in CN docs and Tasmota tests)
|
||||
uint24_le_t i_fast_rms; // Fast RMS current
|
||||
uint24_le_t i_rms; // RMS current
|
||||
uint24_t RESERVED0; // Reserved
|
||||
uint24_le_t v_rms; // RMS voltage
|
||||
uint24_t RESERVED1; // Reserved
|
||||
int24_le_t watt; // Active power (can be negative for bidirectional measurement)
|
||||
uint24_t RESERVED2; // Reserved
|
||||
uint24_le_t cf_cnt; // Energy pulse count
|
||||
uint24_t RESERVED3; // Reserved
|
||||
uint16_le_t tps1; // Internal temperature sensor 1
|
||||
uint8_t RESERVED4; // Reserved (should be 0x00)
|
||||
uint16_le_t tps2; // Internal temperature sensor 2
|
||||
uint8_t RESERVED5; // Reserved (should be 0x00)
|
||||
uint8_t checksum; // Packet checksum
|
||||
|
||||
union DataPacket { // NOLINT(altera-struct-pack-align)
|
||||
uint8_t raw[35];
|
||||
struct {
|
||||
uint8_t frame_header; // value of 0x58 according to docs. 0x55 according to Tasmota real world tests. Reality wins.
|
||||
ube24_t i_fast_rms; // 0x00
|
||||
ube24_t i_rms; // 0x04
|
||||
ube24_t RESERVED0; // reserved
|
||||
ube24_t v_rms; // 0x06
|
||||
ube24_t RESERVED1; // reserved
|
||||
sbe24_t watt; // 0x08
|
||||
ube24_t RESERVED2; // reserved
|
||||
ube24_t cf_cnt; // 0x0A
|
||||
ube24_t RESERVED3; // reserved
|
||||
ube16_t tps1; // 0x0c
|
||||
uint8_t RESERVED4; // value of 0x00
|
||||
ube16_t tps2; // 0x0c
|
||||
uint8_t RESERVED5; // value of 0x00
|
||||
uint8_t checksum; // checksum
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
class BL0940 : public PollingComponent, public uart::UARTDevice {
|
||||
public:
|
||||
// Sensor setters
|
||||
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
||||
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
|
||||
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
|
||||
void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
|
||||
|
||||
// Temperature sensor setters
|
||||
void set_internal_temperature_sensor(sensor::Sensor *internal_temperature_sensor) {
|
||||
internal_temperature_sensor_ = internal_temperature_sensor;
|
||||
}
|
||||
@@ -50,105 +68,42 @@ class BL0940 : public PollingComponent, public uart::UARTDevice {
|
||||
external_temperature_sensor_ = external_temperature_sensor;
|
||||
}
|
||||
|
||||
// Configuration setters
|
||||
void set_legacy_mode(bool enable) { this->legacy_mode_enabled_ = enable; }
|
||||
void set_read_command(uint8_t read_command) { this->read_command_ = read_command; }
|
||||
void set_write_command(uint8_t write_command) { this->write_command_ = write_command; }
|
||||
|
||||
// Reference value setters (used for calibration and conversion)
|
||||
void set_current_reference(float current_ref) { this->current_reference_ = current_ref; }
|
||||
void set_energy_reference(float energy_ref) { this->energy_reference_ = energy_ref; }
|
||||
void set_power_reference(float power_ref) { this->power_reference_ = power_ref; }
|
||||
void set_voltage_reference(float voltage_ref) { this->voltage_reference_ = voltage_ref; }
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
// Calibration number setters (for Home Assistant number entities)
|
||||
void set_current_calibration_number(number::Number *num) { this->current_calibration_number_ = num; }
|
||||
void set_voltage_calibration_number(number::Number *num) { this->voltage_calibration_number_ = num; }
|
||||
void set_power_calibration_number(number::Number *num) { this->power_calibration_number_ = num; }
|
||||
void set_energy_calibration_number(number::Number *num) { this->energy_calibration_number_ = num; }
|
||||
#endif
|
||||
|
||||
#ifdef USE_BUTTON
|
||||
// Resets all calibration values to defaults (can be triggered by a button)
|
||||
void reset_calibration();
|
||||
#endif
|
||||
|
||||
// Core component methods
|
||||
void loop() override;
|
||||
|
||||
void update() override;
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
// --- Sensor pointers ---
|
||||
sensor::Sensor *voltage_sensor_{nullptr}; // Voltage sensor
|
||||
sensor::Sensor *current_sensor_{nullptr}; // Current sensor
|
||||
sensor::Sensor *power_sensor_{nullptr}; // Power sensor (can be negative for bidirectional)
|
||||
sensor::Sensor *energy_sensor_{nullptr}; // Energy sensor
|
||||
sensor::Sensor *internal_temperature_sensor_{nullptr}; // Internal temperature sensor
|
||||
sensor::Sensor *external_temperature_sensor_{nullptr}; // External temperature sensor
|
||||
sensor::Sensor *voltage_sensor_{nullptr};
|
||||
sensor::Sensor *current_sensor_{nullptr};
|
||||
// NB This may be negative as the circuits is seemingly able to measure
|
||||
// power in both directions
|
||||
sensor::Sensor *power_sensor_{nullptr};
|
||||
sensor::Sensor *energy_sensor_{nullptr};
|
||||
sensor::Sensor *internal_temperature_sensor_{nullptr};
|
||||
sensor::Sensor *external_temperature_sensor_{nullptr};
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
// --- Calibration number entities (for dynamic calibration via HA UI) ---
|
||||
number::Number *voltage_calibration_number_{nullptr};
|
||||
number::Number *current_calibration_number_{nullptr};
|
||||
number::Number *power_calibration_number_{nullptr};
|
||||
number::Number *energy_calibration_number_{nullptr};
|
||||
#endif
|
||||
// Max difference between two measurements of the temperature. Used to avoid noise.
|
||||
float max_temperature_diff_{0};
|
||||
// Divide by this to turn into Watt
|
||||
float power_reference_ = BL0940_PREF;
|
||||
// Divide by this to turn into Volt
|
||||
float voltage_reference_ = BL0940_UREF;
|
||||
// Divide by this to turn into Ampere
|
||||
float current_reference_ = BL0940_IREF;
|
||||
// Divide by this to turn into kWh
|
||||
float energy_reference_ = BL0940_EREF;
|
||||
|
||||
// --- Internal state ---
|
||||
uint32_t prev_cf_cnt_ = 0; // Previous energy pulse count (for energy calculation)
|
||||
float max_temperature_diff_{0}; // Max allowed temperature difference between two measurements (noise filter)
|
||||
float update_temp_(sensor::Sensor *sensor, ube16_t packed_temperature) const;
|
||||
|
||||
// --- Reference values for conversion ---
|
||||
float power_reference_; // Divider for raw power to get Watts
|
||||
float power_reference_cal_; // Calibrated power reference
|
||||
float voltage_reference_; // Divider for raw voltage to get Volts
|
||||
float voltage_reference_cal_; // Calibrated voltage reference
|
||||
float current_reference_; // Divider for raw current to get Amperes
|
||||
float current_reference_cal_; // Calibrated current reference
|
||||
float energy_reference_; // Divider for raw energy to get kWh
|
||||
float energy_reference_cal_; // Calibrated energy reference
|
||||
static uint32_t to_uint32_t(ube24_t input);
|
||||
|
||||
// --- Home Assistant calibration values (multipliers, default 1) ---
|
||||
float current_cal_{1};
|
||||
float voltage_cal_{1};
|
||||
float power_cal_{1};
|
||||
float energy_cal_{1};
|
||||
static int32_t to_int32_t(sbe24_t input);
|
||||
|
||||
// --- Protocol commands ---
|
||||
uint8_t read_command_;
|
||||
uint8_t write_command_;
|
||||
static bool validate_checksum(const DataPacket *data);
|
||||
|
||||
// --- Mode flags ---
|
||||
bool legacy_mode_enabled_ = true;
|
||||
|
||||
// --- Methods ---
|
||||
// Converts packed temperature value to float and updates the sensor
|
||||
float update_temp_(sensor::Sensor *sensor, uint16_le_t packed_temperature) const;
|
||||
|
||||
// Validates the checksum of a received data packet
|
||||
bool validate_checksum_(DataPacket *data);
|
||||
|
||||
// Handles a received data packet
|
||||
void received_package_(DataPacket *data);
|
||||
|
||||
// Calculates reference values for calibration and conversion
|
||||
float calculate_energy_reference_();
|
||||
float calculate_power_reference_();
|
||||
float calculate_calibration_value_(float state);
|
||||
|
||||
// Calibration update callbacks (used with number entities)
|
||||
void current_calibration_callback_(float state);
|
||||
void voltage_calibration_callback_(float state);
|
||||
void power_calibration_callback_(float state);
|
||||
void energy_calibration_callback_(float state);
|
||||
void reset_calibration_callback_();
|
||||
|
||||
// Recalculates all reference values after calibration changes
|
||||
void recalibrate_();
|
||||
void received_package_(const DataPacket *data) const;
|
||||
};
|
||||
|
||||
} // namespace bl0940
|
||||
} // namespace esphome
|
||||
|
@@ -1,27 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import button
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import ENTITY_CATEGORY_CONFIG, ICON_RESTART
|
||||
|
||||
from .. import CONF_BL0940_ID, bl0940_ns
|
||||
from ..sensor import BL0940
|
||||
|
||||
CalibrationResetButton = bl0940_ns.class_(
|
||||
"CalibrationResetButton", button.Button, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
button.button_schema(
|
||||
CalibrationResetButton,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
icon=ICON_RESTART,
|
||||
)
|
||||
.extend({cv.GenerateID(CONF_BL0940_ID): cv.use_id(BL0940)})
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = await button.new_button(config)
|
||||
await cg.register_component(var, config)
|
||||
await cg.register_parented(var, config[CONF_BL0940_ID])
|
@@ -1,20 +0,0 @@
|
||||
#include "calibration_reset_button.h"
|
||||
#include "../bl0940.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace bl0940 {
|
||||
|
||||
static const char *const TAG = "bl0940.button.calibration_reset";
|
||||
|
||||
void CalibrationResetButton::dump_config() { LOG_BUTTON("", "Calibration Reset Button", this); }
|
||||
|
||||
void CalibrationResetButton::press_action() {
|
||||
ESP_LOGI(TAG, "Resetting calibration defaults...");
|
||||
this->parent_->reset_calibration();
|
||||
}
|
||||
|
||||
} // namespace bl0940
|
||||
} // namespace esphome
|
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/button/button.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace bl0940 {
|
||||
|
||||
class BL0940; // Forward declaration of BL0940 class
|
||||
|
||||
class CalibrationResetButton : public button::Button, public Component, public Parented<BL0940> {
|
||||
public:
|
||||
void dump_config() override;
|
||||
|
||||
void press_action() override;
|
||||
};
|
||||
|
||||
} // namespace bl0940
|
||||
} // namespace esphome
|
@@ -1,94 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import number
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_MAX_VALUE,
|
||||
CONF_MIN_VALUE,
|
||||
CONF_MODE,
|
||||
CONF_RESTORE_VALUE,
|
||||
CONF_STEP,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
UNIT_PERCENT,
|
||||
)
|
||||
|
||||
from .. import CONF_BL0940_ID, bl0940_ns
|
||||
from ..sensor import BL0940
|
||||
|
||||
# Define calibration types
|
||||
CONF_CURRENT_CALIBRATION = "current_calibration"
|
||||
CONF_VOLTAGE_CALIBRATION = "voltage_calibration"
|
||||
CONF_POWER_CALIBRATION = "power_calibration"
|
||||
CONF_ENERGY_CALIBRATION = "energy_calibration"
|
||||
|
||||
BL0940Number = bl0940_ns.class_("BL0940Number")
|
||||
|
||||
CalibrationNumber = bl0940_ns.class_(
|
||||
"CalibrationNumber", number.Number, cg.PollingComponent
|
||||
)
|
||||
|
||||
|
||||
def validate_min_max(config):
|
||||
if config[CONF_MAX_VALUE] <= config[CONF_MIN_VALUE]:
|
||||
raise cv.Invalid("max_value must be greater than min_value")
|
||||
return config
|
||||
|
||||
|
||||
CALIBRATION_SCHEMA = cv.All(
|
||||
number.number_schema(
|
||||
CalibrationNumber,
|
||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(CONF_MODE, default="BOX"): cv.enum(number.NUMBER_MODES),
|
||||
cv.Optional(CONF_MAX_VALUE, default=10): cv.All(
|
||||
cv.float_, cv.Range(max=50)
|
||||
),
|
||||
cv.Optional(CONF_MIN_VALUE, default=-10): cv.All(
|
||||
cv.float_, cv.Range(min=-50)
|
||||
),
|
||||
cv.Optional(CONF_STEP, default=0.1): cv.positive_float,
|
||||
cv.Optional(CONF_RESTORE_VALUE): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA),
|
||||
validate_min_max,
|
||||
)
|
||||
|
||||
# Configuration schema for BL0940 numbers
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BL0940Number),
|
||||
cv.GenerateID(CONF_BL0940_ID): cv.use_id(BL0940),
|
||||
cv.Optional(CONF_CURRENT_CALIBRATION): CALIBRATION_SCHEMA,
|
||||
cv.Optional(CONF_VOLTAGE_CALIBRATION): CALIBRATION_SCHEMA,
|
||||
cv.Optional(CONF_POWER_CALIBRATION): CALIBRATION_SCHEMA,
|
||||
cv.Optional(CONF_ENERGY_CALIBRATION): CALIBRATION_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
# Get the BL0940 component instance
|
||||
bl0940 = await cg.get_variable(config[CONF_BL0940_ID])
|
||||
|
||||
# Process all calibration types
|
||||
for cal_type, setter_method in [
|
||||
(CONF_CURRENT_CALIBRATION, "set_current_calibration_number"),
|
||||
(CONF_VOLTAGE_CALIBRATION, "set_voltage_calibration_number"),
|
||||
(CONF_POWER_CALIBRATION, "set_power_calibration_number"),
|
||||
(CONF_ENERGY_CALIBRATION, "set_energy_calibration_number"),
|
||||
]:
|
||||
if conf := config.get(cal_type):
|
||||
var = await number.new_number(
|
||||
conf,
|
||||
min_value=conf.get(CONF_MIN_VALUE),
|
||||
max_value=conf.get(CONF_MAX_VALUE),
|
||||
step=conf.get(CONF_STEP),
|
||||
)
|
||||
await cg.register_component(var, conf)
|
||||
|
||||
if restore_value := config.get(CONF_RESTORE_VALUE):
|
||||
cg.add(var.set_restore_value(restore_value))
|
||||
cg.add(getattr(bl0940, setter_method)(var))
|
@@ -1,29 +0,0 @@
|
||||
#include "calibration_number.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace bl0940 {
|
||||
|
||||
static const char *const TAG = "bl0940.number";
|
||||
|
||||
void CalibrationNumber::setup() {
|
||||
float value = 0.0f;
|
||||
if (this->restore_value_) {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
|
||||
if (!this->pref_.load(&value)) {
|
||||
value = 0.0f;
|
||||
}
|
||||
}
|
||||
this->publish_state(value);
|
||||
}
|
||||
|
||||
void CalibrationNumber::control(float value) {
|
||||
this->publish_state(value);
|
||||
if (this->restore_value_)
|
||||
this->pref_.save(&value);
|
||||
}
|
||||
|
||||
void CalibrationNumber::dump_config() { LOG_NUMBER("", "Calibration Number", this); }
|
||||
|
||||
} // namespace bl0940
|
||||
} // namespace esphome
|
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/number/number.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace bl0940 {
|
||||
|
||||
class CalibrationNumber : public number::Number, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; }
|
||||
|
||||
protected:
|
||||
void control(float value) override;
|
||||
bool restore_value_{true};
|
||||
|
||||
ESPPreferenceObject pref_;
|
||||
};
|
||||
|
||||
} // namespace bl0940
|
||||
} // namespace esphome
|
@@ -8,7 +8,6 @@ from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_INTERNAL_TEMPERATURE,
|
||||
CONF_POWER,
|
||||
CONF_REFERENCE_VOLTAGE,
|
||||
CONF_VOLTAGE,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
@@ -24,133 +23,12 @@ from esphome.const import (
|
||||
UNIT_WATT,
|
||||
)
|
||||
|
||||
from . import bl0940_ns
|
||||
|
||||
DEPENDENCIES = ["uart"]
|
||||
|
||||
|
||||
bl0940_ns = cg.esphome_ns.namespace("bl0940")
|
||||
BL0940 = bl0940_ns.class_("BL0940", cg.PollingComponent, uart.UARTDevice)
|
||||
|
||||
CONF_LEGACY_MODE = "legacy_mode"
|
||||
|
||||
CONF_READ_COMMAND = "read_command"
|
||||
CONF_WRITE_COMMAND = "write_command"
|
||||
|
||||
CONF_RESISTOR_SHUNT = "resistor_shunt"
|
||||
CONF_RESISTOR_ONE = "resistor_one"
|
||||
CONF_RESISTOR_TWO = "resistor_two"
|
||||
|
||||
CONF_CURRENT_REFERENCE = "current_reference"
|
||||
CONF_ENERGY_REFERENCE = "energy_reference"
|
||||
CONF_POWER_REFERENCE = "power_reference"
|
||||
CONF_VOLTAGE_REFERENCE = "voltage_reference"
|
||||
|
||||
DEFAULT_BL0940_READ_COMMAND = 0x58
|
||||
DEFAULT_BL0940_WRITE_COMMAND = 0xA1
|
||||
|
||||
# Values according to BL0940 application note:
|
||||
# https://www.belling.com.cn/media/file_object/bel_product/BL0940/guide/BL0940_APPNote_TSSOP14_V1.04_EN.pdf
|
||||
DEFAULT_BL0940_VREF = 1.218 # Vref = 1.218
|
||||
DEFAULT_BL0940_RL = 1 # RL = 1 mΩ
|
||||
DEFAULT_BL0940_R1 = 0.51 # R1 = 0.51 kΩ
|
||||
DEFAULT_BL0940_R2 = 1950 # R2 = 5 x 390 kΩ -> 1950 kΩ
|
||||
|
||||
# ----------------------------------------------------
|
||||
# values from initial implementation
|
||||
DEFAULT_BL0940_LEGACY_READ_COMMAND = 0x50
|
||||
DEFAULT_BL0940_LEGACY_WRITE_COMMAND = 0xA0
|
||||
|
||||
DEFAULT_BL0940_LEGACY_UREF = 33000
|
||||
DEFAULT_BL0940_LEGACY_IREF = 275000
|
||||
DEFAULT_BL0940_LEGACY_PREF = 1430
|
||||
# Measured to 297J per click according to power consumption of 5 minutes
|
||||
# Converted to kWh (3.6MJ per kwH). Used to be 256 * 1638.4
|
||||
DEFAULT_BL0940_LEGACY_EREF = 3.6e6 / 297
|
||||
# ----------------------------------------------------
|
||||
|
||||
|
||||
# methods to calculate voltage and current reference values
|
||||
def calculate_voltage_reference(vref, r_one, r_two):
|
||||
# formula: 79931 / Vref * (R1 * 1000) / (R1 + R2)
|
||||
return 79931 / vref * (r_one * 1000) / (r_one + r_two)
|
||||
|
||||
|
||||
def calculate_current_reference(vref, r_shunt):
|
||||
# formula: 324004 * RL / Vref
|
||||
return 324004 * r_shunt / vref
|
||||
|
||||
|
||||
def calculate_power_reference(voltage_reference, current_reference):
|
||||
# calculate power reference based on voltage and current reference
|
||||
return voltage_reference * current_reference * 4046 / 324004 / 79931
|
||||
|
||||
|
||||
def calculate_energy_reference(power_reference):
|
||||
# formula: power_reference * 3600000 / (1638.4 * 256)
|
||||
return power_reference * 3600000 / (1638.4 * 256)
|
||||
|
||||
|
||||
def validate_legacy_mode(config):
|
||||
# Only allow schematic calibration options if legacy_mode is False
|
||||
if config.get(CONF_LEGACY_MODE, True):
|
||||
forbidden = [
|
||||
CONF_REFERENCE_VOLTAGE,
|
||||
CONF_RESISTOR_SHUNT,
|
||||
CONF_RESISTOR_ONE,
|
||||
CONF_RESISTOR_TWO,
|
||||
]
|
||||
for key in forbidden:
|
||||
if key in config:
|
||||
raise cv.Invalid(
|
||||
f"Option '{key}' is only allowed when legacy_mode: false"
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
def set_command_defaults(config):
|
||||
# Set defaults for read_command and write_command based on legacy_mode
|
||||
legacy = config.get(CONF_LEGACY_MODE, True)
|
||||
if legacy:
|
||||
config.setdefault(CONF_READ_COMMAND, DEFAULT_BL0940_LEGACY_READ_COMMAND)
|
||||
config.setdefault(CONF_WRITE_COMMAND, DEFAULT_BL0940_LEGACY_WRITE_COMMAND)
|
||||
else:
|
||||
config.setdefault(CONF_READ_COMMAND, DEFAULT_BL0940_READ_COMMAND)
|
||||
config.setdefault(CONF_WRITE_COMMAND, DEFAULT_BL0940_WRITE_COMMAND)
|
||||
return config
|
||||
|
||||
|
||||
def set_reference_values(config):
|
||||
# Set default reference values based on legacy_mode
|
||||
if config.get(CONF_LEGACY_MODE, True):
|
||||
config.setdefault(CONF_VOLTAGE_REFERENCE, DEFAULT_BL0940_LEGACY_UREF)
|
||||
config.setdefault(CONF_CURRENT_REFERENCE, DEFAULT_BL0940_LEGACY_IREF)
|
||||
config.setdefault(CONF_POWER_REFERENCE, DEFAULT_BL0940_LEGACY_PREF)
|
||||
config.setdefault(CONF_ENERGY_REFERENCE, DEFAULT_BL0940_LEGACY_PREF)
|
||||
else:
|
||||
vref = config.get(CONF_VOLTAGE_REFERENCE, DEFAULT_BL0940_VREF)
|
||||
r_one = config.get(CONF_RESISTOR_ONE, DEFAULT_BL0940_R1)
|
||||
r_two = config.get(CONF_RESISTOR_TWO, DEFAULT_BL0940_R2)
|
||||
r_shunt = config.get(CONF_RESISTOR_SHUNT, DEFAULT_BL0940_RL)
|
||||
|
||||
config.setdefault(
|
||||
CONF_VOLTAGE_REFERENCE, calculate_voltage_reference(vref, r_one, r_two)
|
||||
)
|
||||
config.setdefault(
|
||||
CONF_CURRENT_REFERENCE, calculate_current_reference(vref, r_shunt)
|
||||
)
|
||||
config.setdefault(
|
||||
CONF_POWER_REFERENCE,
|
||||
calculate_power_reference(
|
||||
config.get(CONF_VOLTAGE_REFERENCE), config.get(CONF_CURRENT_REFERENCE)
|
||||
),
|
||||
)
|
||||
config.setdefault(
|
||||
CONF_ENERGY_REFERENCE,
|
||||
calculate_energy_reference(config.get(CONF_POWER_REFERENCE)),
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
@@ -191,24 +69,10 @@ CONFIG_SCHEMA = (
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_LEGACY_MODE, default=True): cv.boolean,
|
||||
cv.Optional(CONF_READ_COMMAND): cv.hex_uint8_t,
|
||||
cv.Optional(CONF_WRITE_COMMAND): cv.hex_uint8_t,
|
||||
cv.Optional(CONF_REFERENCE_VOLTAGE): cv.float_,
|
||||
cv.Optional(CONF_RESISTOR_SHUNT): cv.float_,
|
||||
cv.Optional(CONF_RESISTOR_ONE): cv.float_,
|
||||
cv.Optional(CONF_RESISTOR_TWO): cv.float_,
|
||||
cv.Optional(CONF_CURRENT_REFERENCE): cv.float_,
|
||||
cv.Optional(CONF_ENERGY_REFERENCE): cv.float_,
|
||||
cv.Optional(CONF_POWER_REFERENCE): cv.float_,
|
||||
cv.Optional(CONF_VOLTAGE_REFERENCE): cv.float_,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
.add_extra(validate_legacy_mode)
|
||||
.add_extra(set_command_defaults)
|
||||
.add_extra(set_reference_values)
|
||||
)
|
||||
|
||||
|
||||
@@ -235,16 +99,3 @@ async def to_code(config):
|
||||
if external_temperature_config := config.get(CONF_EXTERNAL_TEMPERATURE):
|
||||
sens = await sensor.new_sensor(external_temperature_config)
|
||||
cg.add(var.set_external_temperature_sensor(sens))
|
||||
|
||||
# enable legacy mode
|
||||
cg.add(var.set_legacy_mode(config.get(CONF_LEGACY_MODE)))
|
||||
|
||||
# Set bl0940 commands after validator has determined which defaults to use if not set
|
||||
cg.add(var.set_read_command(config.get(CONF_READ_COMMAND)))
|
||||
cg.add(var.set_write_command(config.get(CONF_WRITE_COMMAND)))
|
||||
|
||||
# Set reference values after validator has set the values either from defaults or calculated
|
||||
cg.add(var.set_current_reference(config.get(CONF_CURRENT_REFERENCE)))
|
||||
cg.add(var.set_voltage_reference(config.get(CONF_VOLTAGE_REFERENCE)))
|
||||
cg.add(var.set_power_reference(config.get(CONF_POWER_REFERENCE)))
|
||||
cg.add(var.set_energy_reference(config.get(CONF_ENERGY_REFERENCE)))
|
||||
|
@@ -130,9 +130,7 @@ class BluetoothProxy final : public esp32_ble_tracker::ESPBTDeviceListener, publ
|
||||
|
||||
std::string get_bluetooth_mac_address_pretty() {
|
||||
const uint8_t *mac = esp_bt_dev_get_address();
|
||||
char buf[18];
|
||||
format_mac_addr_upper(mac, buf);
|
||||
return std::string(buf);
|
||||
return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@@ -10,8 +10,7 @@ from esphome.const import (
|
||||
PLATFORM_LN882X,
|
||||
PLATFORM_RTL87XX,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.coroutine import CoroPriority
|
||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||
|
||||
AUTO_LOAD = ["web_server_base", "ota.web_server"]
|
||||
DEPENDENCIES = ["wifi"]
|
||||
@@ -41,7 +40,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.CAPTIVE_PORTAL)
|
||||
@coroutine_with_priority(CoroPriority.COMMUNICATION)
|
||||
async def to_code(config):
|
||||
paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
|
||||
|
||||
|
@@ -7,83 +7,103 @@ namespace esphome {
|
||||
namespace captive_portal {
|
||||
|
||||
const uint8_t INDEX_GZ[] PROGMEM = {
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x95, 0x16, 0x6b, 0x8f, 0xdb, 0x36, 0xf2, 0x7b, 0x7e,
|
||||
0x05, 0x8f, 0x49, 0xbb, 0x52, 0xb3, 0x7a, 0x7a, 0xed, 0x6c, 0x24, 0x51, 0x45, 0x9a, 0xbb, 0xa2, 0x05, 0x9a, 0x36,
|
||||
0xc0, 0x6e, 0x73, 0x1f, 0x82, 0x00, 0x4b, 0x53, 0x23, 0x8b, 0x31, 0x45, 0xea, 0x48, 0xca, 0x8f, 0x18, 0xbe, 0xdf,
|
||||
0x7e, 0xa0, 0x24, 0x7b, 0x9d, 0x45, 0x73, 0xb8, 0xb3, 0x60, 0x61, 0x38, 0xef, 0x19, 0xcd, 0x83, 0xc5, 0xdf, 0x2a,
|
||||
0xc5, 0xec, 0xbe, 0x03, 0xd4, 0xd8, 0x56, 0x94, 0x85, 0x7b, 0x23, 0x41, 0xe5, 0x8a, 0x80, 0x2c, 0x8b, 0x06, 0x68,
|
||||
0x55, 0x16, 0x2d, 0x58, 0x8a, 0x58, 0x43, 0xb5, 0x01, 0x4b, 0xfe, 0xbc, 0xff, 0x39, 0xb8, 0x2d, 0x0b, 0xc1, 0xe5,
|
||||
0x1a, 0x69, 0x10, 0x84, 0x33, 0x25, 0x51, 0xa3, 0xa1, 0x26, 0x15, 0xb5, 0x34, 0xe3, 0x2d, 0x5d, 0xc1, 0x24, 0x22,
|
||||
0x69, 0x0b, 0x64, 0xc3, 0x61, 0xdb, 0x29, 0x6d, 0x11, 0x53, 0xd2, 0x82, 0xb4, 0x04, 0x6f, 0x79, 0x65, 0x1b, 0x52,
|
||||
0xc1, 0x86, 0x33, 0x08, 0x86, 0xc3, 0x35, 0x97, 0xdc, 0x72, 0x2a, 0x02, 0xc3, 0xa8, 0x00, 0x92, 0x5c, 0xf7, 0x06,
|
||||
0xf4, 0x70, 0xa0, 0x4b, 0x01, 0x44, 0x2a, 0x5c, 0x16, 0x86, 0x69, 0xde, 0x59, 0xe4, 0x5c, 0x25, 0xad, 0xaa, 0x7a,
|
||||
0x01, 0x65, 0x14, 0x51, 0x63, 0xc0, 0x9a, 0x88, 0xcb, 0x0a, 0x76, 0xe1, 0x32, 0x66, 0x2c, 0x86, 0xdb, 0xdb, 0xf0,
|
||||
0xb3, 0x79, 0x56, 0x29, 0xd6, 0xb7, 0x20, 0x6d, 0x28, 0x14, 0xa3, 0x96, 0x2b, 0x19, 0x1a, 0xa0, 0x9a, 0x35, 0x84,
|
||||
0x10, 0xfc, 0xa3, 0xa1, 0x1b, 0xc0, 0xdf, 0x7f, 0xef, 0x9d, 0x99, 0x56, 0x60, 0xff, 0x21, 0xc0, 0x81, 0xe6, 0xa7,
|
||||
0xfd, 0x3d, 0x5d, 0xfd, 0x4e, 0x5b, 0xf0, 0x30, 0x35, 0xbc, 0x02, 0xec, 0x7f, 0x8c, 0x3f, 0x85, 0xc6, 0xee, 0x05,
|
||||
0x84, 0x15, 0x37, 0x9d, 0xa0, 0x7b, 0x82, 0x97, 0x42, 0xb1, 0x35, 0xf6, 0xf3, 0xba, 0x97, 0xcc, 0x29, 0x47, 0xc6,
|
||||
0x03, 0xff, 0x20, 0xc0, 0x22, 0x4b, 0xde, 0x51, 0xdb, 0x84, 0x2d, 0xdd, 0x79, 0x23, 0xc0, 0xa5, 0x97, 0xfe, 0xe0,
|
||||
0xc1, 0xcb, 0x24, 0x8e, 0xfd, 0xeb, 0xe1, 0x15, 0xfb, 0x51, 0x12, 0xc7, 0xb9, 0x06, 0xdb, 0x6b, 0x89, 0xa8, 0xf7,
|
||||
0x50, 0x74, 0xd4, 0x36, 0xa8, 0x22, 0xf8, 0x5d, 0x92, 0xa2, 0xe4, 0x75, 0x98, 0xce, 0x7f, 0x0b, 0x5f, 0xa1, 0x9b,
|
||||
0x30, 0x9d, 0xb3, 0x57, 0xc1, 0x1c, 0x25, 0x37, 0xc1, 0x1c, 0xa5, 0x69, 0x38, 0x47, 0xf1, 0x17, 0x8c, 0x6a, 0x2e,
|
||||
0x04, 0xc1, 0x52, 0x49, 0xc0, 0xc8, 0x58, 0xad, 0xd6, 0x40, 0x30, 0xeb, 0xb5, 0x06, 0x69, 0xdf, 0x2a, 0xa1, 0x34,
|
||||
0x8e, 0xca, 0x67, 0xff, 0x97, 0x42, 0xab, 0xa9, 0x34, 0xb5, 0xd2, 0x2d, 0xc1, 0x43, 0xf6, 0xbd, 0x17, 0x07, 0x7b,
|
||||
0x44, 0xee, 0xe5, 0x5f, 0x10, 0x03, 0xa5, 0xf9, 0x8a, 0x4b, 0x82, 0x9d, 0xc6, 0x5b, 0x1c, 0x95, 0x0f, 0xfe, 0xf1,
|
||||
0x1c, 0x3d, 0x75, 0xd1, 0x4f, 0xf1, 0x28, 0xef, 0xe3, 0x43, 0x61, 0x36, 0x2b, 0xb4, 0x6b, 0x85, 0x34, 0x04, 0x37,
|
||||
0xd6, 0x76, 0x59, 0x14, 0x6d, 0xb7, 0xdb, 0x70, 0x3b, 0x0b, 0x95, 0x5e, 0x45, 0x69, 0x1c, 0xc7, 0x91, 0xd9, 0xac,
|
||||
0x30, 0x1a, 0x0b, 0x01, 0xa7, 0x37, 0x18, 0x35, 0xc0, 0x57, 0x8d, 0x1d, 0xe0, 0xf2, 0xc5, 0x01, 0x8e, 0x85, 0xe3,
|
||||
0x28, 0x1f, 0x3e, 0x5d, 0x58, 0xe1, 0x17, 0x56, 0xe0, 0x47, 0xea, 0xe1, 0x53, 0x98, 0x57, 0x43, 0x98, 0xaf, 0x68,
|
||||
0x8a, 0x52, 0x14, 0x0f, 0x4f, 0x1a, 0x38, 0x78, 0x3a, 0x05, 0x4f, 0x4e, 0xe8, 0xe2, 0xe4, 0xa0, 0x76, 0x11, 0xbc,
|
||||
0x3e, 0xcb, 0x26, 0x0e, 0xb3, 0x49, 0xe2, 0x47, 0x84, 0x13, 0xf8, 0x65, 0x71, 0x79, 0x0e, 0xd2, 0x0f, 0x97, 0x0c,
|
||||
0xce, 0x5a, 0x93, 0x7c, 0x58, 0xd0, 0x39, 0x9a, 0x4f, 0x98, 0x79, 0xe0, 0xe0, 0xf3, 0x09, 0xcd, 0x37, 0x69, 0x93,
|
||||
0xb4, 0xc1, 0x22, 0x98, 0xd3, 0x19, 0x9a, 0x4d, 0x8e, 0xcc, 0xd0, 0x6c, 0x93, 0x36, 0x8b, 0x0f, 0x8b, 0x4b, 0x5c,
|
||||
0x30, 0xfb, 0x72, 0x15, 0x95, 0xd8, 0xcf, 0x30, 0x7e, 0x8c, 0x5c, 0x5d, 0x46, 0x1e, 0x7e, 0x56, 0x5c, 0x7a, 0x18,
|
||||
0xfb, 0xc7, 0x1a, 0x2c, 0x6b, 0x3c, 0x1c, 0x31, 0x25, 0x6b, 0xbe, 0x0a, 0x3f, 0x1b, 0x25, 0xb1, 0x1f, 0xda, 0x06,
|
||||
0xa4, 0x77, 0x12, 0x75, 0x82, 0x30, 0x50, 0xbc, 0xa7, 0x14, 0xeb, 0x1f, 0xce, 0xf5, 0x6f, 0xb9, 0x15, 0x40, 0x6c,
|
||||
0xe8, 0x1a, 0xf6, 0xfa, 0x8c, 0x5d, 0xaa, 0x6a, 0xff, 0x8d, 0xd6, 0x68, 0x92, 0xb1, 0x2f, 0xb8, 0x94, 0xa0, 0xef,
|
||||
0x61, 0x67, 0x09, 0x7e, 0xf7, 0xe6, 0x2d, 0x7a, 0x53, 0x55, 0x1a, 0x8c, 0xc9, 0x10, 0x7e, 0x69, 0xc3, 0x96, 0xb2,
|
||||
0xff, 0x5d, 0x57, 0xf2, 0x95, 0xae, 0x7f, 0xf2, 0x9f, 0x39, 0xfa, 0x1d, 0xec, 0x56, 0xe9, 0xf5, 0xa4, 0xcd, 0xb9,
|
||||
0x96, 0xbb, 0x0e, 0xd3, 0xc4, 0x86, 0xb4, 0x33, 0xa1, 0x11, 0x9c, 0x81, 0x97, 0xf8, 0x61, 0x4b, 0xbb, 0xc7, 0xa8,
|
||||
0xe4, 0x29, 0x51, 0x0f, 0x45, 0xc5, 0x37, 0x88, 0x09, 0x6a, 0x0c, 0xc1, 0x72, 0x54, 0x85, 0xd1, 0x33, 0x34, 0xfc,
|
||||
0x94, 0x64, 0x82, 0xb3, 0x35, 0xc1, 0x7f, 0x31, 0x01, 0x7e, 0xda, 0xff, 0x5a, 0x79, 0x57, 0xc6, 0xf0, 0xea, 0xca,
|
||||
0x0f, 0x37, 0x54, 0xf4, 0x80, 0x08, 0xb2, 0x0d, 0x37, 0x8f, 0x0e, 0xe6, 0xdf, 0x14, 0xeb, 0xcc, 0xfa, 0xca, 0x0f,
|
||||
0x6b, 0xc5, 0x7a, 0xe3, 0xf9, 0xb8, 0x9c, 0xcc, 0x15, 0x74, 0x1c, 0x90, 0xf8, 0x39, 0x7e, 0xe2, 0x51, 0x20, 0xa0,
|
||||
0xb6, 0x67, 0x3e, 0x84, 0x5e, 0x1c, 0x8c, 0x27, 0x43, 0x6d, 0x0c, 0xf7, 0x8f, 0x67, 0x64, 0x61, 0x3a, 0x2a, 0x9f,
|
||||
0x0a, 0x3a, 0x07, 0x5d, 0xab, 0xc8, 0xd0, 0x41, 0xae, 0x5f, 0x3a, 0x2a, 0xcf, 0x06, 0x23, 0x7a, 0x02, 0x5f, 0x1c,
|
||||
0xb8, 0x27, 0xdd, 0x14, 0x5c, 0x9f, 0x35, 0x16, 0x51, 0xc5, 0x37, 0xe5, 0xc3, 0xd1, 0x7f, 0x8c, 0xe3, 0x5f, 0x3d,
|
||||
0xe8, 0xfd, 0x1d, 0x08, 0x60, 0x56, 0x69, 0x0f, 0x3f, 0x97, 0x60, 0xb1, 0x3f, 0x06, 0xfc, 0xcb, 0xfd, 0xbb, 0xdf,
|
||||
0x88, 0xf2, 0xb4, 0x7f, 0xfd, 0x2d, 0x6e, 0xb7, 0x0a, 0x3e, 0x6a, 0x10, 0xff, 0x26, 0x57, 0x6e, 0x19, 0x5c, 0x7d,
|
||||
0xc2, 0x7e, 0x38, 0xc4, 0xfb, 0xf0, 0xb8, 0x11, 0x5c, 0x3b, 0xbf, 0xdc, 0xb5, 0xe2, 0xda, 0x45, 0x18, 0x2c, 0xe6,
|
||||
0xfe, 0xf1, 0xe1, 0xe8, 0x1f, 0xfd, 0xbc, 0x88, 0xc6, 0xb9, 0x5e, 0x16, 0xc3, 0x88, 0x2d, 0x7f, 0x38, 0x2c, 0xd5,
|
||||
0x2e, 0x30, 0xfc, 0x0b, 0x97, 0xab, 0x8c, 0xcb, 0x06, 0x34, 0xb7, 0xc7, 0x8a, 0x6f, 0xae, 0xb9, 0xec, 0x7a, 0x7b,
|
||||
0xe8, 0x68, 0x55, 0x39, 0xca, 0xbc, 0xdb, 0xe5, 0xb5, 0x92, 0xd6, 0x71, 0x42, 0x96, 0x40, 0x7b, 0x1c, 0xe9, 0xc3,
|
||||
0x44, 0xc9, 0x5e, 0xcf, 0xbf, 0x3b, 0xba, 0x82, 0x3b, 0x58, 0xd8, 0xd9, 0x80, 0x0a, 0xbe, 0x92, 0x19, 0x03, 0x69,
|
||||
0x41, 0x8f, 0x42, 0x35, 0x6d, 0xb9, 0xd8, 0x67, 0x86, 0x4a, 0x13, 0x18, 0xd0, 0xbc, 0x3e, 0x2e, 0x7b, 0x6b, 0x95,
|
||||
0x3c, 0x2c, 0x95, 0xae, 0x40, 0x67, 0x71, 0x3e, 0x02, 0x81, 0xa6, 0x15, 0xef, 0x4d, 0x16, 0xce, 0x34, 0xb4, 0xf9,
|
||||
0x92, 0xb2, 0xf5, 0x4a, 0xab, 0x5e, 0x56, 0x01, 0x73, 0x93, 0x36, 0x7b, 0x9e, 0xd4, 0x74, 0x06, 0x2c, 0x9f, 0x4e,
|
||||
0x75, 0x5d, 0xe7, 0x82, 0x4b, 0x08, 0xc6, 0x59, 0x96, 0xa5, 0xe1, 0x8d, 0x13, 0xbb, 0x70, 0x33, 0x4c, 0x1d, 0x62,
|
||||
0xf4, 0x31, 0x89, 0xe3, 0xef, 0xf2, 0x53, 0x38, 0x71, 0xce, 0x7a, 0x6d, 0x94, 0xce, 0x3a, 0xc5, 0x9d, 0x9b, 0xc7,
|
||||
0x96, 0x72, 0x79, 0xe9, 0xbd, 0x2b, 0x93, 0x7c, 0x5a, 0x3f, 0x19, 0x97, 0x83, 0x99, 0x61, 0x09, 0xe5, 0x2d, 0x97,
|
||||
0xe3, 0x0e, 0xcd, 0xd2, 0x45, 0xdc, 0xed, 0x8e, 0xe1, 0x54, 0x20, 0x87, 0x13, 0x77, 0x2d, 0x60, 0x97, 0x7f, 0xee,
|
||||
0x8d, 0xe5, 0xf5, 0x3e, 0x98, 0x76, 0x70, 0x66, 0x3a, 0xca, 0x20, 0x58, 0x82, 0xdd, 0x02, 0xc8, 0x7c, 0xb0, 0x11,
|
||||
0x70, 0x0b, 0xad, 0x99, 0xf2, 0x74, 0x56, 0x33, 0x14, 0xe8, 0xd7, 0xba, 0xfe, 0x1b, 0xb7, 0xab, 0xc5, 0x43, 0x4b,
|
||||
0xf5, 0x8a, 0xcb, 0x60, 0xa9, 0xac, 0x55, 0x6d, 0x16, 0xbc, 0xea, 0x76, 0xf9, 0x84, 0x72, 0xca, 0xb2, 0xc4, 0xb9,
|
||||
0x39, 0xec, 0xd6, 0x53, 0xbe, 0x93, 0x6e, 0x87, 0x8c, 0x12, 0xbc, 0x9a, 0xf8, 0x06, 0x16, 0x14, 0x9f, 0xd3, 0x93,
|
||||
0xcc, 0xbb, 0x1d, 0x72, 0xb8, 0x53, 0xaa, 0x6f, 0xea, 0x5b, 0x9a, 0xc4, 0x7f, 0xf1, 0x45, 0xaa, 0xba, 0x4e, 0x97,
|
||||
0xf5, 0x39, 0x53, 0x6e, 0x4d, 0xba, 0xd6, 0x18, 0x4a, 0xab, 0x88, 0xc6, 0xdb, 0x8c, 0xab, 0x8c, 0xb2, 0x70, 0x19,
|
||||
0x2e, 0x8b, 0x26, 0x41, 0xbc, 0x22, 0x2d, 0x65, 0xe5, 0xc5, 0xf8, 0x2a, 0xa2, 0x26, 0x39, 0x91, 0x9a, 0xa4, 0xfc,
|
||||
0x6a, 0x18, 0x8d, 0xb4, 0xc1, 0xfb, 0xf2, 0xad, 0x92, 0x12, 0x98, 0xe5, 0x72, 0x85, 0xac, 0x42, 0x53, 0x0a, 0xc2,
|
||||
0x30, 0x2c, 0x96, 0xba, 0x7c, 0x2f, 0x80, 0x1a, 0x40, 0x5b, 0xca, 0x6d, 0x58, 0x44, 0x23, 0xff, 0xd8, 0xc7, 0xbc,
|
||||
0x22, 0x12, 0x6c, 0x39, 0x35, 0x6c, 0xd1, 0xcc, 0x46, 0x03, 0x77, 0x60, 0x9d, 0x26, 0x67, 0x60, 0x56, 0x16, 0x6e,
|
||||
0xe5, 0x22, 0x3a, 0x8c, 0x34, 0x12, 0x6d, 0x79, 0xcd, 0xdd, 0x95, 0xa5, 0x2c, 0x86, 0x22, 0x77, 0x1a, 0x5c, 0x9e,
|
||||
0xc7, 0xeb, 0xd5, 0x00, 0x09, 0x90, 0x2b, 0xdb, 0x90, 0x59, 0x8a, 0x3a, 0x41, 0x19, 0x34, 0x4a, 0x54, 0xa0, 0xc9,
|
||||
0xdd, 0xdd, 0xaf, 0x7f, 0x2f, 0x9d, 0x33, 0x8f, 0x72, 0x9d, 0x59, 0x8f, 0x62, 0x0e, 0x98, 0xa4, 0x16, 0x37, 0xe3,
|
||||
0xa5, 0xaa, 0xa3, 0xc6, 0x6c, 0x95, 0xae, 0xbe, 0xd2, 0xf1, 0x7e, 0x42, 0x8e, 0x7a, 0x86, 0xff, 0xd0, 0x2a, 0xe5,
|
||||
0x1d, 0xdd, 0x40, 0x11, 0x4d, 0x87, 0x22, 0x72, 0x0e, 0x8f, 0xf4, 0x66, 0xe2, 0x6b, 0x92, 0xf2, 0x8f, 0xfb, 0x37,
|
||||
0xe8, 0xcf, 0xae, 0xa2, 0x16, 0xc6, 0xb4, 0x0d, 0x51, 0xb5, 0x60, 0x1b, 0x55, 0x91, 0xf7, 0x7f, 0xdc, 0xdd, 0x9f,
|
||||
0x23, 0xec, 0x07, 0x26, 0x04, 0x92, 0x8d, 0xd7, 0xbb, 0x5e, 0x58, 0xde, 0x51, 0x6d, 0x07, 0xb5, 0x81, 0x9b, 0x22,
|
||||
0xa7, 0x18, 0x06, 0x7a, 0xcd, 0x05, 0x8c, 0x61, 0x8c, 0x82, 0x25, 0x3a, 0x79, 0x75, 0xb2, 0xf6, 0xc4, 0xaf, 0x68,
|
||||
0xfc, 0xda, 0xd1, 0xf8, 0xe9, 0xa3, 0xe1, 0xa6, 0xfb, 0x1f, 0x53, 0x58, 0x46, 0xb2, 0xf9, 0x0a, 0x00, 0x00};
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xdd, 0x58, 0x6d, 0x6f, 0xdb, 0x38, 0x12, 0xfe, 0xde,
|
||||
0x5f, 0x31, 0xa7, 0x36, 0x6b, 0x6b, 0x1b, 0x51, 0x22, 0xe5, 0xb7, 0xd8, 0x92, 0x16, 0x69, 0xae, 0x8b, 0x5d, 0xa0,
|
||||
0xdd, 0x2d, 0x90, 0x6c, 0xef, 0x43, 0x51, 0x20, 0xb4, 0x34, 0xb2, 0xd8, 0x48, 0xa4, 0x4e, 0xa4, 0x5f, 0x52, 0xc3,
|
||||
0xf7, 0xdb, 0x0f, 0x94, 0x6c, 0xc7, 0xe9, 0x35, 0x87, 0xeb, 0xe2, 0x0e, 0x87, 0xdd, 0x18, 0x21, 0x86, 0xe4, 0xcc,
|
||||
0x70, 0xe6, 0xf1, 0x0c, 0x67, 0xcc, 0xe8, 0x2f, 0x99, 0x4a, 0xcd, 0x7d, 0x8d, 0x50, 0x98, 0xaa, 0x4c, 0x22, 0x3b,
|
||||
0x42, 0xc9, 0xe5, 0x22, 0x46, 0x99, 0x44, 0x05, 0xf2, 0x2c, 0x89, 0x2a, 0x34, 0x1c, 0xd2, 0x82, 0x37, 0x1a, 0x4d,
|
||||
0xfc, 0xdb, 0xcd, 0x8f, 0xde, 0x04, 0xfc, 0x24, 0x2a, 0x85, 0xbc, 0x83, 0x06, 0xcb, 0x58, 0xa4, 0x4a, 0x42, 0xd1,
|
||||
0x60, 0x1e, 0x67, 0xdc, 0xf0, 0xa9, 0xa8, 0xf8, 0x02, 0x2d, 0x43, 0x2b, 0x26, 0x79, 0x85, 0xf1, 0x4a, 0xe0, 0xba,
|
||||
0x56, 0x8d, 0x81, 0x54, 0x49, 0x83, 0xd2, 0xc4, 0xce, 0x5a, 0x64, 0xa6, 0x88, 0x33, 0x5c, 0x89, 0x14, 0xbd, 0x76,
|
||||
0x72, 0x2e, 0xa4, 0x30, 0x82, 0x97, 0x9e, 0x4e, 0x79, 0x89, 0x31, 0x3d, 0x5f, 0x6a, 0x6c, 0xda, 0x09, 0x9f, 0x97,
|
||||
0x18, 0x4b, 0xe5, 0xf8, 0x49, 0xa4, 0xd3, 0x46, 0xd4, 0x06, 0xac, 0xbd, 0x71, 0xa5, 0xb2, 0x65, 0x89, 0x89, 0xef,
|
||||
0x73, 0xad, 0xd1, 0x68, 0x5f, 0xc8, 0x0c, 0x37, 0x64, 0x14, 0x86, 0x29, 0xe3, 0xe3, 0x9c, 0x7c, 0xd2, 0xcf, 0x32,
|
||||
0x95, 0x2e, 0x2b, 0x94, 0x86, 0x94, 0x2a, 0xe5, 0x46, 0x28, 0x49, 0x34, 0xf2, 0x26, 0x2d, 0xe2, 0x38, 0x76, 0x7e,
|
||||
0xd0, 0x7c, 0x85, 0xce, 0x77, 0xdf, 0xf5, 0x8f, 0x4c, 0x0b, 0x34, 0xaf, 0x4b, 0xb4, 0xa4, 0x7e, 0x75, 0x7f, 0xc3,
|
||||
0x17, 0xbf, 0xf0, 0x0a, 0xfb, 0x0e, 0xd7, 0x22, 0x43, 0xc7, 0xfd, 0x10, 0x7c, 0x24, 0xda, 0xdc, 0x97, 0x48, 0x32,
|
||||
0xa1, 0xeb, 0x92, 0xdf, 0xc7, 0xce, 0xbc, 0x54, 0xe9, 0x9d, 0xe3, 0xce, 0xf2, 0xa5, 0x4c, 0xad, 0x72, 0xd0, 0x7d,
|
||||
0x74, 0xb7, 0x25, 0x1a, 0x30, 0xf1, 0x5b, 0x6e, 0x0a, 0x52, 0xf1, 0x4d, 0xbf, 0x23, 0x84, 0xec, 0xb3, 0xef, 0xfb,
|
||||
0xf8, 0x92, 0x06, 0x81, 0x7b, 0xde, 0x0e, 0x81, 0xeb, 0xd3, 0x20, 0x98, 0x35, 0x68, 0x96, 0x8d, 0x04, 0xde, 0xbf,
|
||||
0x8d, 0x6a, 0x6e, 0x0a, 0xc8, 0x62, 0xa7, 0xa2, 0x8c, 0x04, 0xc1, 0x04, 0xe8, 0x05, 0x61, 0x43, 0x8f, 0x52, 0x12,
|
||||
0x7a, 0x74, 0x98, 0x8e, 0xbd, 0x21, 0xd0, 0x81, 0x37, 0x04, 0xc6, 0xc8, 0x10, 0x82, 0xcf, 0x0e, 0xe4, 0xa2, 0x2c,
|
||||
0x63, 0x47, 0x2a, 0x89, 0x0e, 0x68, 0xd3, 0xa8, 0x3b, 0x8c, 0x9d, 0x74, 0xd9, 0x34, 0x28, 0xcd, 0x95, 0x2a, 0x55,
|
||||
0xe3, 0xf8, 0xc9, 0x33, 0x78, 0xf4, 0xf7, 0xcd, 0x47, 0x98, 0x86, 0x4b, 0x9d, 0xab, 0xa6, 0x8a, 0x9d, 0xf6, 0x4b,
|
||||
0xe9, 0xbf, 0xd8, 0x9a, 0x1d, 0xd8, 0xc1, 0x3d, 0xd9, 0xf4, 0x54, 0x23, 0x16, 0x42, 0xc6, 0x0e, 0x65, 0x40, 0x27,
|
||||
0x8e, 0x9f, 0xdc, 0xba, 0xbb, 0x23, 0x26, 0xdc, 0x62, 0xb2, 0xf7, 0x52, 0xf5, 0x3f, 0xdc, 0x46, 0x7a, 0xb5, 0x80,
|
||||
0x4d, 0x55, 0x4a, 0x1d, 0x3b, 0x85, 0x31, 0xf5, 0xd4, 0xf7, 0xd7, 0xeb, 0x35, 0x59, 0x87, 0x44, 0x35, 0x0b, 0x9f,
|
||||
0x05, 0x41, 0xe0, 0xeb, 0xd5, 0xc2, 0x81, 0x2e, 0x3e, 0x1c, 0x36, 0x70, 0xa0, 0x40, 0xb1, 0x28, 0x4c, 0x4b, 0x27,
|
||||
0x2f, 0xb6, 0xb8, 0x8b, 0x2c, 0x47, 0x72, 0xfb, 0xf1, 0xe4, 0x14, 0x71, 0x72, 0x0a, 0xfe, 0x70, 0x82, 0x66, 0xef,
|
||||
0xad, 0x35, 0x6a, 0xcc, 0x19, 0x30, 0x08, 0xda, 0x0f, 0xf3, 0x2c, 0xbd, 0x9f, 0x79, 0x5f, 0xcc, 0xe0, 0x64, 0x06,
|
||||
0x0c, 0x9e, 0x01, 0xb0, 0x6a, 0xe4, 0x5d, 0x1c, 0xc5, 0xa9, 0xdd, 0x5e, 0xd1, 0xe0, 0x61, 0xc1, 0xca, 0xfc, 0x34,
|
||||
0x3a, 0x9d, 0x7b, 0xec, 0xbd, 0x65, 0xb0, 0xd8, 0x1f, 0x85, 0x3c, 0x56, 0xd0, 0xf7, 0x23, 0x3e, 0x84, 0xe1, 0x7e,
|
||||
0x65, 0xe8, 0x59, 0xfa, 0x38, 0xb3, 0x27, 0xc1, 0x70, 0xc5, 0x0a, 0x5a, 0x79, 0x23, 0x6f, 0xc8, 0x43, 0x08, 0xf7,
|
||||
0x26, 0x85, 0x10, 0xae, 0x58, 0x31, 0x7a, 0x3f, 0x3a, 0x5d, 0xf3, 0xc2, 0xcf, 0x3d, 0x0b, 0xf3, 0xd4, 0x71, 0x1e,
|
||||
0x30, 0x50, 0xa7, 0x18, 0x90, 0x4f, 0x4a, 0xc8, 0xbe, 0xe3, 0xb8, 0xbb, 0x1c, 0x4d, 0x5a, 0xf4, 0x1d, 0x3f, 0x55,
|
||||
0x32, 0x17, 0x0b, 0xf2, 0x49, 0x2b, 0xe9, 0xb8, 0xc4, 0x14, 0x28, 0xfb, 0x07, 0x51, 0x2b, 0x88, 0xed, 0x4e, 0xff,
|
||||
0xcb, 0x1d, 0xe3, 0x6e, 0x8f, 0xf9, 0x61, 0x84, 0x29, 0x31, 0x36, 0xc4, 0x66, 0xf4, 0xf9, 0x71, 0x75, 0xae, 0xb2,
|
||||
0xfb, 0x27, 0x52, 0xa7, 0xa0, 0x5d, 0xde, 0x08, 0x29, 0xb1, 0xb9, 0xc1, 0x8d, 0x89, 0x9d, 0xb7, 0x97, 0x57, 0x70,
|
||||
0x99, 0x65, 0x0d, 0x6a, 0x3d, 0x05, 0xe7, 0xa5, 0x21, 0x15, 0x4f, 0xff, 0x73, 0x5d, 0xf4, 0x91, 0xae, 0xbf, 0x89,
|
||||
0x1f, 0x05, 0xfc, 0x82, 0x66, 0xad, 0x9a, 0xbb, 0xbd, 0x36, 0x6b, 0xda, 0xcc, 0x66, 0x60, 0x13, 0x1b, 0xc2, 0x6b,
|
||||
0x4d, 0x74, 0x29, 0x52, 0xec, 0x53, 0x97, 0x54, 0xbc, 0x7e, 0xf0, 0x4a, 0x1e, 0x80, 0xba, 0x8d, 0x32, 0xb1, 0x82,
|
||||
0xb4, 0xe4, 0x5a, 0xc7, 0x8e, 0xec, 0x54, 0x39, 0xb0, 0x4f, 0x1b, 0x25, 0xd3, 0x52, 0xa4, 0x77, 0xb1, 0xf3, 0x95,
|
||||
0x1b, 0xe2, 0xd5, 0xfd, 0xcf, 0x59, 0xbf, 0xa7, 0xb5, 0xc8, 0x7a, 0x2e, 0x59, 0xf1, 0x72, 0x89, 0x10, 0x83, 0x29,
|
||||
0x84, 0x7e, 0x30, 0x70, 0xf6, 0xa4, 0x58, 0xad, 0xef, 0x7a, 0x2e, 0xc9, 0x55, 0xba, 0xd4, 0x7d, 0xd7, 0x39, 0x64,
|
||||
0x69, 0xc4, 0xbb, 0x3b, 0xd4, 0x79, 0xee, 0x7c, 0x61, 0x91, 0x57, 0x62, 0x6e, 0x9c, 0x87, 0x6c, 0x7e, 0xb1, 0xd5,
|
||||
0x7d, 0x49, 0x1a, 0xad, 0x85, 0xbb, 0x3b, 0x2e, 0x46, 0xba, 0xe6, 0xf2, 0x4b, 0x41, 0x6b, 0xa0, 0x4d, 0x1a, 0x49,
|
||||
0x2c, 0x65, 0x33, 0xa7, 0xe6, 0xf2, 0x78, 0xa0, 0xcf, 0x0f, 0xe4, 0x8b, 0xad, 0xe8, 0x4b, 0x7b, 0x4b, 0xde, 0x1d,
|
||||
0x35, 0x46, 0x7e, 0x26, 0x56, 0xc9, 0xed, 0xce, 0x7d, 0xf0, 0xe3, 0xef, 0x4b, 0x6c, 0xee, 0xaf, 0xb1, 0xc4, 0xd4,
|
||||
0xa8, 0xa6, 0xef, 0x3c, 0x97, 0x68, 0x1c, 0xb7, 0x73, 0xf8, 0xa7, 0x9b, 0xb7, 0x6f, 0x62, 0xd5, 0x6f, 0xdc, 0xf3,
|
||||
0xa7, 0xb8, 0x6d, 0xb5, 0xf8, 0xd0, 0x60, 0xf9, 0x8f, 0xb8, 0x67, 0xeb, 0x45, 0xef, 0xa3, 0xe3, 0x92, 0xd6, 0xdf,
|
||||
0xdb, 0x87, 0xa2, 0x61, 0x13, 0xfb, 0xe5, 0xa6, 0x2a, 0xcf, 0xad, 0x87, 0xde, 0x68, 0xe8, 0xee, 0x6e, 0x77, 0xee,
|
||||
0xce, 0x9d, 0x45, 0x7e, 0x77, 0xef, 0x27, 0x51, 0x7b, 0x05, 0x27, 0xdf, 0x6f, 0xe7, 0x6a, 0xe3, 0x69, 0xf1, 0x59,
|
||||
0xc8, 0xc5, 0x54, 0xc8, 0x02, 0x1b, 0x61, 0x76, 0x99, 0x58, 0x9d, 0x0b, 0x59, 0x2f, 0xcd, 0xb6, 0xe6, 0x59, 0x66,
|
||||
0x77, 0x86, 0xf5, 0x66, 0x96, 0x2b, 0x69, 0x2c, 0x27, 0x4e, 0x29, 0x56, 0xbb, 0x6e, 0xbf, 0xbd, 0x5b, 0xa6, 0x17,
|
||||
0xc3, 0xb3, 0x9d, 0x0d, 0xb8, 0xad, 0xc1, 0x8d, 0xf1, 0x78, 0x29, 0x16, 0x72, 0x9a, 0xa2, 0x34, 0xd8, 0x74, 0x42,
|
||||
0x39, 0xaf, 0x44, 0x79, 0x3f, 0xd5, 0x5c, 0x6a, 0x4f, 0x63, 0x23, 0xf2, 0xdd, 0x7c, 0x69, 0x8c, 0x92, 0xdb, 0xb9,
|
||||
0x6a, 0x32, 0x6c, 0xa6, 0xc1, 0xac, 0x23, 0xbc, 0x86, 0x67, 0x62, 0xa9, 0xa7, 0x24, 0x6c, 0xb0, 0x9a, 0xcd, 0x79,
|
||||
0x7a, 0xb7, 0x68, 0xd4, 0x52, 0x66, 0x5e, 0x6a, 0x6f, 0xe1, 0xe9, 0x73, 0x9a, 0xf3, 0x10, 0xd3, 0xd9, 0x7e, 0x96,
|
||||
0xe7, 0xf9, 0xac, 0x14, 0x12, 0xbd, 0xee, 0x56, 0x9b, 0x32, 0x32, 0xb0, 0x62, 0x27, 0x66, 0x12, 0x66, 0x17, 0x3a,
|
||||
0x1b, 0x69, 0x10, 0x9c, 0xcd, 0x0e, 0xee, 0x04, 0xb3, 0x74, 0xd9, 0x68, 0xd5, 0x4c, 0x6b, 0x25, 0xac, 0x99, 0xbb,
|
||||
0x8a, 0x0b, 0x79, 0x6a, 0xbd, 0x0d, 0x93, 0xd9, 0xbe, 0x3c, 0x4d, 0x85, 0x6c, 0x8f, 0x69, 0x8b, 0xd4, 0xac, 0x12,
|
||||
0xb2, 0x2b, 0xb2, 0x53, 0x36, 0x0a, 0xea, 0xcd, 0x8e, 0xec, 0x03, 0x64, 0x7b, 0xe0, 0xce, 0x4b, 0xdc, 0xcc, 0x3e,
|
||||
0x2d, 0xb5, 0x11, 0xf9, 0xbd, 0xb7, 0x2f, 0xd2, 0x53, 0x5d, 0xf3, 0x14, 0xbd, 0x39, 0x9a, 0x35, 0xa2, 0x9c, 0xb5,
|
||||
0x67, 0x78, 0xc2, 0x60, 0xa5, 0xf7, 0x38, 0x1d, 0xd5, 0xb4, 0x01, 0xfa, 0x58, 0xd7, 0xbf, 0xe3, 0xb6, 0xb1, 0xb8,
|
||||
0xad, 0x78, 0xb3, 0x10, 0xd2, 0x9b, 0x2b, 0x63, 0x54, 0x35, 0xf5, 0xc6, 0xf5, 0x66, 0xb6, 0x5f, 0xb2, 0xca, 0xa6,
|
||||
0xd4, 0x9a, 0xd9, 0xd6, 0xde, 0x03, 0xde, 0xb4, 0xde, 0x80, 0x56, 0xa5, 0xc8, 0xf6, 0x7c, 0x2d, 0x0b, 0x04, 0x47,
|
||||
0x78, 0xe8, 0xb0, 0xde, 0x80, 0x5d, 0x3b, 0x40, 0x3d, 0xc8, 0x27, 0x9c, 0x06, 0x5f, 0xf9, 0x46, 0xb2, 0x3c, 0x67,
|
||||
0xf3, 0xfc, 0x88, 0x94, 0x2d, 0xa1, 0x3b, 0xb1, 0x8f, 0x0a, 0x36, 0xa8, 0x37, 0xb3, 0xc3, 0x77, 0x33, 0xa8, 0x37,
|
||||
0x3b, 0xd1, 0xa6, 0xc5, 0xf6, 0x44, 0x4b, 0x1b, 0xaa, 0xd3, 0x65, 0x53, 0xf6, 0x9d, 0xaf, 0x84, 0xee, 0x59, 0x78,
|
||||
0xf5, 0x50, 0xe2, 0x7a, 0x4f, 0x97, 0xb8, 0x1e, 0xd8, 0xa6, 0xe8, 0x95, 0xda, 0xc4, 0xbd, 0xb6, 0xd8, 0x0c, 0x80,
|
||||
0x0d, 0x7a, 0x67, 0xe1, 0xeb, 0xb3, 0xf0, 0xea, 0xbf, 0x52, 0xbb, 0x7e, 0x77, 0xe1, 0xfa, 0x86, 0xaa, 0xf5, 0x8d,
|
||||
0x15, 0xab, 0xf3, 0xce, 0x3a, 0x7f, 0x16, 0xbe, 0x76, 0xdc, 0x9d, 0x20, 0x5a, 0x2c, 0xe8, 0xff, 0x02, 0xda, 0x7f,
|
||||
0xc5, 0x31, 0xbc, 0xa4, 0x13, 0x72, 0x01, 0xed, 0xd0, 0x41, 0x44, 0xc2, 0x09, 0x8c, 0xaf, 0x06, 0x64, 0x40, 0xc1,
|
||||
0xb6, 0x43, 0x23, 0x18, 0x93, 0xc9, 0x05, 0xd0, 0x11, 0x09, 0xc7, 0x40, 0x19, 0x30, 0x4a, 0x86, 0x6f, 0x58, 0x48,
|
||||
0x46, 0x43, 0x18, 0x5f, 0xb1, 0x80, 0x84, 0x0c, 0x3a, 0xde, 0x11, 0x61, 0x0c, 0x42, 0xcb, 0x12, 0x56, 0x01, 0xb0,
|
||||
0x34, 0x24, 0xc1, 0x18, 0x02, 0x18, 0x91, 0xe0, 0x82, 0x4c, 0x46, 0x30, 0x21, 0x63, 0x0a, 0x8c, 0x0c, 0x86, 0xa5,
|
||||
0x37, 0x24, 0x14, 0x46, 0x24, 0x1c, 0xf1, 0x09, 0x19, 0x84, 0xd0, 0x0e, 0x1d, 0x1c, 0x63, 0xc2, 0x98, 0x47, 0x02,
|
||||
0xfa, 0x26, 0x24, 0x6c, 0x0c, 0x63, 0x32, 0x18, 0x5c, 0xd2, 0x11, 0xb9, 0x18, 0x40, 0x37, 0x76, 0xf0, 0x52, 0x06,
|
||||
0xc3, 0xa7, 0x40, 0x63, 0x7f, 0x5e, 0xd0, 0x42, 0xc2, 0x28, 0x84, 0xe4, 0x62, 0xc2, 0x6d, 0x5f, 0xca, 0xa0, 0x1b,
|
||||
0x3b, 0xdc, 0x28, 0x85, 0xe0, 0x77, 0x63, 0x16, 0xfe, 0x79, 0x31, 0xa3, 0x16, 0x01, 0x46, 0x06, 0xe1, 0x25, 0x0d,
|
||||
0xc9, 0x08, 0xda, 0xa1, 0x3b, 0x9b, 0x32, 0x98, 0x5c, 0x5d, 0xc0, 0x04, 0x46, 0x64, 0x34, 0x81, 0x0b, 0x18, 0x5a,
|
||||
0x74, 0x2f, 0xc8, 0x64, 0xd0, 0x09, 0x79, 0x8c, 0x7c, 0x2b, 0x8c, 0x83, 0x3f, 0x30, 0x8c, 0x4f, 0xf9, 0xf4, 0x07,
|
||||
0x76, 0xe9, 0xff, 0x71, 0x05, 0x45, 0x7e, 0xd7, 0x86, 0x45, 0x7e, 0xf7, 0x3c, 0x60, 0xbb, 0xa8, 0x24, 0xb2, 0xdd,
|
||||
0x48, 0x12, 0x15, 0x14, 0x44, 0x16, 0x57, 0x3c, 0x4d, 0x4e, 0x5a, 0xfd, 0xc8, 0x2f, 0xe8, 0x61, 0xab, 0xa0, 0xc9,
|
||||
0xa3, 0xc6, 0xbd, 0xdb, 0x6b, 0x2b, 0x7d, 0x72, 0x53, 0x20, 0xbc, 0xbe, 0x7e, 0x07, 0x6b, 0x51, 0x96, 0x20, 0xd5,
|
||||
0x1a, 0x4c, 0x73, 0x0f, 0x46, 0xd9, 0x57, 0x03, 0x89, 0xa9, 0xb1, 0xa4, 0x29, 0x10, 0xf6, 0x7d, 0x04, 0x21, 0x24,
|
||||
0x9a, 0x37, 0xc9, 0xbb, 0x12, 0xb9, 0x46, 0x58, 0x88, 0x15, 0x82, 0x30, 0xa0, 0x55, 0x85, 0x60, 0x84, 0x1d, 0x8e,
|
||||
0x82, 0x2d, 0x5f, 0xe4, 0x77, 0x87, 0x74, 0x8d, 0xb2, 0xc8, 0x62, 0x89, 0x26, 0xd9, 0x77, 0xc4, 0x51, 0x11, 0x76,
|
||||
0x56, 0x5d, 0xa3, 0x31, 0x42, 0x2e, 0xac, 0x55, 0x61, 0x12, 0xd9, 0x5f, 0xb7, 0xc0, 0xdb, 0xdf, 0x0c, 0xb1, 0xbf,
|
||||
0x16, 0xb9, 0xb0, 0x6f, 0x06, 0x49, 0xd4, 0x76, 0x91, 0x56, 0x83, 0x6d, 0x64, 0xba, 0x07, 0x8e, 0x96, 0x2a, 0x51,
|
||||
0x2e, 0x4c, 0x11, 0x87, 0x0c, 0xea, 0x92, 0xa7, 0x58, 0xa8, 0x32, 0xc3, 0x26, 0xbe, 0xbe, 0xfe, 0xf9, 0xaf, 0xf6,
|
||||
0x35, 0xc4, 0x9a, 0x70, 0x94, 0xac, 0xf5, 0x5d, 0x27, 0x68, 0x89, 0xbd, 0xdc, 0x68, 0xd0, 0xbd, 0x6b, 0xd4, 0x5c,
|
||||
0xeb, 0xb5, 0x6a, 0xb2, 0x47, 0x5a, 0xde, 0x1d, 0x16, 0xf7, 0x9a, 0xda, 0xff, 0xb6, 0x1f, 0xed, 0x84, 0xf4, 0x72,
|
||||
0x5e, 0x09, 0x93, 0x5c, 0xf3, 0x15, 0x46, 0x7e, 0xb7, 0x91, 0x44, 0xbe, 0x75, 0xa0, 0xe3, 0x2d, 0xf6, 0x32, 0x05,
|
||||
0x4d, 0x7e, 0xbd, 0xb9, 0x84, 0xdf, 0xea, 0x8c, 0x1b, 0xec, 0xb0, 0x6f, 0xbd, 0xac, 0xd0, 0x14, 0x2a, 0x8b, 0xdf,
|
||||
0xfd, 0x7a, 0x7d, 0x73, 0xf4, 0x78, 0xd9, 0x32, 0x01, 0xca, 0xb4, 0x7b, 0x6f, 0x59, 0x96, 0x46, 0xd4, 0xbc, 0x31,
|
||||
0xad, 0x5a, 0xcf, 0x66, 0xc7, 0xc1, 0xa3, 0x76, 0x3f, 0x17, 0x25, 0x76, 0x4e, 0xed, 0x05, 0xfd, 0x04, 0xbe, 0x66,
|
||||
0xe3, 0xe1, 0xec, 0x2f, 0xac, 0xf4, 0xbb, 0x00, 0xf2, 0xbb, 0x68, 0xf2, 0xdb, 0xd7, 0xa8, 0x7f, 0x02, 0x14, 0xee,
|
||||
0xbc, 0x64, 0x9d, 0x12, 0x00, 0x00};
|
||||
|
||||
} // namespace captive_portal
|
||||
} // namespace esphome
|
||||
|
@@ -152,7 +152,7 @@ void CCS811Component::send_env_data_() {
|
||||
void CCS811Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "CCS811");
|
||||
LOG_I2C_DEVICE(this)
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_UPDATE_INTERVAL(this)
|
||||
LOG_SENSOR(" ", "CO2 Sensor", this->co2_);
|
||||
LOG_SENSOR(" ", "TVOC Sensor", this->tvoc_);
|
||||
LOG_TEXT_SENSOR(" ", "Firmware Version Sensor", this->version_)
|
||||
|
@@ -52,7 +52,7 @@ void DebugComponent::on_shutdown() {
|
||||
char buffer[REBOOT_MAX_LEN]{};
|
||||
auto pref = global_preferences->make_preference(REBOOT_MAX_LEN, fnv1_hash(REBOOT_KEY + App.get_name()));
|
||||
if (component != nullptr) {
|
||||
strncpy(buffer, LOG_STR_ARG(component->get_component_log_str()), REBOOT_MAX_LEN - 1);
|
||||
strncpy(buffer, component->get_component_source(), REBOOT_MAX_LEN - 1);
|
||||
buffer[REBOOT_MAX_LEN - 1] = '\0';
|
||||
}
|
||||
ESP_LOGD(TAG, "Storing reboot source: %s", buffer);
|
||||
|
@@ -54,13 +54,13 @@ struct ISRPinArg {
|
||||
|
||||
ISRInternalGPIOPin ESP32InternalGPIOPin::to_isr() const {
|
||||
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
arg->pin = this->get_pin_num();
|
||||
arg->pin = this->pin_;
|
||||
arg->flags = gpio::FLAG_NONE;
|
||||
arg->inverted = this->pin_flags_.inverted;
|
||||
arg->inverted = inverted_;
|
||||
#if defined(USE_ESP32_VARIANT_ESP32)
|
||||
arg->use_rtc = rtc_gpio_is_valid_gpio(this->get_pin_num());
|
||||
arg->use_rtc = rtc_gpio_is_valid_gpio(this->pin_);
|
||||
if (arg->use_rtc)
|
||||
arg->rtc_pin = rtc_io_number_get(this->get_pin_num());
|
||||
arg->rtc_pin = rtc_io_number_get(this->pin_);
|
||||
#endif
|
||||
return ISRInternalGPIOPin((void *) arg);
|
||||
}
|
||||
@@ -69,23 +69,23 @@ void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpi
|
||||
gpio_int_type_t idf_type = GPIO_INTR_ANYEDGE;
|
||||
switch (type) {
|
||||
case gpio::INTERRUPT_RISING_EDGE:
|
||||
idf_type = this->pin_flags_.inverted ? GPIO_INTR_NEGEDGE : GPIO_INTR_POSEDGE;
|
||||
idf_type = inverted_ ? GPIO_INTR_NEGEDGE : GPIO_INTR_POSEDGE;
|
||||
break;
|
||||
case gpio::INTERRUPT_FALLING_EDGE:
|
||||
idf_type = this->pin_flags_.inverted ? GPIO_INTR_POSEDGE : GPIO_INTR_NEGEDGE;
|
||||
idf_type = inverted_ ? GPIO_INTR_POSEDGE : GPIO_INTR_NEGEDGE;
|
||||
break;
|
||||
case gpio::INTERRUPT_ANY_EDGE:
|
||||
idf_type = GPIO_INTR_ANYEDGE;
|
||||
break;
|
||||
case gpio::INTERRUPT_LOW_LEVEL:
|
||||
idf_type = this->pin_flags_.inverted ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL;
|
||||
idf_type = inverted_ ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL;
|
||||
break;
|
||||
case gpio::INTERRUPT_HIGH_LEVEL:
|
||||
idf_type = this->pin_flags_.inverted ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL;
|
||||
idf_type = inverted_ ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL;
|
||||
break;
|
||||
}
|
||||
gpio_set_intr_type(this->get_pin_num(), idf_type);
|
||||
gpio_intr_enable(this->get_pin_num());
|
||||
gpio_set_intr_type(pin_, idf_type);
|
||||
gpio_intr_enable(pin_);
|
||||
if (!isr_service_installed) {
|
||||
auto res = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL3);
|
||||
if (res != ESP_OK) {
|
||||
@@ -94,31 +94,31 @@ void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpi
|
||||
}
|
||||
isr_service_installed = true;
|
||||
}
|
||||
gpio_isr_handler_add(this->get_pin_num(), func, arg);
|
||||
gpio_isr_handler_add(pin_, func, arg);
|
||||
}
|
||||
|
||||
std::string ESP32InternalGPIOPin::dump_summary() const {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "GPIO%" PRIu32, static_cast<uint32_t>(this->pin_));
|
||||
snprintf(buffer, sizeof(buffer), "GPIO%" PRIu32, static_cast<uint32_t>(pin_));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void ESP32InternalGPIOPin::setup() {
|
||||
gpio_config_t conf{};
|
||||
conf.pin_bit_mask = 1ULL << static_cast<uint32_t>(this->pin_);
|
||||
conf.mode = flags_to_mode(this->flags_);
|
||||
conf.pull_up_en = this->flags_ & gpio::FLAG_PULLUP ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE;
|
||||
conf.pull_down_en = this->flags_ & gpio::FLAG_PULLDOWN ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE;
|
||||
conf.pin_bit_mask = 1ULL << static_cast<uint32_t>(pin_);
|
||||
conf.mode = flags_to_mode(flags_);
|
||||
conf.pull_up_en = flags_ & gpio::FLAG_PULLUP ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE;
|
||||
conf.pull_down_en = flags_ & gpio::FLAG_PULLDOWN ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE;
|
||||
conf.intr_type = GPIO_INTR_DISABLE;
|
||||
gpio_config(&conf);
|
||||
if (this->flags_ & gpio::FLAG_OUTPUT) {
|
||||
gpio_set_drive_capability(this->get_pin_num(), this->get_drive_strength());
|
||||
if (flags_ & gpio::FLAG_OUTPUT) {
|
||||
gpio_set_drive_capability(pin_, drive_strength_);
|
||||
}
|
||||
}
|
||||
|
||||
void ESP32InternalGPIOPin::pin_mode(gpio::Flags flags) {
|
||||
// can't call gpio_config here because that logs in esp-idf which may cause issues
|
||||
gpio_set_direction(this->get_pin_num(), flags_to_mode(flags));
|
||||
gpio_set_direction(pin_, flags_to_mode(flags));
|
||||
gpio_pull_mode_t pull_mode = GPIO_FLOATING;
|
||||
if ((flags & gpio::FLAG_PULLUP) && (flags & gpio::FLAG_PULLDOWN)) {
|
||||
pull_mode = GPIO_PULLUP_PULLDOWN;
|
||||
@@ -127,16 +127,12 @@ void ESP32InternalGPIOPin::pin_mode(gpio::Flags flags) {
|
||||
} else if (flags & gpio::FLAG_PULLDOWN) {
|
||||
pull_mode = GPIO_PULLDOWN_ONLY;
|
||||
}
|
||||
gpio_set_pull_mode(this->get_pin_num(), pull_mode);
|
||||
gpio_set_pull_mode(pin_, pull_mode);
|
||||
}
|
||||
|
||||
bool ESP32InternalGPIOPin::digital_read() {
|
||||
return bool(gpio_get_level(this->get_pin_num())) != this->pin_flags_.inverted;
|
||||
}
|
||||
void ESP32InternalGPIOPin::digital_write(bool value) {
|
||||
gpio_set_level(this->get_pin_num(), value != this->pin_flags_.inverted ? 1 : 0);
|
||||
}
|
||||
void ESP32InternalGPIOPin::detach_interrupt() const { gpio_intr_disable(this->get_pin_num()); }
|
||||
bool ESP32InternalGPIOPin::digital_read() { return bool(gpio_get_level(pin_)) != inverted_; }
|
||||
void ESP32InternalGPIOPin::digital_write(bool value) { gpio_set_level(pin_, value != inverted_ ? 1 : 0); }
|
||||
void ESP32InternalGPIOPin::detach_interrupt() const { gpio_intr_disable(pin_); }
|
||||
|
||||
} // namespace esp32
|
||||
|
||||
|
@@ -7,18 +7,12 @@
|
||||
namespace esphome {
|
||||
namespace esp32 {
|
||||
|
||||
// Static assertions to ensure our bit-packed fields can hold the enum values
|
||||
static_assert(GPIO_NUM_MAX <= 256, "gpio_num_t has too many values for uint8_t");
|
||||
static_assert(GPIO_DRIVE_CAP_MAX <= 4, "gpio_drive_cap_t has too many values for 2-bit field");
|
||||
|
||||
class ESP32InternalGPIOPin : public InternalGPIOPin {
|
||||
public:
|
||||
void set_pin(gpio_num_t pin) { this->pin_ = static_cast<uint8_t>(pin); }
|
||||
void set_inverted(bool inverted) { this->pin_flags_.inverted = inverted; }
|
||||
void set_drive_strength(gpio_drive_cap_t drive_strength) {
|
||||
this->pin_flags_.drive_strength = static_cast<uint8_t>(drive_strength);
|
||||
}
|
||||
void set_flags(gpio::Flags flags) { this->flags_ = flags; }
|
||||
void set_pin(gpio_num_t pin) { pin_ = pin; }
|
||||
void set_inverted(bool inverted) { inverted_ = inverted; }
|
||||
void set_drive_strength(gpio_drive_cap_t drive_strength) { drive_strength_ = drive_strength; }
|
||||
void set_flags(gpio::Flags flags) { flags_ = flags; }
|
||||
|
||||
void setup() override;
|
||||
void pin_mode(gpio::Flags flags) override;
|
||||
@@ -27,26 +21,17 @@ class ESP32InternalGPIOPin : public InternalGPIOPin {
|
||||
std::string dump_summary() const override;
|
||||
void detach_interrupt() const override;
|
||||
ISRInternalGPIOPin to_isr() const override;
|
||||
uint8_t get_pin() const override { return this->pin_; }
|
||||
gpio::Flags get_flags() const override { return this->flags_; }
|
||||
bool is_inverted() const override { return this->pin_flags_.inverted; }
|
||||
gpio_num_t get_pin_num() const { return static_cast<gpio_num_t>(this->pin_); }
|
||||
gpio_drive_cap_t get_drive_strength() const { return static_cast<gpio_drive_cap_t>(this->pin_flags_.drive_strength); }
|
||||
uint8_t get_pin() const override { return (uint8_t) pin_; }
|
||||
gpio::Flags get_flags() const override { return flags_; }
|
||||
bool is_inverted() const override { return inverted_; }
|
||||
|
||||
protected:
|
||||
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
|
||||
|
||||
// Memory layout: 8 bytes total on 32-bit systems
|
||||
// - 3 bytes for members below
|
||||
// - 1 byte padding for alignment
|
||||
// - 4 bytes for vtable pointer
|
||||
uint8_t pin_; // GPIO pin number (0-255, actual max ~54 on ESP32)
|
||||
gpio::Flags flags_; // GPIO flags (1 byte)
|
||||
struct PinFlags {
|
||||
uint8_t inverted : 1; // Invert pin logic (1 bit)
|
||||
uint8_t drive_strength : 2; // Drive strength 0-3 (2 bits)
|
||||
uint8_t reserved : 5; // Reserved for future use (5 bits)
|
||||
} pin_flags_; // Total: 1 byte
|
||||
gpio_num_t pin_;
|
||||
gpio_drive_cap_t drive_strength_;
|
||||
gpio::Flags flags_;
|
||||
bool inverted_;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
static bool isr_service_installed;
|
||||
};
|
||||
|
@@ -7,7 +7,6 @@
|
||||
#include <cstdio>
|
||||
#include <cinttypes>
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome::esp32_ble {
|
||||
|
||||
@@ -170,42 +169,22 @@ bool ESPBTUUID::operator==(const ESPBTUUID &uuid) const {
|
||||
}
|
||||
esp_bt_uuid_t ESPBTUUID::get_uuid() const { return this->uuid_; }
|
||||
std::string ESPBTUUID::to_string() const {
|
||||
char buf[40]; // Enough for 128-bit UUID with dashes
|
||||
char *pos = buf;
|
||||
|
||||
switch (this->uuid_.len) {
|
||||
case ESP_UUID_LEN_16:
|
||||
*pos++ = '0';
|
||||
*pos++ = 'x';
|
||||
*pos++ = format_hex_pretty_char(this->uuid_.uuid.uuid16 >> 12);
|
||||
*pos++ = format_hex_pretty_char((this->uuid_.uuid.uuid16 >> 8) & 0x0F);
|
||||
*pos++ = format_hex_pretty_char((this->uuid_.uuid.uuid16 >> 4) & 0x0F);
|
||||
*pos++ = format_hex_pretty_char(this->uuid_.uuid.uuid16 & 0x0F);
|
||||
*pos = '\0';
|
||||
return std::string(buf);
|
||||
|
||||
return str_snprintf("0x%02X%02X", 6, this->uuid_.uuid.uuid16 >> 8, this->uuid_.uuid.uuid16 & 0xff);
|
||||
case ESP_UUID_LEN_32:
|
||||
*pos++ = '0';
|
||||
*pos++ = 'x';
|
||||
for (int shift = 28; shift >= 0; shift -= 4) {
|
||||
*pos++ = format_hex_pretty_char((this->uuid_.uuid.uuid32 >> shift) & 0x0F);
|
||||
}
|
||||
*pos = '\0';
|
||||
return std::string(buf);
|
||||
|
||||
return str_snprintf("0x%02" PRIX32 "%02" PRIX32 "%02" PRIX32 "%02" PRIX32, 10, (this->uuid_.uuid.uuid32 >> 24),
|
||||
(this->uuid_.uuid.uuid32 >> 16 & 0xff), (this->uuid_.uuid.uuid32 >> 8 & 0xff),
|
||||
this->uuid_.uuid.uuid32 & 0xff);
|
||||
default:
|
||||
case ESP_UUID_LEN_128:
|
||||
// Format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
std::string buf;
|
||||
for (int8_t i = 15; i >= 0; i--) {
|
||||
uint8_t byte = this->uuid_.uuid.uuid128[i];
|
||||
*pos++ = format_hex_pretty_char(byte >> 4);
|
||||
*pos++ = format_hex_pretty_char(byte & 0x0F);
|
||||
if (i == 12 || i == 10 || i == 8 || i == 6) {
|
||||
*pos++ = '-';
|
||||
}
|
||||
buf += str_snprintf("%02X", 2, this->uuid_.uuid.uuid128[i]);
|
||||
if (i == 6 || i == 8 || i == 10 || i == 12)
|
||||
buf += "-";
|
||||
}
|
||||
*pos = '\0';
|
||||
return std::string(buf);
|
||||
return buf;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
@@ -31,13 +31,12 @@ void ESP32BLEBeacon::dump_config() {
|
||||
char uuid[37];
|
||||
char *bpos = uuid;
|
||||
for (int8_t ii = 0; ii < 16; ++ii) {
|
||||
*bpos++ = format_hex_pretty_char(this->uuid_[ii] >> 4);
|
||||
*bpos++ = format_hex_pretty_char(this->uuid_[ii] & 0x0F);
|
||||
bpos += sprintf(bpos, "%02X", this->uuid_[ii]);
|
||||
if (ii == 3 || ii == 5 || ii == 7 || ii == 9) {
|
||||
*bpos++ = '-';
|
||||
bpos += sprintf(bpos, "-");
|
||||
}
|
||||
}
|
||||
*bpos = '\0';
|
||||
uuid[36] = '\0';
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" UUID: %s, Major: %u, Minor: %u, Min Interval: %ums, Max Interval: %ums, Measured Power: %d"
|
||||
", TX Power: %ddBm",
|
||||
|
@@ -60,14 +60,11 @@ class BLEClientBase : public espbt::ESPBTClient, public Component {
|
||||
if (address == 0) {
|
||||
this->address_str_ = "";
|
||||
} else {
|
||||
char buf[18];
|
||||
uint8_t mac[6] = {
|
||||
(uint8_t) ((this->address_ >> 40) & 0xff), (uint8_t) ((this->address_ >> 32) & 0xff),
|
||||
(uint8_t) ((this->address_ >> 24) & 0xff), (uint8_t) ((this->address_ >> 16) & 0xff),
|
||||
(uint8_t) ((this->address_ >> 8) & 0xff), (uint8_t) ((this->address_ >> 0) & 0xff),
|
||||
};
|
||||
format_mac_addr_upper(mac, buf);
|
||||
this->address_str_ = buf;
|
||||
this->address_str_ =
|
||||
str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, (uint8_t) (this->address_ >> 40) & 0xff,
|
||||
(uint8_t) (this->address_ >> 32) & 0xff, (uint8_t) (this->address_ >> 24) & 0xff,
|
||||
(uint8_t) (this->address_ >> 16) & 0xff, (uint8_t) (this->address_ >> 8) & 0xff,
|
||||
(uint8_t) (this->address_ >> 0) & 0xff);
|
||||
}
|
||||
}
|
||||
const std::string &address_str() const { return this->address_str_; }
|
||||
|
@@ -607,8 +607,9 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint8_t len) {
|
||||
}
|
||||
|
||||
std::string ESPBTDevice::address_str() const {
|
||||
char mac[18];
|
||||
format_mac_addr_upper(this->address_, mac);
|
||||
char mac[24];
|
||||
snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X", this->address_[0], this->address_[1], this->address_[2],
|
||||
this->address_[3], this->address_[4], this->address_[5]);
|
||||
return mac;
|
||||
}
|
||||
|
||||
|
@@ -15,8 +15,6 @@ using namespace bytebuffer;
|
||||
|
||||
static const char *const TAG = "esp32_improv.component";
|
||||
static const char *const ESPHOME_MY_LINK = "https://my.home-assistant.io/redirect/config_flow_start?domain=esphome";
|
||||
static constexpr uint16_t STOP_ADVERTISING_DELAY =
|
||||
10000; // Delay (ms) before stopping service to allow BLE clients to read the final state
|
||||
|
||||
ESP32ImprovComponent::ESP32ImprovComponent() { global_improv_component = this; }
|
||||
|
||||
@@ -33,9 +31,6 @@ void ESP32ImprovComponent::setup() {
|
||||
#endif
|
||||
global_ble_server->on(BLEServerEvt::EmptyEvt::ON_DISCONNECT,
|
||||
[this](uint16_t conn_id) { this->set_error_(improv::ERROR_NONE); });
|
||||
|
||||
// Start with loop disabled - will be enabled by start() when needed
|
||||
this->disable_loop();
|
||||
}
|
||||
|
||||
void ESP32ImprovComponent::setup_characteristics() {
|
||||
@@ -195,25 +190,6 @@ void ESP32ImprovComponent::set_status_indicator_state_(bool state) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
|
||||
const char *ESP32ImprovComponent::state_to_string_(improv::State state) {
|
||||
switch (state) {
|
||||
case improv::STATE_STOPPED:
|
||||
return "STOPPED";
|
||||
case improv::STATE_AWAITING_AUTHORIZATION:
|
||||
return "AWAITING_AUTHORIZATION";
|
||||
case improv::STATE_AUTHORIZED:
|
||||
return "AUTHORIZED";
|
||||
case improv::STATE_PROVISIONING:
|
||||
return "PROVISIONING";
|
||||
case improv::STATE_PROVISIONED:
|
||||
return "PROVISIONED";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ESP32ImprovComponent::check_identify_() {
|
||||
uint32_t now = millis();
|
||||
|
||||
@@ -227,42 +203,31 @@ bool ESP32ImprovComponent::check_identify_() {
|
||||
}
|
||||
|
||||
void ESP32ImprovComponent::set_state_(improv::State state) {
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
|
||||
if (this->state_ != state) {
|
||||
ESP_LOGD(TAG, "State transition: %s (0x%02X) -> %s (0x%02X)", this->state_to_string_(this->state_), this->state_,
|
||||
this->state_to_string_(state), state);
|
||||
}
|
||||
#endif
|
||||
ESP_LOGV(TAG, "Setting state: %d", state);
|
||||
this->state_ = state;
|
||||
if (this->status_ != nullptr && (this->status_->get_value().empty() || this->status_->get_value()[0] != state)) {
|
||||
if (this->status_->get_value().empty() || this->status_->get_value()[0] != state) {
|
||||
this->status_->set_value(ByteBuffer::wrap(static_cast<uint8_t>(state)));
|
||||
if (state != improv::STATE_STOPPED)
|
||||
this->status_->notify();
|
||||
}
|
||||
// Only advertise valid Improv states (0x01-0x04).
|
||||
// STATE_STOPPED (0x00) is internal only and not part of the Improv spec.
|
||||
// Advertising 0x00 causes undefined behavior in some clients and makes them
|
||||
// repeatedly connect trying to determine the actual state.
|
||||
if (state != improv::STATE_STOPPED) {
|
||||
std::vector<uint8_t> service_data(8, 0);
|
||||
service_data[0] = 0x77; // PR
|
||||
service_data[1] = 0x46; // IM
|
||||
service_data[2] = static_cast<uint8_t>(state);
|
||||
std::vector<uint8_t> service_data(8, 0);
|
||||
service_data[0] = 0x77; // PR
|
||||
service_data[1] = 0x46; // IM
|
||||
service_data[2] = static_cast<uint8_t>(state);
|
||||
|
||||
uint8_t capabilities = 0x00;
|
||||
uint8_t capabilities = 0x00;
|
||||
#ifdef USE_OUTPUT
|
||||
if (this->status_indicator_ != nullptr)
|
||||
capabilities |= improv::CAPABILITY_IDENTIFY;
|
||||
if (this->status_indicator_ != nullptr)
|
||||
capabilities |= improv::CAPABILITY_IDENTIFY;
|
||||
#endif
|
||||
|
||||
service_data[3] = capabilities;
|
||||
service_data[4] = 0x00; // Reserved
|
||||
service_data[5] = 0x00; // Reserved
|
||||
service_data[6] = 0x00; // Reserved
|
||||
service_data[7] = 0x00; // Reserved
|
||||
service_data[3] = capabilities;
|
||||
service_data[4] = 0x00; // Reserved
|
||||
service_data[5] = 0x00; // Reserved
|
||||
service_data[6] = 0x00; // Reserved
|
||||
service_data[7] = 0x00; // Reserved
|
||||
|
||||
esp32_ble::global_ble->advertising_set_service_data(service_data);
|
||||
}
|
||||
esp32_ble::global_ble->advertising_set_service_data(service_data);
|
||||
#ifdef USE_ESP32_IMPROV_STATE_CALLBACK
|
||||
this->state_callback_.call(this->state_, this->error_state_);
|
||||
#endif
|
||||
@@ -272,12 +237,7 @@ void ESP32ImprovComponent::set_error_(improv::Error error) {
|
||||
if (error != improv::ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "Error: %d", error);
|
||||
}
|
||||
// The error_ characteristic is initialized in setup_characteristics() which is called
|
||||
// from the loop, while the BLE disconnect callback is registered in setup().
|
||||
// error_ can be nullptr if:
|
||||
// 1. A client connects/disconnects before setup_characteristics() is called
|
||||
// 2. The device is already provisioned so the service never starts (should_start_ is false)
|
||||
if (this->error_ != nullptr && (this->error_->get_value().empty() || this->error_->get_value()[0] != error)) {
|
||||
if (this->error_->get_value().empty() || this->error_->get_value()[0] != error) {
|
||||
this->error_->set_value(ByteBuffer::wrap(static_cast<uint8_t>(error)));
|
||||
if (this->state_ != improv::STATE_STOPPED)
|
||||
this->error_->notify();
|
||||
@@ -301,10 +261,7 @@ void ESP32ImprovComponent::start() {
|
||||
|
||||
void ESP32ImprovComponent::stop() {
|
||||
this->should_start_ = false;
|
||||
// Wait before stopping the service to ensure all BLE clients see the state change.
|
||||
// This prevents clients from repeatedly reconnecting and wasting resources by allowing
|
||||
// them to observe that the device is provisioned before the service disappears.
|
||||
this->set_timeout("end-service", STOP_ADVERTISING_DELAY, [this] {
|
||||
this->set_timeout("end-service", 1000, [this] {
|
||||
if (this->state_ == improv::STATE_STOPPED || this->service_ == nullptr)
|
||||
return;
|
||||
this->service_->stop();
|
||||
|
@@ -79,12 +79,12 @@ class ESP32ImprovComponent : public Component {
|
||||
std::vector<uint8_t> incoming_data_;
|
||||
wifi::WiFiAP connecting_sta_;
|
||||
|
||||
BLEService *service_{nullptr};
|
||||
BLECharacteristic *status_{nullptr};
|
||||
BLECharacteristic *error_{nullptr};
|
||||
BLECharacteristic *rpc_{nullptr};
|
||||
BLECharacteristic *rpc_response_{nullptr};
|
||||
BLECharacteristic *capabilities_{nullptr};
|
||||
BLEService *service_ = nullptr;
|
||||
BLECharacteristic *status_;
|
||||
BLECharacteristic *error_;
|
||||
BLECharacteristic *rpc_;
|
||||
BLECharacteristic *rpc_response_;
|
||||
BLECharacteristic *capabilities_;
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
binary_sensor::BinarySensor *authorizer_{nullptr};
|
||||
@@ -108,9 +108,6 @@ class ESP32ImprovComponent : public Component {
|
||||
void process_incoming_data_();
|
||||
void on_wifi_connect_timeout_();
|
||||
bool check_identify_();
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
|
||||
const char *state_to_string_(improv::State state);
|
||||
#endif
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
@@ -16,8 +16,7 @@ from esphome.const import (
|
||||
CONF_SAFE_MODE,
|
||||
CONF_VERSION,
|
||||
)
|
||||
from esphome.core import coroutine_with_priority
|
||||
from esphome.coroutine import CoroPriority
|
||||
from esphome.core import CoroPriority, coroutine_with_priority
|
||||
import esphome.final_validate as fv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -122,7 +121,7 @@ CONFIG_SCHEMA = (
|
||||
FINAL_VALIDATE_SCHEMA = ota_esphome_final_validate
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.OTA_UPDATES)
|
||||
@coroutine_with_priority(CoroPriority.COMMUNICATION)
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_port(config[CONF_PORT]))
|
||||
|
@@ -2,11 +2,11 @@
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_OTA
|
||||
#include "esphome/components/ota/ota_backend.h"
|
||||
#include "esphome/components/socket/socket.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/components/ota/ota_backend.h"
|
||||
#include "esphome/components/socket/socket.h"
|
||||
|
||||
namespace esphome {
|
||||
|
||||
|
@@ -77,13 +77,6 @@ ETHERNET_TYPES = {
|
||||
"DM9051": EthernetType.ETHERNET_TYPE_DM9051,
|
||||
}
|
||||
|
||||
# PHY types that need compile-time defines for conditional compilation
|
||||
_PHY_TYPE_TO_DEFINE = {
|
||||
"KSZ8081": "USE_ETHERNET_KSZ8081",
|
||||
"KSZ8081RNA": "USE_ETHERNET_KSZ8081",
|
||||
# Add other PHY types here only if they need conditional compilation
|
||||
}
|
||||
|
||||
SPI_ETHERNET_TYPES = ["W5500", "DM9051"]
|
||||
SPI_ETHERNET_DEFAULT_POLLING_INTERVAL = TimePeriodMilliseconds(milliseconds=10)
|
||||
|
||||
@@ -352,10 +345,6 @@ async def to_code(config):
|
||||
if CONF_MANUAL_IP in config:
|
||||
cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP])))
|
||||
|
||||
# Add compile-time define for PHY types with specific code
|
||||
if phy_define := _PHY_TYPE_TO_DEFINE.get(config[CONF_TYPE]):
|
||||
cg.add_define(phy_define)
|
||||
|
||||
cg.add_define("USE_ETHERNET")
|
||||
|
||||
# Disable WiFi when using Ethernet to save memory
|
||||
|
@@ -229,12 +229,10 @@ void EthernetComponent::setup() {
|
||||
ESPHL_ERROR_CHECK(err, "ETH driver install error");
|
||||
|
||||
#ifndef USE_ETHERNET_SPI
|
||||
#ifdef USE_ETHERNET_KSZ8081
|
||||
if (this->type_ == ETHERNET_TYPE_KSZ8081RNA && this->clk_mode_ == EMAC_CLK_OUT) {
|
||||
// KSZ8081RNA default is incorrect. It expects a 25MHz clock instead of the 50MHz we provide.
|
||||
this->ksz8081_set_clock_reference_(mac);
|
||||
}
|
||||
#endif // USE_ETHERNET_KSZ8081
|
||||
|
||||
for (const auto &phy_register : this->phy_registers_) {
|
||||
this->write_phy_register_(mac, phy_register);
|
||||
@@ -302,7 +300,6 @@ void EthernetComponent::loop() {
|
||||
this->state_ = EthernetComponentState::CONNECTING;
|
||||
this->start_connect_();
|
||||
} else {
|
||||
this->finish_connect_();
|
||||
// When connected and stable, disable the loop to save CPU cycles
|
||||
this->disable_loop();
|
||||
}
|
||||
@@ -489,35 +486,10 @@ void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_
|
||||
}
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
|
||||
void EthernetComponent::finish_connect_() {
|
||||
#if USE_NETWORK_IPV6
|
||||
// Retry IPv6 link-local setup if it failed during initial connect
|
||||
// This handles the case where min_ipv6_addr_count is NOT set (or is 0),
|
||||
// allowing us to reach CONNECTED state with just IPv4.
|
||||
// If IPv6 setup failed in start_connect_() because the interface wasn't ready:
|
||||
// - Bootup timing issues (#10281)
|
||||
// - Cable unplugged/network interruption (#10705)
|
||||
// We can now retry since we're in CONNECTED state and the interface is definitely up.
|
||||
if (!this->ipv6_setup_done_) {
|
||||
esp_err_t err = esp_netif_create_ip6_linklocal(this->eth_netif_);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGD(TAG, "IPv6 link-local address created (retry succeeded)");
|
||||
}
|
||||
// Always set the flag to prevent continuous retries
|
||||
// If IPv6 setup fails here with the interface up and stable, it's
|
||||
// likely a persistent issue (IPv6 disabled at router, hardware
|
||||
// limitation, etc.) that won't be resolved by further retries.
|
||||
// The device continues to work with IPv4.
|
||||
this->ipv6_setup_done_ = true;
|
||||
}
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
}
|
||||
|
||||
void EthernetComponent::start_connect_() {
|
||||
global_eth_component->got_ipv4_address_ = false;
|
||||
#if USE_NETWORK_IPV6
|
||||
global_eth_component->ipv6_count_ = 0;
|
||||
this->ipv6_setup_done_ = false;
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
this->connect_begin_ = millis();
|
||||
this->status_set_warning(LOG_STR("waiting for IP configuration"));
|
||||
@@ -573,27 +545,9 @@ void EthernetComponent::start_connect_() {
|
||||
}
|
||||
}
|
||||
#if USE_NETWORK_IPV6
|
||||
// Attempt to create IPv6 link-local address
|
||||
// We MUST attempt this here, not just in finish_connect_(), because with
|
||||
// min_ipv6_addr_count set, the component won't reach CONNECTED state without IPv6.
|
||||
// However, this may fail with ESP_FAIL if the interface is not up yet:
|
||||
// - At bootup when link isn't ready (#10281)
|
||||
// - After disconnection/cable unplugged (#10705)
|
||||
// We'll retry in finish_connect_() if it fails here.
|
||||
err = esp_netif_create_ip6_linklocal(this->eth_netif_);
|
||||
if (err != ESP_OK) {
|
||||
if (err == ESP_ERR_ESP_NETIF_INVALID_PARAMS) {
|
||||
// This is a programming error, not a transient failure
|
||||
ESPHL_ERROR_CHECK(err, "esp_netif_create_ip6_linklocal invalid parameters");
|
||||
} else {
|
||||
// ESP_FAIL means the interface isn't up yet
|
||||
// This is expected and non-fatal, happens in multiple scenarios:
|
||||
// - During reconnection after network interruptions (#10705)
|
||||
// - At bootup when the link isn't ready yet (#10281)
|
||||
// We'll retry once we reach CONNECTED state and the interface is up
|
||||
ESP_LOGW(TAG, "esp_netif_create_ip6_linklocal failed: %s", esp_err_to_name(err));
|
||||
// Don't mark component as failed - this is a transient error
|
||||
}
|
||||
ESPHL_ERROR_CHECK(err, "Enable IPv6 link local failed");
|
||||
}
|
||||
#endif /* USE_NETWORK_IPV6 */
|
||||
|
||||
@@ -684,9 +638,7 @@ void EthernetComponent::get_eth_mac_address_raw(uint8_t *mac) {
|
||||
std::string EthernetComponent::get_eth_mac_address_pretty() {
|
||||
uint8_t mac[6];
|
||||
get_eth_mac_address_raw(mac);
|
||||
char buf[18];
|
||||
format_mac_addr_upper(mac, buf);
|
||||
return std::string(buf);
|
||||
return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
}
|
||||
|
||||
eth_duplex_t EthernetComponent::get_duplex_mode() {
|
||||
@@ -723,7 +675,6 @@ bool EthernetComponent::powerdown() {
|
||||
|
||||
#ifndef USE_ETHERNET_SPI
|
||||
|
||||
#ifdef USE_ETHERNET_KSZ8081
|
||||
constexpr uint8_t KSZ80XX_PC2R_REG_ADDR = 0x1F;
|
||||
|
||||
void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
|
||||
@@ -752,7 +703,6 @@ void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
|
||||
ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty((u_int8_t *) &phy_control_2, 2).c_str());
|
||||
}
|
||||
}
|
||||
#endif // USE_ETHERNET_KSZ8081
|
||||
|
||||
void EthernetComponent::write_phy_register_(esp_eth_mac_t *mac, PHYRegister register_data) {
|
||||
esp_err_t err;
|
||||
|
@@ -102,12 +102,9 @@ class EthernetComponent : public Component {
|
||||
#endif /* LWIP_IPV6 */
|
||||
|
||||
void start_connect_();
|
||||
void finish_connect_();
|
||||
void dump_connect_params_();
|
||||
#ifdef USE_ETHERNET_KSZ8081
|
||||
/// @brief Set `RMII Reference Clock Select` bit for KSZ8081.
|
||||
void ksz8081_set_clock_reference_(esp_eth_mac_t *mac);
|
||||
#endif
|
||||
/// @brief Set arbitratry PHY registers from config.
|
||||
void write_phy_register_(esp_eth_mac_t *mac, PHYRegister register_data);
|
||||
|
||||
@@ -147,7 +144,6 @@ class EthernetComponent : public Component {
|
||||
bool got_ipv4_address_{false};
|
||||
#if LWIP_IPV6
|
||||
uint8_t ipv6_count_{0};
|
||||
bool ipv6_setup_done_{false};
|
||||
#endif /* LWIP_IPV6 */
|
||||
|
||||
// Pointers at the end (naturally aligned)
|
||||
|
@@ -1,13 +1,7 @@
|
||||
#include "factory_reset_button.h"
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_OPENTHREAD
|
||||
#include "esphome/components/openthread/openthread.h"
|
||||
#endif
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace factory_reset {
|
||||
@@ -19,20 +13,9 @@ void FactoryResetButton::press_action() {
|
||||
ESP_LOGI(TAG, "Resetting");
|
||||
// Let MQTT settle a bit
|
||||
delay(100); // NOLINT
|
||||
#ifdef USE_OPENTHREAD
|
||||
openthread::global_openthread_component->on_factory_reset(FactoryResetButton::factory_reset_callback);
|
||||
#else
|
||||
global_preferences->reset();
|
||||
App.safe_reboot();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef USE_OPENTHREAD
|
||||
void FactoryResetButton::factory_reset_callback() {
|
||||
global_preferences->reset();
|
||||
App.safe_reboot();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace factory_reset
|
||||
} // namespace esphome
|
||||
|
@@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#include "esphome/components/button/button.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/button/button.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace factory_reset {
|
||||
@@ -11,9 +9,6 @@ namespace factory_reset {
|
||||
class FactoryResetButton : public button::Button, public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
#ifdef USE_OPENTHREAD
|
||||
static void factory_reset_callback();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void press_action() override;
|
||||
|
@@ -1,13 +1,7 @@
|
||||
#include "factory_reset_switch.h"
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_OPENTHREAD
|
||||
#include "esphome/components/openthread/openthread.h"
|
||||
#endif
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace factory_reset {
|
||||
@@ -23,21 +17,10 @@ void FactoryResetSwitch::write_state(bool state) {
|
||||
ESP_LOGI(TAG, "Resetting");
|
||||
// Let MQTT settle a bit
|
||||
delay(100); // NOLINT
|
||||
#ifdef USE_OPENTHREAD
|
||||
openthread::global_openthread_component->on_factory_reset(FactoryResetSwitch::factory_reset_callback);
|
||||
#else
|
||||
global_preferences->reset();
|
||||
App.safe_reboot();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_OPENTHREAD
|
||||
void FactoryResetSwitch::factory_reset_callback() {
|
||||
global_preferences->reset();
|
||||
App.safe_reboot();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace factory_reset
|
||||
} // namespace esphome
|
||||
|
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace factory_reset {
|
||||
@@ -10,9 +9,6 @@ namespace factory_reset {
|
||||
class FactoryResetSwitch : public switch_::Switch, public Component {
|
||||
public:
|
||||
void dump_config() override;
|
||||
#ifdef USE_OPENTHREAD
|
||||
static void factory_reset_callback();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
|
@@ -6,7 +6,6 @@ namespace gpio {
|
||||
|
||||
static const char *const TAG = "gpio.binary_sensor";
|
||||
|
||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
|
||||
static const LogString *interrupt_type_to_string(gpio::InterruptType type) {
|
||||
switch (type) {
|
||||
case gpio::INTERRUPT_RISING_EDGE:
|
||||
@@ -23,7 +22,6 @@ static const LogString *interrupt_type_to_string(gpio::InterruptType type) {
|
||||
static const LogString *gpio_mode_to_string(bool use_interrupt) {
|
||||
return use_interrupt ? LOG_STR("interrupt") : LOG_STR("polling");
|
||||
}
|
||||
#endif
|
||||
|
||||
void IRAM_ATTR GPIOBinarySensorStore::gpio_intr(GPIOBinarySensorStore *arg) {
|
||||
bool new_state = arg->isr_pin_.digital_read();
|
||||
|
@@ -4,7 +4,6 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome::gpio_expander {
|
||||
@@ -12,27 +11,18 @@ namespace esphome::gpio_expander {
|
||||
/// @brief A class to cache the read state of a GPIO expander.
|
||||
/// This class caches reads between GPIO Pins which are on the same bank.
|
||||
/// This means that for reading whole Port (ex. 8 pins) component needs only one
|
||||
/// I2C/SPI read per main loop call. It assumes that one bit in byte identifies one GPIO pin.
|
||||
///
|
||||
/// I2C/SPI read per main loop call. It assumes, that one bit in byte identifies one GPIO pin
|
||||
/// Template parameters:
|
||||
/// T - Type which represents internal bank register. Could be uint8_t or uint16_t.
|
||||
/// Choose based on how your I/O expander reads pins:
|
||||
/// * uint8_t: For chips that read banks separately (8 pins at a time)
|
||||
/// Examples: MCP23017 (2x8-bit banks), TCA9555 (2x8-bit banks)
|
||||
/// * uint16_t: For chips that read all pins at once (up to 16 pins)
|
||||
/// Examples: PCF8574/8575 (8/16 pins), PCA9554/9555 (8/16 pins)
|
||||
/// N - Total number of pins (maximum 65535)
|
||||
/// P - Type for pin number parameters (automatically selected based on N:
|
||||
/// uint8_t for N<=256, uint16_t for N>256). Can be explicitly specified
|
||||
/// if needed (e.g., for components like SN74HC165 with >256 pins)
|
||||
template<typename T, uint16_t N, typename P = typename std::conditional<(N > 256), uint16_t, uint8_t>::type>
|
||||
class CachedGpioExpander {
|
||||
/// T - Type which represents internal register. Could be uint8_t or uint16_t. Adjust to
|
||||
/// match size of your internal GPIO bank register.
|
||||
/// N - Number of pins
|
||||
template<typename T, T N> class CachedGpioExpander {
|
||||
public:
|
||||
/// @brief Read the state of the given pin. This will invalidate the cache for the given pin number.
|
||||
/// @param pin Pin number to read
|
||||
/// @return Pin state
|
||||
bool digital_read(P pin) {
|
||||
const P bank = pin / BANK_SIZE;
|
||||
bool digital_read(T pin) {
|
||||
const uint8_t bank = pin / BANK_SIZE;
|
||||
const T pin_mask = (1 << (pin % BANK_SIZE));
|
||||
// Check if specific pin cache is valid
|
||||
if (this->read_cache_valid_[bank] & pin_mask) {
|
||||
@@ -48,31 +38,21 @@ class CachedGpioExpander {
|
||||
return this->digital_read_cache(pin);
|
||||
}
|
||||
|
||||
void digital_write(P pin, bool value) { this->digital_write_hw(pin, value); }
|
||||
void digital_write(T pin, bool value) { this->digital_write_hw(pin, value); }
|
||||
|
||||
protected:
|
||||
/// @brief Read GPIO bank from hardware into internal state
|
||||
/// @param pin Pin number (used to determine which bank to read)
|
||||
/// @return true if read succeeded, false on communication error
|
||||
/// @note This does NOT return the pin state. It returns whether the read operation succeeded.
|
||||
/// The actual pin state should be returned by digital_read_cache().
|
||||
virtual bool digital_read_hw(P pin) = 0;
|
||||
|
||||
/// @brief Get cached pin value from internal state
|
||||
/// @param pin Pin number to read
|
||||
/// @return Pin state (true = HIGH, false = LOW)
|
||||
virtual bool digital_read_cache(P pin) = 0;
|
||||
|
||||
/// @brief Write GPIO state to hardware
|
||||
/// @param pin Pin number to write
|
||||
/// @param value Pin state to write (true = HIGH, false = LOW)
|
||||
virtual void digital_write_hw(P pin, bool value) = 0;
|
||||
/// @brief Call component low level function to read GPIO state from device
|
||||
virtual bool digital_read_hw(T pin) = 0;
|
||||
/// @brief Call component read function from internal cache.
|
||||
virtual bool digital_read_cache(T pin) = 0;
|
||||
/// @brief Call component low level function to write GPIO state to device
|
||||
virtual void digital_write_hw(T pin, bool value) = 0;
|
||||
|
||||
/// @brief Invalidate cache. This function should be called in component loop().
|
||||
void reset_pin_cache_() { memset(this->read_cache_valid_, 0x00, CACHE_SIZE_BYTES); }
|
||||
|
||||
static constexpr uint16_t BITS_PER_BYTE = 8;
|
||||
static constexpr uint16_t BANK_SIZE = sizeof(T) * BITS_PER_BYTE;
|
||||
static constexpr uint8_t BITS_PER_BYTE = 8;
|
||||
static constexpr uint8_t BANK_SIZE = sizeof(T) * BITS_PER_BYTE;
|
||||
static constexpr size_t BANKS = N / BANK_SIZE;
|
||||
static constexpr size_t CACHE_SIZE_BYTES = BANKS * sizeof(T);
|
||||
|
||||
|
@@ -57,7 +57,7 @@ void GroveGasMultichannelV2Component::update() {
|
||||
void GroveGasMultichannelV2Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Grove Multichannel Gas Sensor V2");
|
||||
LOG_I2C_DEVICE(this)
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_UPDATE_INTERVAL(this)
|
||||
LOG_SENSOR(" ", "Nitrogen Dioxide", this->nitrogen_dioxide_sensor_);
|
||||
LOG_SENSOR(" ", "Ethanol", this->ethanol_sensor_);
|
||||
LOG_SENSOR(" ", "Carbon Monoxide", this->carbon_monoxide_sensor_);
|
||||
|
@@ -128,4 +128,4 @@ async def to_code(config):
|
||||
|
||||
cg.add_library("tonia/HeatpumpIR", "1.0.37")
|
||||
if CORE.is_libretiny or CORE.is_esp32:
|
||||
CORE.add_platformio_option("lib_ignore", ["IRremoteESP8266"])
|
||||
CORE.add_platformio_option("lib_ignore", "IRremoteESP8266")
|
||||
|
@@ -42,7 +42,7 @@ void HLW8012Component::dump_config() {
|
||||
" Current resistor: %.1f mΩ\n"
|
||||
" Voltage Divider: %.1f",
|
||||
this->change_mode_every_, this->current_resistor_ * 1000.0f, this->voltage_divider_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_UPDATE_INTERVAL(this)
|
||||
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
|
||||
LOG_SENSOR(" ", "Current", this->current_sensor_);
|
||||
LOG_SENSOR(" ", "Power", this->power_sensor_);
|
||||
|
@@ -3,8 +3,7 @@ import esphome.codegen as cg
|
||||
from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_PASSWORD, CONF_URL, CONF_USERNAME
|
||||
from esphome.core import coroutine_with_priority
|
||||
from esphome.coroutine import CoroPriority
|
||||
from esphome.core import CoroPriority, coroutine_with_priority
|
||||
|
||||
from .. import CONF_HTTP_REQUEST_ID, HttpRequestComponent, http_request_ns
|
||||
|
||||
@@ -41,7 +40,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.OTA_UPDATES)
|
||||
@coroutine_with_priority(CoroPriority.COMMUNICATION)
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await ota_to_code(var, config)
|
||||
|
@@ -4,9 +4,6 @@ from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant
|
||||
from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32,
|
||||
VARIANT_ESP32C3,
|
||||
VARIANT_ESP32C5,
|
||||
VARIANT_ESP32C6,
|
||||
VARIANT_ESP32H2,
|
||||
VARIANT_ESP32P4,
|
||||
VARIANT_ESP32S2,
|
||||
VARIANT_ESP32S3,
|
||||
@@ -65,15 +62,12 @@ I2S_ROLE_OPTIONS = {
|
||||
CONF_SECONDARY: i2s_role_t.I2S_ROLE_SLAVE, # NOLINT
|
||||
}
|
||||
|
||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/{variant}/include/soc/soc_caps.h (SOC_I2S_NUM)
|
||||
# https://github.com/espressif/esp-idf/blob/master/components/soc/{variant}/include/soc/soc_caps.h
|
||||
I2S_PORTS = {
|
||||
VARIANT_ESP32: 2,
|
||||
VARIANT_ESP32S2: 1,
|
||||
VARIANT_ESP32S3: 2,
|
||||
VARIANT_ESP32C3: 1,
|
||||
VARIANT_ESP32C5: 1,
|
||||
VARIANT_ESP32C6: 1,
|
||||
VARIANT_ESP32H2: 1,
|
||||
VARIANT_ESP32P4: 3,
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,6 @@ from esphome.const import (
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
UNIT_AMPERE,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_VOLT,
|
||||
@@ -163,7 +162,7 @@ INA2XX_SCHEMA = cv.Schema(
|
||||
unit_of_measurement=UNIT_WATT_HOURS,
|
||||
accuracy_decimals=8,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
key=CONF_NAME,
|
||||
),
|
||||
@@ -171,8 +170,7 @@ INA2XX_SCHEMA = cv.Schema(
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_JOULE,
|
||||
accuracy_decimals=8,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
key=CONF_NAME,
|
||||
),
|
||||
|
@@ -1 +0,0 @@
|
||||
CODEOWNERS = ["@jesserockz", "@JosipKuci"]
|
@@ -1,105 +0,0 @@
|
||||
WAVEFORMS = {
|
||||
"inkplate_6": (
|
||||
(0, 1, 1, 0, 0, 1, 1, 0, 0),
|
||||
(0, 1, 2, 1, 1, 2, 1, 0, 0),
|
||||
(1, 1, 1, 2, 2, 1, 0, 0, 0),
|
||||
(0, 0, 0, 1, 1, 1, 2, 0, 0),
|
||||
(2, 1, 1, 1, 2, 1, 2, 0, 0),
|
||||
(2, 2, 1, 1, 2, 1, 2, 0, 0),
|
||||
(1, 1, 1, 2, 1, 2, 2, 0, 0),
|
||||
(0, 0, 0, 0, 0, 0, 2, 0, 0),
|
||||
),
|
||||
"inkplate_10": (
|
||||
(0, 0, 0, 0, 0, 0, 0, 1, 0),
|
||||
(0, 0, 0, 2, 2, 2, 1, 1, 0),
|
||||
(0, 0, 2, 1, 1, 2, 2, 1, 0),
|
||||
(0, 1, 2, 2, 1, 2, 2, 1, 0),
|
||||
(0, 0, 2, 1, 2, 2, 2, 1, 0),
|
||||
(0, 2, 2, 2, 2, 2, 2, 1, 0),
|
||||
(0, 0, 0, 0, 0, 2, 1, 2, 0),
|
||||
(0, 0, 0, 2, 2, 2, 2, 2, 0),
|
||||
),
|
||||
"inkplate_6_plus": (
|
||||
(0, 0, 0, 0, 0, 2, 1, 1, 0),
|
||||
(0, 0, 2, 1, 1, 1, 2, 1, 0),
|
||||
(0, 2, 2, 2, 1, 1, 2, 1, 0),
|
||||
(0, 0, 2, 2, 2, 1, 2, 1, 0),
|
||||
(0, 0, 0, 0, 2, 2, 2, 1, 0),
|
||||
(0, 0, 2, 1, 2, 1, 1, 2, 0),
|
||||
(0, 0, 2, 2, 2, 1, 1, 2, 0),
|
||||
(0, 0, 0, 0, 2, 2, 2, 2, 0),
|
||||
),
|
||||
"inkplate_6_v2": (
|
||||
(1, 0, 1, 0, 1, 1, 1, 0, 0),
|
||||
(0, 0, 0, 1, 1, 1, 1, 0, 0),
|
||||
(1, 1, 1, 1, 0, 2, 1, 0, 0),
|
||||
(1, 1, 1, 2, 2, 1, 1, 0, 0),
|
||||
(1, 1, 1, 1, 2, 2, 1, 0, 0),
|
||||
(0, 1, 1, 1, 2, 2, 1, 0, 0),
|
||||
(0, 0, 0, 0, 1, 1, 2, 0, 0),
|
||||
(0, 0, 0, 0, 0, 1, 2, 0, 0),
|
||||
),
|
||||
"inkplate_5": (
|
||||
(0, 0, 1, 1, 0, 1, 1, 1, 0),
|
||||
(0, 1, 1, 1, 1, 2, 0, 1, 0),
|
||||
(1, 2, 2, 0, 2, 1, 1, 1, 0),
|
||||
(1, 1, 1, 2, 0, 1, 1, 2, 0),
|
||||
(0, 1, 1, 1, 2, 0, 1, 2, 0),
|
||||
(0, 0, 0, 1, 1, 2, 1, 2, 0),
|
||||
(1, 1, 1, 2, 0, 2, 1, 2, 0),
|
||||
(0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
),
|
||||
"inkplate_5_v2": (
|
||||
(0, 0, 1, 1, 2, 1, 1, 1, 0),
|
||||
(1, 1, 2, 2, 1, 2, 1, 1, 0),
|
||||
(0, 1, 2, 2, 1, 1, 2, 1, 0),
|
||||
(0, 0, 1, 1, 1, 1, 1, 2, 0),
|
||||
(1, 2, 1, 2, 1, 1, 1, 2, 0),
|
||||
(0, 1, 1, 1, 2, 0, 1, 2, 0),
|
||||
(1, 1, 1, 2, 2, 2, 1, 2, 0),
|
||||
(0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
),
|
||||
}
|
||||
|
||||
INKPLATE_10_CUSTOM_WAVEFORMS = (
|
||||
(
|
||||
(0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
(0, 0, 0, 2, 1, 2, 1, 1, 0),
|
||||
(0, 0, 0, 2, 2, 1, 2, 1, 0),
|
||||
(0, 0, 2, 2, 1, 2, 2, 1, 0),
|
||||
(0, 0, 0, 2, 1, 1, 1, 2, 0),
|
||||
(0, 0, 2, 2, 2, 1, 1, 2, 0),
|
||||
(0, 0, 0, 0, 0, 1, 2, 2, 0),
|
||||
(0, 0, 0, 0, 2, 2, 2, 2, 0),
|
||||
),
|
||||
(
|
||||
(0, 3, 3, 3, 3, 3, 3, 3, 0),
|
||||
(0, 1, 2, 1, 1, 2, 2, 1, 0),
|
||||
(0, 2, 2, 2, 1, 2, 2, 1, 0),
|
||||
(0, 0, 2, 2, 2, 2, 2, 1, 0),
|
||||
(0, 3, 3, 2, 1, 1, 1, 2, 0),
|
||||
(0, 3, 3, 2, 2, 1, 1, 2, 0),
|
||||
(0, 2, 1, 2, 1, 2, 1, 2, 0),
|
||||
(0, 3, 3, 3, 2, 2, 2, 2, 0),
|
||||
),
|
||||
(
|
||||
(0, 0, 0, 0, 0, 0, 0, 1, 0),
|
||||
(0, 0, 0, 2, 2, 2, 1, 1, 0),
|
||||
(0, 0, 2, 1, 1, 2, 2, 1, 0),
|
||||
(1, 1, 2, 2, 1, 2, 2, 1, 0),
|
||||
(0, 0, 2, 1, 2, 2, 2, 1, 0),
|
||||
(0, 1, 2, 2, 2, 2, 2, 1, 0),
|
||||
(0, 0, 0, 2, 2, 2, 1, 2, 0),
|
||||
(0, 0, 0, 2, 2, 2, 2, 2, 0),
|
||||
),
|
||||
(
|
||||
(0, 0, 0, 0, 0, 0, 0, 1, 0),
|
||||
(0, 0, 0, 2, 2, 2, 1, 1, 0),
|
||||
(2, 2, 2, 1, 0, 2, 1, 0, 0),
|
||||
(2, 1, 1, 2, 1, 1, 1, 2, 0),
|
||||
(2, 2, 2, 1, 1, 1, 0, 2, 0),
|
||||
(2, 2, 2, 1, 1, 2, 1, 2, 0),
|
||||
(0, 0, 0, 0, 2, 1, 2, 2, 0),
|
||||
(0, 0, 0, 0, 2, 2, 2, 2, 0),
|
||||
),
|
||||
)
|
@@ -1,238 +0,0 @@
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import display, i2c
|
||||
from esphome.components.esp32 import CONF_CPU_FREQUENCY
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_FULL_UPDATE_EVERY,
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
CONF_MIRROR_X,
|
||||
CONF_MIRROR_Y,
|
||||
CONF_MODEL,
|
||||
CONF_OE_PIN,
|
||||
CONF_PAGES,
|
||||
CONF_TRANSFORM,
|
||||
CONF_WAKEUP_PIN,
|
||||
PLATFORM_ESP32,
|
||||
)
|
||||
import esphome.final_validate as fv
|
||||
|
||||
from .const import INKPLATE_10_CUSTOM_WAVEFORMS, WAVEFORMS
|
||||
|
||||
DEPENDENCIES = ["i2c", "esp32"]
|
||||
AUTO_LOAD = ["psram"]
|
||||
|
||||
CONF_DISPLAY_DATA_0_PIN = "display_data_0_pin"
|
||||
CONF_DISPLAY_DATA_1_PIN = "display_data_1_pin"
|
||||
CONF_DISPLAY_DATA_2_PIN = "display_data_2_pin"
|
||||
CONF_DISPLAY_DATA_3_PIN = "display_data_3_pin"
|
||||
CONF_DISPLAY_DATA_4_PIN = "display_data_4_pin"
|
||||
CONF_DISPLAY_DATA_5_PIN = "display_data_5_pin"
|
||||
CONF_DISPLAY_DATA_6_PIN = "display_data_6_pin"
|
||||
CONF_DISPLAY_DATA_7_PIN = "display_data_7_pin"
|
||||
|
||||
CONF_CL_PIN = "cl_pin"
|
||||
CONF_CKV_PIN = "ckv_pin"
|
||||
CONF_GREYSCALE = "greyscale"
|
||||
CONF_GMOD_PIN = "gmod_pin"
|
||||
CONF_GPIO0_ENABLE_PIN = "gpio0_enable_pin"
|
||||
CONF_LE_PIN = "le_pin"
|
||||
CONF_PARTIAL_UPDATING = "partial_updating"
|
||||
CONF_POWERUP_PIN = "powerup_pin"
|
||||
CONF_SPH_PIN = "sph_pin"
|
||||
CONF_SPV_PIN = "spv_pin"
|
||||
CONF_VCOM_PIN = "vcom_pin"
|
||||
|
||||
inkplate_ns = cg.esphome_ns.namespace("inkplate")
|
||||
Inkplate = inkplate_ns.class_(
|
||||
"Inkplate",
|
||||
cg.PollingComponent,
|
||||
i2c.I2CDevice,
|
||||
display.Display,
|
||||
display.DisplayBuffer,
|
||||
)
|
||||
|
||||
InkplateModel = inkplate_ns.enum("InkplateModel")
|
||||
|
||||
MODELS = {
|
||||
"inkplate_6": InkplateModel.INKPLATE_6,
|
||||
"inkplate_10": InkplateModel.INKPLATE_10,
|
||||
"inkplate_6_plus": InkplateModel.INKPLATE_6_PLUS,
|
||||
"inkplate_6_v2": InkplateModel.INKPLATE_6_V2,
|
||||
"inkplate_5": InkplateModel.INKPLATE_5,
|
||||
"inkplate_5_v2": InkplateModel.INKPLATE_5_V2,
|
||||
}
|
||||
|
||||
CONF_CUSTOM_WAVEFORM = "custom_waveform"
|
||||
|
||||
|
||||
def _validate_custom_waveform(config):
|
||||
if CONF_CUSTOM_WAVEFORM in config and config[CONF_MODEL] != "inkplate_10":
|
||||
raise cv.Invalid("Custom waveforms are only supported on the Inkplate 10")
|
||||
return config
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
display.FULL_DISPLAY_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Inkplate),
|
||||
cv.Optional(CONF_GREYSCALE, default=False): cv.boolean,
|
||||
cv.Optional(CONF_CUSTOM_WAVEFORM): cv.All(
|
||||
cv.uint8_t, cv.Range(min=1, max=len(INKPLATE_10_CUSTOM_WAVEFORMS))
|
||||
),
|
||||
cv.Optional(CONF_TRANSFORM): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_MIRROR_X, default=False): cv.boolean,
|
||||
cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_PARTIAL_UPDATING, default=True): cv.boolean,
|
||||
cv.Optional(CONF_FULL_UPDATE_EVERY, default=10): cv.uint32_t,
|
||||
cv.Optional(CONF_MODEL, default="inkplate_6"): cv.enum(
|
||||
MODELS, lower=True, space="_"
|
||||
),
|
||||
# Control pins
|
||||
cv.Required(CONF_CKV_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_GMOD_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_GPIO0_ENABLE_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_OE_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_POWERUP_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_SPH_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_SPV_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_VCOM_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_WAKEUP_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_CL_PIN, default=0): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(CONF_LE_PIN, default=2): pins.internal_gpio_output_pin_schema,
|
||||
# Data pins
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_0_PIN, default=4
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_1_PIN, default=5
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_2_PIN, default=18
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_3_PIN, default=19
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_4_PIN, default=23
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_5_PIN, default=25
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_6_PIN, default=26
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_7_PIN, default=27
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("5s"))
|
||||
.extend(i2c.i2c_device_schema(0x48)),
|
||||
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
|
||||
_validate_custom_waveform,
|
||||
)
|
||||
|
||||
|
||||
def _validate_cpu_frequency(config):
|
||||
esp32_config = fv.full_config.get()[PLATFORM_ESP32]
|
||||
if esp32_config[CONF_CPU_FREQUENCY] != "240MHZ":
|
||||
raise cv.Invalid(
|
||||
"Inkplate requires 240MHz CPU frequency (set in esp32 component)"
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _validate_cpu_frequency
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
|
||||
await display.register_display(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [(display.DisplayRef, "it")], return_type=cg.void
|
||||
)
|
||||
cg.add(var.set_writer(lambda_))
|
||||
|
||||
cg.add(var.set_greyscale(config[CONF_GREYSCALE]))
|
||||
if transform := config.get(CONF_TRANSFORM):
|
||||
cg.add(var.set_mirror_x(transform[CONF_MIRROR_X]))
|
||||
cg.add(var.set_mirror_y(transform[CONF_MIRROR_Y]))
|
||||
cg.add(var.set_partial_updating(config[CONF_PARTIAL_UPDATING]))
|
||||
cg.add(var.set_full_update_every(config[CONF_FULL_UPDATE_EVERY]))
|
||||
|
||||
cg.add(var.set_model(config[CONF_MODEL]))
|
||||
|
||||
if custom_waveform := config.get(CONF_CUSTOM_WAVEFORM):
|
||||
waveform = INKPLATE_10_CUSTOM_WAVEFORMS[custom_waveform - 1]
|
||||
waveform = [element for tupl in waveform for element in tupl]
|
||||
cg.add(var.set_waveform(waveform, True))
|
||||
else:
|
||||
waveform = WAVEFORMS[config[CONF_MODEL]]
|
||||
waveform = [element for tupl in waveform for element in tupl]
|
||||
cg.add(var.set_waveform(waveform, False))
|
||||
|
||||
ckv = await cg.gpio_pin_expression(config[CONF_CKV_PIN])
|
||||
cg.add(var.set_ckv_pin(ckv))
|
||||
|
||||
gmod = await cg.gpio_pin_expression(config[CONF_GMOD_PIN])
|
||||
cg.add(var.set_gmod_pin(gmod))
|
||||
|
||||
gpio0_enable = await cg.gpio_pin_expression(config[CONF_GPIO0_ENABLE_PIN])
|
||||
cg.add(var.set_gpio0_enable_pin(gpio0_enable))
|
||||
|
||||
oe = await cg.gpio_pin_expression(config[CONF_OE_PIN])
|
||||
cg.add(var.set_oe_pin(oe))
|
||||
|
||||
powerup = await cg.gpio_pin_expression(config[CONF_POWERUP_PIN])
|
||||
cg.add(var.set_powerup_pin(powerup))
|
||||
|
||||
sph = await cg.gpio_pin_expression(config[CONF_SPH_PIN])
|
||||
cg.add(var.set_sph_pin(sph))
|
||||
|
||||
spv = await cg.gpio_pin_expression(config[CONF_SPV_PIN])
|
||||
cg.add(var.set_spv_pin(spv))
|
||||
|
||||
vcom = await cg.gpio_pin_expression(config[CONF_VCOM_PIN])
|
||||
cg.add(var.set_vcom_pin(vcom))
|
||||
|
||||
wakeup = await cg.gpio_pin_expression(config[CONF_WAKEUP_PIN])
|
||||
cg.add(var.set_wakeup_pin(wakeup))
|
||||
|
||||
cl = await cg.gpio_pin_expression(config[CONF_CL_PIN])
|
||||
cg.add(var.set_cl_pin(cl))
|
||||
|
||||
le = await cg.gpio_pin_expression(config[CONF_LE_PIN])
|
||||
cg.add(var.set_le_pin(le))
|
||||
|
||||
display_data_0 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_0_PIN])
|
||||
cg.add(var.set_display_data_0_pin(display_data_0))
|
||||
|
||||
display_data_1 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_1_PIN])
|
||||
cg.add(var.set_display_data_1_pin(display_data_1))
|
||||
|
||||
display_data_2 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_2_PIN])
|
||||
cg.add(var.set_display_data_2_pin(display_data_2))
|
||||
|
||||
display_data_3 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_3_PIN])
|
||||
cg.add(var.set_display_data_3_pin(display_data_3))
|
||||
|
||||
display_data_4 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_4_PIN])
|
||||
cg.add(var.set_display_data_4_pin(display_data_4))
|
||||
|
||||
display_data_5 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_5_PIN])
|
||||
cg.add(var.set_display_data_5_pin(display_data_5))
|
||||
|
||||
display_data_6 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_6_PIN])
|
||||
cg.add(var.set_display_data_6_pin(display_data_6))
|
||||
|
||||
display_data_7 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_7_PIN])
|
||||
cg.add(var.set_display_data_7_pin(display_data_7))
|
@@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
|
@@ -1,5 +1,214 @@
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import display, i2c
|
||||
from esphome.components.esp32 import CONF_CPU_FREQUENCY
|
||||
import esphome.config_validation as cv
|
||||
|
||||
CONFIG_SCHEMA = cv.invalid(
|
||||
"The inkplate6 display component has been renamed to inkplate."
|
||||
from esphome.const import (
|
||||
CONF_FULL_UPDATE_EVERY,
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
CONF_MIRROR_X,
|
||||
CONF_MIRROR_Y,
|
||||
CONF_MODEL,
|
||||
CONF_OE_PIN,
|
||||
CONF_PAGES,
|
||||
CONF_TRANSFORM,
|
||||
CONF_WAKEUP_PIN,
|
||||
PLATFORM_ESP32,
|
||||
)
|
||||
import esphome.final_validate as fv
|
||||
|
||||
DEPENDENCIES = ["i2c", "esp32"]
|
||||
AUTO_LOAD = ["psram"]
|
||||
|
||||
CONF_DISPLAY_DATA_0_PIN = "display_data_0_pin"
|
||||
CONF_DISPLAY_DATA_1_PIN = "display_data_1_pin"
|
||||
CONF_DISPLAY_DATA_2_PIN = "display_data_2_pin"
|
||||
CONF_DISPLAY_DATA_3_PIN = "display_data_3_pin"
|
||||
CONF_DISPLAY_DATA_4_PIN = "display_data_4_pin"
|
||||
CONF_DISPLAY_DATA_5_PIN = "display_data_5_pin"
|
||||
CONF_DISPLAY_DATA_6_PIN = "display_data_6_pin"
|
||||
CONF_DISPLAY_DATA_7_PIN = "display_data_7_pin"
|
||||
|
||||
CONF_CL_PIN = "cl_pin"
|
||||
CONF_CKV_PIN = "ckv_pin"
|
||||
CONF_GREYSCALE = "greyscale"
|
||||
CONF_GMOD_PIN = "gmod_pin"
|
||||
CONF_GPIO0_ENABLE_PIN = "gpio0_enable_pin"
|
||||
CONF_LE_PIN = "le_pin"
|
||||
CONF_PARTIAL_UPDATING = "partial_updating"
|
||||
CONF_POWERUP_PIN = "powerup_pin"
|
||||
CONF_SPH_PIN = "sph_pin"
|
||||
CONF_SPV_PIN = "spv_pin"
|
||||
CONF_VCOM_PIN = "vcom_pin"
|
||||
|
||||
inkplate6_ns = cg.esphome_ns.namespace("inkplate6")
|
||||
Inkplate6 = inkplate6_ns.class_(
|
||||
"Inkplate6",
|
||||
cg.PollingComponent,
|
||||
i2c.I2CDevice,
|
||||
display.Display,
|
||||
display.DisplayBuffer,
|
||||
)
|
||||
|
||||
InkplateModel = inkplate6_ns.enum("InkplateModel")
|
||||
|
||||
MODELS = {
|
||||
"inkplate_6": InkplateModel.INKPLATE_6,
|
||||
"inkplate_10": InkplateModel.INKPLATE_10,
|
||||
"inkplate_6_plus": InkplateModel.INKPLATE_6_PLUS,
|
||||
"inkplate_6_v2": InkplateModel.INKPLATE_6_V2,
|
||||
"inkplate_5": InkplateModel.INKPLATE_5,
|
||||
"inkplate_5_v2": InkplateModel.INKPLATE_5_V2,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
display.FULL_DISPLAY_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Inkplate6),
|
||||
cv.Optional(CONF_GREYSCALE, default=False): cv.boolean,
|
||||
cv.Optional(CONF_TRANSFORM): cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_MIRROR_X, default=False): cv.boolean,
|
||||
cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_PARTIAL_UPDATING, default=True): cv.boolean,
|
||||
cv.Optional(CONF_FULL_UPDATE_EVERY, default=10): cv.uint32_t,
|
||||
cv.Optional(CONF_MODEL, default="inkplate_6"): cv.enum(
|
||||
MODELS, lower=True, space="_"
|
||||
),
|
||||
# Control pins
|
||||
cv.Required(CONF_CKV_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_GMOD_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_GPIO0_ENABLE_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_OE_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_POWERUP_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_SPH_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_SPV_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_VCOM_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Required(CONF_WAKEUP_PIN): pins.gpio_output_pin_schema,
|
||||
cv.Optional(CONF_CL_PIN, default=0): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(CONF_LE_PIN, default=2): pins.internal_gpio_output_pin_schema,
|
||||
# Data pins
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_0_PIN, default=4
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_1_PIN, default=5
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_2_PIN, default=18
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_3_PIN, default=19
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_4_PIN, default=23
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_5_PIN, default=25
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_6_PIN, default=26
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
cv.Optional(
|
||||
CONF_DISPLAY_DATA_7_PIN, default=27
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("5s"))
|
||||
.extend(i2c.i2c_device_schema(0x48)),
|
||||
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
|
||||
)
|
||||
|
||||
|
||||
def _validate_cpu_frequency(config):
|
||||
esp32_config = fv.full_config.get()[PLATFORM_ESP32]
|
||||
if esp32_config[CONF_CPU_FREQUENCY] != "240MHZ":
|
||||
raise cv.Invalid(
|
||||
"Inkplate requires 240MHz CPU frequency (set in esp32 component)"
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _validate_cpu_frequency
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
|
||||
await display.register_display(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [(display.DisplayRef, "it")], return_type=cg.void
|
||||
)
|
||||
cg.add(var.set_writer(lambda_))
|
||||
|
||||
cg.add(var.set_greyscale(config[CONF_GREYSCALE]))
|
||||
if transform := config.get(CONF_TRANSFORM):
|
||||
cg.add(var.set_mirror_x(transform[CONF_MIRROR_X]))
|
||||
cg.add(var.set_mirror_y(transform[CONF_MIRROR_Y]))
|
||||
cg.add(var.set_partial_updating(config[CONF_PARTIAL_UPDATING]))
|
||||
cg.add(var.set_full_update_every(config[CONF_FULL_UPDATE_EVERY]))
|
||||
|
||||
cg.add(var.set_model(config[CONF_MODEL]))
|
||||
|
||||
ckv = await cg.gpio_pin_expression(config[CONF_CKV_PIN])
|
||||
cg.add(var.set_ckv_pin(ckv))
|
||||
|
||||
gmod = await cg.gpio_pin_expression(config[CONF_GMOD_PIN])
|
||||
cg.add(var.set_gmod_pin(gmod))
|
||||
|
||||
gpio0_enable = await cg.gpio_pin_expression(config[CONF_GPIO0_ENABLE_PIN])
|
||||
cg.add(var.set_gpio0_enable_pin(gpio0_enable))
|
||||
|
||||
oe = await cg.gpio_pin_expression(config[CONF_OE_PIN])
|
||||
cg.add(var.set_oe_pin(oe))
|
||||
|
||||
powerup = await cg.gpio_pin_expression(config[CONF_POWERUP_PIN])
|
||||
cg.add(var.set_powerup_pin(powerup))
|
||||
|
||||
sph = await cg.gpio_pin_expression(config[CONF_SPH_PIN])
|
||||
cg.add(var.set_sph_pin(sph))
|
||||
|
||||
spv = await cg.gpio_pin_expression(config[CONF_SPV_PIN])
|
||||
cg.add(var.set_spv_pin(spv))
|
||||
|
||||
vcom = await cg.gpio_pin_expression(config[CONF_VCOM_PIN])
|
||||
cg.add(var.set_vcom_pin(vcom))
|
||||
|
||||
wakeup = await cg.gpio_pin_expression(config[CONF_WAKEUP_PIN])
|
||||
cg.add(var.set_wakeup_pin(wakeup))
|
||||
|
||||
cl = await cg.gpio_pin_expression(config[CONF_CL_PIN])
|
||||
cg.add(var.set_cl_pin(cl))
|
||||
|
||||
le = await cg.gpio_pin_expression(config[CONF_LE_PIN])
|
||||
cg.add(var.set_le_pin(le))
|
||||
|
||||
display_data_0 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_0_PIN])
|
||||
cg.add(var.set_display_data_0_pin(display_data_0))
|
||||
|
||||
display_data_1 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_1_PIN])
|
||||
cg.add(var.set_display_data_1_pin(display_data_1))
|
||||
|
||||
display_data_2 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_2_PIN])
|
||||
cg.add(var.set_display_data_2_pin(display_data_2))
|
||||
|
||||
display_data_3 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_3_PIN])
|
||||
cg.add(var.set_display_data_3_pin(display_data_3))
|
||||
|
||||
display_data_4 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_4_PIN])
|
||||
cg.add(var.set_display_data_4_pin(display_data_4))
|
||||
|
||||
display_data_5 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_5_PIN])
|
||||
cg.add(var.set_display_data_5_pin(display_data_5))
|
||||
|
||||
display_data_6 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_6_PIN])
|
||||
cg.add(var.set_display_data_6_pin(display_data_6))
|
||||
|
||||
display_data_7 = await cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_7_PIN])
|
||||
cg.add(var.set_display_data_7_pin(display_data_7))
|
||||
|
@@ -6,11 +6,11 @@
|
||||
#include <hal/gpio_hal.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace inkplate {
|
||||
namespace inkplate6 {
|
||||
|
||||
static const char *const TAG = "inkplate";
|
||||
|
||||
void Inkplate::setup() {
|
||||
void Inkplate6::setup() {
|
||||
for (uint32_t i = 0; i < 256; i++) {
|
||||
this->pin_lut_[i] = ((i & 0b00000011) << 4) | (((i & 0b00001100) >> 2) << 18) | (((i & 0b00010000) >> 4) << 23) |
|
||||
(((i & 0b11100000) >> 5) << 25);
|
||||
@@ -56,7 +56,7 @@ void Inkplate::setup() {
|
||||
/**
|
||||
* Allocate buffers. May be called after setup to re-initialise if e.g. greyscale is changed.
|
||||
*/
|
||||
void Inkplate::initialize_() {
|
||||
void Inkplate6::initialize_() {
|
||||
RAMAllocator<uint8_t> allocator;
|
||||
RAMAllocator<uint32_t> allocator32;
|
||||
uint32_t buffer_size = this->get_buffer_length_();
|
||||
@@ -81,25 +81,29 @@ void Inkplate::initialize_() {
|
||||
return;
|
||||
}
|
||||
if (this->greyscale_) {
|
||||
this->glut_ = allocator32.allocate(256 * GLUT_SIZE);
|
||||
uint8_t glut_size = 9;
|
||||
|
||||
this->glut_ = allocator32.allocate(256 * glut_size);
|
||||
if (this->glut_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Could not allocate glut!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->glut2_ = allocator32.allocate(256 * GLUT_SIZE);
|
||||
this->glut2_ = allocator32.allocate(256 * glut_size);
|
||||
if (this->glut2_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Could not allocate glut2!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < GLUT_SIZE; i++) {
|
||||
const auto *const waveform3_bit = waveform3BitAll[this->model_];
|
||||
|
||||
for (int i = 0; i < glut_size; i++) {
|
||||
for (uint32_t j = 0; j < 256; j++) {
|
||||
uint8_t z = (this->waveform_[j & 0x07][i] << 2) | (this->waveform_[(j >> 4) & 0x07][i]);
|
||||
uint8_t z = (waveform3_bit[j & 0x07][i] << 2) | (waveform3_bit[(j >> 4) & 0x07][i]);
|
||||
this->glut_[i * 256 + j] = ((z & 0b00000011) << 4) | (((z & 0b00001100) >> 2) << 18) |
|
||||
(((z & 0b00010000) >> 4) << 23) | (((z & 0b11100000) >> 5) << 25);
|
||||
z = ((this->waveform_[j & 0x07][i] << 2) | (this->waveform_[(j >> 4) & 0x07][i])) << 4;
|
||||
z = ((waveform3_bit[j & 0x07][i] << 2) | (waveform3_bit[(j >> 4) & 0x07][i])) << 4;
|
||||
this->glut2_[i * 256 + j] = ((z & 0b00000011) << 4) | (((z & 0b00001100) >> 2) << 18) |
|
||||
(((z & 0b00010000) >> 4) << 23) | (((z & 0b11100000) >> 5) << 25);
|
||||
}
|
||||
@@ -126,9 +130,9 @@ void Inkplate::initialize_() {
|
||||
memset(this->buffer_, 0, buffer_size);
|
||||
}
|
||||
|
||||
float Inkplate::get_setup_priority() const { return setup_priority::PROCESSOR; }
|
||||
float Inkplate6::get_setup_priority() const { return setup_priority::PROCESSOR; }
|
||||
|
||||
size_t Inkplate::get_buffer_length_() {
|
||||
size_t Inkplate6::get_buffer_length_() {
|
||||
if (this->greyscale_) {
|
||||
return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 2u;
|
||||
} else {
|
||||
@@ -136,7 +140,7 @@ size_t Inkplate::get_buffer_length_() {
|
||||
}
|
||||
}
|
||||
|
||||
void Inkplate::update() {
|
||||
void Inkplate6::update() {
|
||||
this->do_update_();
|
||||
|
||||
if (this->full_update_every_ > 0 && this->partial_updates_ >= this->full_update_every_) {
|
||||
@@ -146,7 +150,7 @@ void Inkplate::update() {
|
||||
this->display();
|
||||
}
|
||||
|
||||
void HOT Inkplate::draw_absolute_pixel_internal(int x, int y, Color color) {
|
||||
void HOT Inkplate6::draw_absolute_pixel_internal(int x, int y, Color color) {
|
||||
if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0)
|
||||
return;
|
||||
|
||||
@@ -167,18 +171,18 @@ void HOT Inkplate::draw_absolute_pixel_internal(int x, int y, Color color) {
|
||||
// uint8_t gs = (uint8_t)(px*7);
|
||||
|
||||
uint8_t gs = ((color.red * 2126 / 10000) + (color.green * 7152 / 10000) + (color.blue * 722 / 10000)) >> 5;
|
||||
this->buffer_[pos] = (PIXEL_MASK_GLUT[x_sub] & current) | (x_sub ? gs : gs << 4);
|
||||
this->buffer_[pos] = (pixelMaskGLUT[x_sub] & current) | (x_sub ? gs : gs << 4);
|
||||
|
||||
} else {
|
||||
int x1 = x / 8;
|
||||
int x_sub = x % 8;
|
||||
uint32_t pos = (x1 + y * (this->get_width_internal() / 8));
|
||||
uint8_t current = this->partial_buffer_[pos];
|
||||
this->partial_buffer_[pos] = (~PIXEL_MASK_LUT[x_sub] & current) | (color.is_on() ? 0 : PIXEL_MASK_LUT[x_sub]);
|
||||
this->partial_buffer_[pos] = (~pixelMaskLUT[x_sub] & current) | (color.is_on() ? 0 : pixelMaskLUT[x_sub]);
|
||||
}
|
||||
}
|
||||
|
||||
void Inkplate::dump_config() {
|
||||
void Inkplate6::dump_config() {
|
||||
LOG_DISPLAY("", "Inkplate", this);
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Greyscale: %s\n"
|
||||
@@ -210,7 +214,7 @@ void Inkplate::dump_config() {
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void Inkplate::eink_off_() {
|
||||
void Inkplate6::eink_off_() {
|
||||
ESP_LOGV(TAG, "Eink off called");
|
||||
if (!panel_on_)
|
||||
return;
|
||||
@@ -238,7 +242,7 @@ void Inkplate::eink_off_() {
|
||||
pins_z_state_();
|
||||
}
|
||||
|
||||
void Inkplate::eink_on_() {
|
||||
void Inkplate6::eink_on_() {
|
||||
ESP_LOGV(TAG, "Eink on called");
|
||||
if (panel_on_)
|
||||
return;
|
||||
@@ -280,7 +284,7 @@ void Inkplate::eink_on_() {
|
||||
this->oe_pin_->digital_write(true);
|
||||
}
|
||||
|
||||
bool Inkplate::read_power_status_() {
|
||||
bool Inkplate6::read_power_status_() {
|
||||
uint8_t data;
|
||||
auto err = this->read_register(0x0F, &data, 1);
|
||||
if (err == i2c::ERROR_OK) {
|
||||
@@ -289,7 +293,7 @@ bool Inkplate::read_power_status_() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Inkplate::fill(Color color) {
|
||||
void Inkplate6::fill(Color color) {
|
||||
ESP_LOGV(TAG, "Fill called");
|
||||
uint32_t start_time = millis();
|
||||
|
||||
@@ -304,7 +308,7 @@ void Inkplate::fill(Color color) {
|
||||
ESP_LOGV(TAG, "Fill finished (%ums)", millis() - start_time);
|
||||
}
|
||||
|
||||
void Inkplate::display() {
|
||||
void Inkplate6::display() {
|
||||
ESP_LOGV(TAG, "Display called");
|
||||
uint32_t start_time = millis();
|
||||
|
||||
@@ -320,7 +324,7 @@ void Inkplate::display() {
|
||||
ESP_LOGV(TAG, "Display finished (full) (%ums)", millis() - start_time);
|
||||
}
|
||||
|
||||
void Inkplate::display1b_() {
|
||||
void Inkplate6::display1b_() {
|
||||
ESP_LOGV(TAG, "Display1b called");
|
||||
uint32_t start_time = millis();
|
||||
|
||||
@@ -330,71 +334,32 @@ void Inkplate::display1b_() {
|
||||
uint8_t buffer_value;
|
||||
const uint8_t *buffer_ptr;
|
||||
eink_on_();
|
||||
uint8_t rep = 4;
|
||||
switch (this->model_) {
|
||||
case INKPLATE_10:
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 10);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 10);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 10);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 10);
|
||||
rep = 5;
|
||||
break;
|
||||
case INKPLATE_6_PLUS:
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 15);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 5);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 15);
|
||||
break;
|
||||
case INKPLATE_6:
|
||||
case INKPLATE_6_V2:
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 18);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 18);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 18);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 18);
|
||||
clean_fast_(2, 1);
|
||||
if (this->model_ == INKPLATE_6_V2)
|
||||
rep = 5;
|
||||
break;
|
||||
case INKPLATE_5:
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 14);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 14);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 14);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 14);
|
||||
clean_fast_(2, 1);
|
||||
rep = 5;
|
||||
break;
|
||||
case INKPLATE_5_V2:
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 11);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 11);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 11);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 11);
|
||||
rep = 3;
|
||||
break;
|
||||
if (this->model_ == INKPLATE_6_PLUS) {
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 15);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 5);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 15);
|
||||
} else {
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 21);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 12);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 21);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 12);
|
||||
clean_fast_(2, 1);
|
||||
}
|
||||
|
||||
uint32_t clock = (1 << this->cl_pin_->get_pin());
|
||||
uint32_t data_mask = this->get_data_pin_mask_();
|
||||
ESP_LOGV(TAG, "Display1b start loops (%ums)", millis() - start_time);
|
||||
|
||||
for (uint8_t k = 0; k < rep; k++) {
|
||||
int rep = (this->model_ == INKPLATE_6_V2) ? 5 : 4;
|
||||
|
||||
for (int k = 0; k < rep; k++) {
|
||||
buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1];
|
||||
vscan_start_();
|
||||
for (int i = 0, im = this->get_height_internal(); i < im; i++) {
|
||||
@@ -487,75 +452,28 @@ void Inkplate::display1b_() {
|
||||
ESP_LOGV(TAG, "Display1b finished (%ums)", millis() - start_time);
|
||||
}
|
||||
|
||||
void Inkplate::display3b_() {
|
||||
void Inkplate6::display3b_() {
|
||||
ESP_LOGV(TAG, "Display3b called");
|
||||
uint32_t start_time = millis();
|
||||
|
||||
eink_on_();
|
||||
|
||||
switch (this->model_) {
|
||||
case INKPLATE_10:
|
||||
if (this->custom_waveform_) {
|
||||
clean_fast_(1, 1);
|
||||
clean_fast_(0, 7);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 12);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 7);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 12);
|
||||
} else {
|
||||
clean_fast_(1, 1);
|
||||
clean_fast_(0, 10);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 10);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 10);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 10);
|
||||
}
|
||||
break;
|
||||
case INKPLATE_6_PLUS:
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 15);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 5);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 15);
|
||||
break;
|
||||
case INKPLATE_6:
|
||||
case INKPLATE_6_V2:
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 18);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 18);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 18);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 18);
|
||||
clean_fast_(2, 1);
|
||||
break;
|
||||
case INKPLATE_5:
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 14);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 14);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 14);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 14);
|
||||
clean_fast_(2, 1);
|
||||
break;
|
||||
case INKPLATE_5_V2:
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 11);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 11);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 11);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 11);
|
||||
break;
|
||||
if (this->model_ == INKPLATE_6_PLUS) {
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 15);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 5);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 15);
|
||||
} else {
|
||||
clean_fast_(0, 1);
|
||||
clean_fast_(1, 21);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 12);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(1, 21);
|
||||
clean_fast_(2, 1);
|
||||
clean_fast_(0, 12);
|
||||
clean_fast_(2, 1);
|
||||
}
|
||||
|
||||
uint32_t clock = (1 << this->cl_pin_->get_pin());
|
||||
@@ -600,7 +518,7 @@ void Inkplate::display3b_() {
|
||||
ESP_LOGV(TAG, "Display3b finished (%ums)", millis() - start_time);
|
||||
}
|
||||
|
||||
bool Inkplate::partial_update_() {
|
||||
bool Inkplate6::partial_update_() {
|
||||
ESP_LOGV(TAG, "Partial update called");
|
||||
uint32_t start_time = millis();
|
||||
if (this->greyscale_)
|
||||
@@ -642,7 +560,7 @@ bool Inkplate::partial_update_() {
|
||||
GPIO.out_w1ts = this->pin_lut_[data] | clock;
|
||||
GPIO.out_w1tc = data_mask | clock;
|
||||
}
|
||||
// New Inkplate panel doesn't need last clock
|
||||
// New Inkplate6 panel doesn't need last clock
|
||||
if (this->model_ != INKPLATE_6_V2) {
|
||||
GPIO.out_w1ts = clock;
|
||||
GPIO.out_w1tc = data_mask | clock;
|
||||
@@ -662,7 +580,7 @@ bool Inkplate::partial_update_() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Inkplate::vscan_start_() {
|
||||
void Inkplate6::vscan_start_() {
|
||||
this->ckv_pin_->digital_write(true);
|
||||
delayMicroseconds(7);
|
||||
this->spv_pin_->digital_write(false);
|
||||
@@ -686,7 +604,7 @@ void Inkplate::vscan_start_() {
|
||||
this->ckv_pin_->digital_write(true);
|
||||
}
|
||||
|
||||
void Inkplate::hscan_start_(uint32_t d) {
|
||||
void Inkplate6::hscan_start_(uint32_t d) {
|
||||
uint8_t clock = (1 << this->cl_pin_->get_pin());
|
||||
this->sph_pin_->digital_write(false);
|
||||
GPIO.out_w1ts = d | clock;
|
||||
@@ -695,14 +613,14 @@ void Inkplate::hscan_start_(uint32_t d) {
|
||||
this->ckv_pin_->digital_write(true);
|
||||
}
|
||||
|
||||
void Inkplate::vscan_end_() {
|
||||
void Inkplate6::vscan_end_() {
|
||||
this->ckv_pin_->digital_write(false);
|
||||
this->le_pin_->digital_write(true);
|
||||
this->le_pin_->digital_write(false);
|
||||
delayMicroseconds(0);
|
||||
}
|
||||
|
||||
void Inkplate::clean() {
|
||||
void Inkplate6::clean() {
|
||||
ESP_LOGV(TAG, "Clean called");
|
||||
uint32_t start_time = millis();
|
||||
|
||||
@@ -716,7 +634,7 @@ void Inkplate::clean() {
|
||||
ESP_LOGV(TAG, "Clean finished (%ums)", millis() - start_time);
|
||||
}
|
||||
|
||||
void Inkplate::clean_fast_(uint8_t c, uint8_t rep) {
|
||||
void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) {
|
||||
ESP_LOGV(TAG, "Clean fast called with: (%d, %d)", c, rep);
|
||||
uint32_t start_time = millis();
|
||||
|
||||
@@ -748,7 +666,7 @@ void Inkplate::clean_fast_(uint8_t c, uint8_t rep) {
|
||||
GPIO.out_w1ts = clock;
|
||||
GPIO.out_w1tc = clock;
|
||||
}
|
||||
// New Inkplate panel doesn't need last clock
|
||||
// New Inkplate6 panel doesn't need last clock
|
||||
if (this->model_ != INKPLATE_6_V2) {
|
||||
GPIO.out_w1ts = send | clock;
|
||||
GPIO.out_w1tc = clock;
|
||||
@@ -761,7 +679,7 @@ void Inkplate::clean_fast_(uint8_t c, uint8_t rep) {
|
||||
ESP_LOGV(TAG, "Clean fast finished (%ums)", millis() - start_time);
|
||||
}
|
||||
|
||||
void Inkplate::pins_z_state_() {
|
||||
void Inkplate6::pins_z_state_() {
|
||||
this->cl_pin_->pin_mode(gpio::FLAG_INPUT);
|
||||
this->le_pin_->pin_mode(gpio::FLAG_INPUT);
|
||||
this->ckv_pin_->pin_mode(gpio::FLAG_INPUT);
|
||||
@@ -781,7 +699,7 @@ void Inkplate::pins_z_state_() {
|
||||
this->display_data_7_pin_->pin_mode(gpio::FLAG_INPUT);
|
||||
}
|
||||
|
||||
void Inkplate::pins_as_outputs_() {
|
||||
void Inkplate6::pins_as_outputs_() {
|
||||
this->cl_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
||||
this->le_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
||||
this->ckv_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
||||
@@ -801,5 +719,5 @@ void Inkplate::pins_as_outputs_() {
|
||||
this->display_data_7_pin_->pin_mode(gpio::FLAG_OUTPUT);
|
||||
}
|
||||
|
||||
} // namespace inkplate
|
||||
} // namespace inkplate6
|
||||
} // namespace esphome
|
@@ -5,10 +5,8 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace esphome {
|
||||
namespace inkplate {
|
||||
namespace inkplate6 {
|
||||
|
||||
enum InkplateModel : uint8_t {
|
||||
INKPLATE_6 = 0,
|
||||
@@ -19,35 +17,79 @@ enum InkplateModel : uint8_t {
|
||||
INKPLATE_5_V2 = 5,
|
||||
};
|
||||
|
||||
static constexpr uint8_t GLUT_SIZE = 9;
|
||||
static constexpr uint8_t GLUT_COUNT = 8;
|
||||
|
||||
static constexpr uint8_t LUT2[16] = {0xAA, 0xA9, 0xA6, 0xA5, 0x9A, 0x99, 0x96, 0x95,
|
||||
0x6A, 0x69, 0x66, 0x65, 0x5A, 0x59, 0x56, 0x55};
|
||||
static constexpr uint8_t LUTW[16] = {0xFF, 0xFE, 0xFB, 0xFA, 0xEF, 0xEE, 0xEB, 0xEA,
|
||||
0xBF, 0xBE, 0xBB, 0xBA, 0xAF, 0xAE, 0xAB, 0xAA};
|
||||
static constexpr uint8_t LUTB[16] = {0xFF, 0xFD, 0xF7, 0xF5, 0xDF, 0xDD, 0xD7, 0xD5,
|
||||
0x7F, 0x7D, 0x77, 0x75, 0x5F, 0x5D, 0x57, 0x55};
|
||||
|
||||
static constexpr uint8_t PIXEL_MASK_LUT[8] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
|
||||
static constexpr uint8_t PIXEL_MASK_GLUT[2] = {0x0F, 0xF0};
|
||||
|
||||
class Inkplate : public display::DisplayBuffer, public i2c::I2CDevice {
|
||||
class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice {
|
||||
public:
|
||||
const uint8_t LUT2[16] = {0xAA, 0xA9, 0xA6, 0xA5, 0x9A, 0x99, 0x96, 0x95,
|
||||
0x6A, 0x69, 0x66, 0x65, 0x5A, 0x59, 0x56, 0x55};
|
||||
const uint8_t LUTW[16] = {0xFF, 0xFE, 0xFB, 0xFA, 0xEF, 0xEE, 0xEB, 0xEA,
|
||||
0xBF, 0xBE, 0xBB, 0xBA, 0xAF, 0xAE, 0xAB, 0xAA};
|
||||
const uint8_t LUTB[16] = {0xFF, 0xFD, 0xF7, 0xF5, 0xDF, 0xDD, 0xD7, 0xD5,
|
||||
0x7F, 0x7D, 0x77, 0x75, 0x5F, 0x5D, 0x57, 0x55};
|
||||
|
||||
const uint8_t pixelMaskLUT[8] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
|
||||
const uint8_t pixelMaskGLUT[2] = {0x0F, 0xF0};
|
||||
|
||||
const uint8_t waveform3BitAll[6][8][9] = {// INKPLATE_6
|
||||
{{0, 1, 1, 0, 0, 1, 1, 0, 0},
|
||||
{0, 1, 2, 1, 1, 2, 1, 0, 0},
|
||||
{1, 1, 1, 2, 2, 1, 0, 0, 0},
|
||||
{0, 0, 0, 1, 1, 1, 2, 0, 0},
|
||||
{2, 1, 1, 1, 2, 1, 2, 0, 0},
|
||||
{2, 2, 1, 1, 2, 1, 2, 0, 0},
|
||||
{1, 1, 1, 2, 1, 2, 2, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 2, 0, 0}},
|
||||
// INKPLATE_10
|
||||
{{0, 0, 0, 0, 0, 0, 0, 1, 0},
|
||||
{0, 0, 0, 2, 2, 2, 1, 1, 0},
|
||||
{0, 0, 2, 1, 1, 2, 2, 1, 0},
|
||||
{0, 1, 2, 2, 1, 2, 2, 1, 0},
|
||||
{0, 0, 2, 1, 2, 2, 2, 1, 0},
|
||||
{0, 2, 2, 2, 2, 2, 2, 1, 0},
|
||||
{0, 0, 0, 0, 0, 2, 1, 2, 0},
|
||||
{0, 0, 0, 2, 2, 2, 2, 2, 0}},
|
||||
// INKPLATE_6_PLUS
|
||||
{{0, 0, 0, 0, 0, 2, 1, 1, 0},
|
||||
{0, 0, 2, 1, 1, 1, 2, 1, 0},
|
||||
{0, 2, 2, 2, 1, 1, 2, 1, 0},
|
||||
{0, 0, 2, 2, 2, 1, 2, 1, 0},
|
||||
{0, 0, 0, 0, 2, 2, 2, 1, 0},
|
||||
{0, 0, 2, 1, 2, 1, 1, 2, 0},
|
||||
{0, 0, 2, 2, 2, 1, 1, 2, 0},
|
||||
{0, 0, 0, 0, 2, 2, 2, 2, 0}},
|
||||
// INKPLATE_6_V2
|
||||
{{1, 0, 1, 0, 1, 1, 1, 0, 0},
|
||||
{0, 0, 0, 1, 1, 1, 1, 0, 0},
|
||||
{1, 1, 1, 1, 0, 2, 1, 0, 0},
|
||||
{1, 1, 1, 2, 2, 1, 1, 0, 0},
|
||||
{1, 1, 1, 1, 2, 2, 1, 0, 0},
|
||||
{0, 1, 1, 1, 2, 2, 1, 0, 0},
|
||||
{0, 0, 0, 0, 1, 1, 2, 0, 0},
|
||||
{0, 0, 0, 0, 0, 1, 2, 0, 0}},
|
||||
// INKPLATE_5
|
||||
{{0, 0, 1, 1, 0, 1, 1, 1, 0},
|
||||
{0, 1, 1, 1, 1, 2, 0, 1, 0},
|
||||
{1, 2, 2, 0, 2, 1, 1, 1, 0},
|
||||
{1, 1, 1, 2, 0, 1, 1, 2, 0},
|
||||
{0, 1, 1, 1, 2, 0, 1, 2, 0},
|
||||
{0, 0, 0, 1, 1, 2, 1, 2, 0},
|
||||
{1, 1, 1, 2, 0, 2, 1, 2, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
// INKPLATE_5_V2
|
||||
{{0, 0, 1, 1, 2, 1, 1, 1, 0},
|
||||
{1, 1, 2, 2, 1, 2, 1, 1, 0},
|
||||
{0, 1, 2, 2, 1, 1, 2, 1, 0},
|
||||
{0, 0, 1, 1, 1, 1, 1, 2, 0},
|
||||
{1, 2, 1, 2, 1, 1, 1, 2, 0},
|
||||
{0, 1, 1, 1, 2, 0, 1, 2, 0},
|
||||
{1, 1, 1, 2, 2, 2, 1, 2, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}}};
|
||||
|
||||
void set_greyscale(bool greyscale) {
|
||||
this->greyscale_ = greyscale;
|
||||
this->block_partial_ = true;
|
||||
if (this->is_ready())
|
||||
this->initialize_();
|
||||
}
|
||||
|
||||
void set_waveform(const std::array<uint8_t, GLUT_COUNT * GLUT_SIZE> &waveform, bool is_custom) {
|
||||
static_assert(sizeof(this->waveform_) == sizeof(uint8_t) * GLUT_COUNT * GLUT_SIZE,
|
||||
"waveform_ buffer size must match input waveform array size");
|
||||
memmove(this->waveform_, waveform.data(), sizeof(this->waveform_));
|
||||
this->custom_waveform_ = is_custom;
|
||||
}
|
||||
|
||||
void set_mirror_y(bool mirror_y) { this->mirror_y_ = mirror_y; }
|
||||
void set_mirror_x(bool mirror_x) { this->mirror_x_ = mirror_x; }
|
||||
|
||||
@@ -183,8 +225,6 @@ class Inkplate : public display::DisplayBuffer, public i2c::I2CDevice {
|
||||
bool mirror_y_{false};
|
||||
bool mirror_x_{false};
|
||||
bool partial_updating_;
|
||||
bool custom_waveform_{false};
|
||||
uint8_t waveform_[GLUT_COUNT][GLUT_SIZE];
|
||||
|
||||
InkplateModel model_;
|
||||
|
||||
@@ -210,5 +250,5 @@ class Inkplate : public display::DisplayBuffer, public i2c::I2CDevice {
|
||||
GPIOPin *wakeup_pin_;
|
||||
};
|
||||
|
||||
} // namespace inkplate
|
||||
} // namespace inkplate6
|
||||
} // namespace esphome
|
@@ -12,7 +12,6 @@
|
||||
#include "light_transformer.h"
|
||||
|
||||
#include <vector>
|
||||
#include <strings.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace light {
|
||||
|
@@ -246,35 +246,14 @@ void Logger::add_on_log_callback(std::function<void(uint8_t, const char *, const
|
||||
this->log_callback_.add(std::move(callback));
|
||||
}
|
||||
float Logger::get_setup_priority() const { return setup_priority::BUS + 500.0f; }
|
||||
|
||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||
// ESP8266: PSTR() cannot be used in array initializers, so we need to declare
|
||||
// each string separately as a global constant first
|
||||
static const char LOG_LEVEL_NONE[] PROGMEM = "NONE";
|
||||
static const char LOG_LEVEL_ERROR[] PROGMEM = "ERROR";
|
||||
static const char LOG_LEVEL_WARN[] PROGMEM = "WARN";
|
||||
static const char LOG_LEVEL_INFO[] PROGMEM = "INFO";
|
||||
static const char LOG_LEVEL_CONFIG[] PROGMEM = "CONFIG";
|
||||
static const char LOG_LEVEL_DEBUG[] PROGMEM = "DEBUG";
|
||||
static const char LOG_LEVEL_VERBOSE[] PROGMEM = "VERBOSE";
|
||||
static const char LOG_LEVEL_VERY_VERBOSE[] PROGMEM = "VERY_VERBOSE";
|
||||
|
||||
static const LogString *const LOG_LEVELS[] = {
|
||||
reinterpret_cast<const LogString *>(LOG_LEVEL_NONE), reinterpret_cast<const LogString *>(LOG_LEVEL_ERROR),
|
||||
reinterpret_cast<const LogString *>(LOG_LEVEL_WARN), reinterpret_cast<const LogString *>(LOG_LEVEL_INFO),
|
||||
reinterpret_cast<const LogString *>(LOG_LEVEL_CONFIG), reinterpret_cast<const LogString *>(LOG_LEVEL_DEBUG),
|
||||
reinterpret_cast<const LogString *>(LOG_LEVEL_VERBOSE), reinterpret_cast<const LogString *>(LOG_LEVEL_VERY_VERBOSE),
|
||||
};
|
||||
#else
|
||||
static const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "VERBOSE", "VERY_VERBOSE"};
|
||||
#endif
|
||||
|
||||
void Logger::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"Logger:\n"
|
||||
" Max Level: %s\n"
|
||||
" Initial Level: %s",
|
||||
LOG_STR_ARG(LOG_LEVELS[ESPHOME_LOG_LEVEL]), LOG_STR_ARG(LOG_LEVELS[this->current_level_]));
|
||||
LOG_LEVELS[ESPHOME_LOG_LEVEL], LOG_LEVELS[this->current_level_]);
|
||||
#ifndef USE_HOST
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Log Baud Rate: %" PRIu32 "\n"
|
||||
@@ -288,14 +267,14 @@ void Logger::dump_config() {
|
||||
#endif
|
||||
|
||||
for (auto &it : this->log_levels_) {
|
||||
ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.first.c_str(), LOG_STR_ARG(LOG_LEVELS[it.second]));
|
||||
ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.first.c_str(), LOG_LEVELS[it.second]);
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::set_log_level(uint8_t level) {
|
||||
if (level > ESPHOME_LOG_LEVEL) {
|
||||
level = ESPHOME_LOG_LEVEL;
|
||||
ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_STR_ARG(LOG_LEVELS[ESPHOME_LOG_LEVEL]));
|
||||
ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);
|
||||
}
|
||||
this->current_level_ = level;
|
||||
this->level_callback_.call(level);
|
||||
|
@@ -11,7 +11,6 @@ from esphome.const import (
|
||||
CONF_OUTPUT,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["gpio_expander"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
|
||||
|
@@ -22,29 +22,14 @@ void MCP23016::setup() {
|
||||
this->write_reg_(MCP23016_IODIR0, 0xFF);
|
||||
this->write_reg_(MCP23016_IODIR1, 0xFF);
|
||||
}
|
||||
|
||||
void MCP23016::loop() {
|
||||
// Invalidate cache at the start of each loop
|
||||
this->reset_pin_cache_();
|
||||
}
|
||||
bool MCP23016::digital_read_hw(uint8_t pin) {
|
||||
bool MCP23016::digital_read(uint8_t pin) {
|
||||
uint8_t bit = pin % 8;
|
||||
uint8_t reg_addr = pin < 8 ? MCP23016_GP0 : MCP23016_GP1;
|
||||
uint8_t value = 0;
|
||||
if (!this->read_reg_(reg_addr, &value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the appropriate part of input_mask_
|
||||
if (pin < 8) {
|
||||
this->input_mask_ = (this->input_mask_ & 0xFF00) | value;
|
||||
} else {
|
||||
this->input_mask_ = (this->input_mask_ & 0x00FF) | (uint16_t(value) << 8);
|
||||
}
|
||||
return true;
|
||||
this->read_reg_(reg_addr, &value);
|
||||
return value & (1 << bit);
|
||||
}
|
||||
|
||||
bool MCP23016::digital_read_cache(uint8_t pin) { return this->input_mask_ & (1 << pin); }
|
||||
void MCP23016::digital_write_hw(uint8_t pin, bool value) {
|
||||
void MCP23016::digital_write(uint8_t pin, bool value) {
|
||||
uint8_t reg_addr = pin < 8 ? MCP23016_OLAT0 : MCP23016_OLAT1;
|
||||
this->update_reg_(pin, value, reg_addr);
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/gpio_expander/cached_gpio.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mcp23016 {
|
||||
@@ -25,22 +24,19 @@ enum MCP23016GPIORegisters {
|
||||
MCP23016_IOCON1 = 0x0B,
|
||||
};
|
||||
|
||||
class MCP23016 : public Component, public i2c::I2CDevice, public gpio_expander::CachedGpioExpander<uint8_t, 16> {
|
||||
class MCP23016 : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
MCP23016() = default;
|
||||
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
|
||||
bool digital_read(uint8_t pin);
|
||||
void digital_write(uint8_t pin, bool value);
|
||||
void pin_mode(uint8_t pin, gpio::Flags flags);
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
// Virtual methods from CachedGpioExpander
|
||||
bool digital_read_hw(uint8_t pin) override;
|
||||
bool digital_read_cache(uint8_t pin) override;
|
||||
void digital_write_hw(uint8_t pin, bool value) override;
|
||||
|
||||
// read a given register
|
||||
bool read_reg_(uint8_t reg, uint8_t *value);
|
||||
// write a value to a given register
|
||||
@@ -50,8 +46,6 @@ class MCP23016 : public Component, public i2c::I2CDevice, public gpio_expander::
|
||||
|
||||
uint8_t olat_0_{0x00};
|
||||
uint8_t olat_1_{0x00};
|
||||
// Cache for input values (16-bit combined for both banks)
|
||||
uint16_t input_mask_{0x00};
|
||||
};
|
||||
|
||||
class MCP23016GPIOPin : public GPIOPin {
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include "md5.h"
|
||||
#ifdef USE_MD5
|
||||
@@ -43,9 +44,7 @@ void MD5Digest::get_bytes(uint8_t *output) { memcpy(output, this->digest_, 16);
|
||||
|
||||
void MD5Digest::get_hex(char *output) {
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
uint8_t byte = this->digest_[i];
|
||||
output[i * 2] = format_hex_char(byte >> 4);
|
||||
output[i * 2 + 1] = format_hex_char(byte & 0x0F);
|
||||
sprintf(output + i * 2, "%02x", this->digest_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -11,8 +11,7 @@ from esphome.const import (
|
||||
CONF_SERVICES,
|
||||
PlatformFramework,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.coroutine import CoroPriority
|
||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
DEPENDENCIES = ["network"]
|
||||
@@ -73,7 +72,7 @@ def mdns_service(
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.NETWORK_SERVICES)
|
||||
@coroutine_with_priority(CoroPriority.COMMUNICATION)
|
||||
async def to_code(config):
|
||||
if config[CONF_DISABLED] is True:
|
||||
return
|
||||
|
@@ -222,12 +222,6 @@ def delay(ms):
|
||||
|
||||
|
||||
class DriverChip:
|
||||
"""
|
||||
A class representing a MIPI DBI driver chip model.
|
||||
The parameters supplied as defaults will be used to provide default values for the display configuration.
|
||||
Setting swap_xy to cv.UNDEFINED will indicate that the model does not support swapping X and Y axes.
|
||||
"""
|
||||
|
||||
models: dict[str, Self] = {}
|
||||
|
||||
def __init__(
|
||||
@@ -238,7 +232,7 @@ class DriverChip:
|
||||
):
|
||||
name = name.upper()
|
||||
self.name = name
|
||||
self.initsequence = initsequence
|
||||
self.initsequence = initsequence or defaults.get("init_sequence")
|
||||
self.defaults = defaults
|
||||
DriverChip.models[name] = self
|
||||
|
||||
@@ -252,17 +246,6 @@ class DriverChip:
|
||||
return models
|
||||
|
||||
def extend(self, name, **kwargs) -> "DriverChip":
|
||||
"""
|
||||
Extend the current model with additional parameters or a modified init sequence.
|
||||
Parameters supplied here will override the defaults of the current model.
|
||||
if the initsequence is not provided, the current model's initsequence will be used.
|
||||
If add_init_sequence is provided, it will be appended to the current initsequence.
|
||||
:param name:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
initsequence = list(kwargs.pop("initsequence", self.initsequence))
|
||||
initsequence.extend(kwargs.pop("add_init_sequence", ()))
|
||||
defaults = self.defaults.copy()
|
||||
if (
|
||||
CONF_WIDTH in defaults
|
||||
@@ -277,39 +260,23 @@ class DriverChip:
|
||||
):
|
||||
defaults[CONF_NATIVE_HEIGHT] = defaults[CONF_HEIGHT]
|
||||
defaults.update(kwargs)
|
||||
return self.__class__(name, initsequence=tuple(initsequence), **defaults)
|
||||
return DriverChip(name, initsequence=self.initsequence, **defaults)
|
||||
|
||||
def get_default(self, key, fallback: Any = False) -> Any:
|
||||
return self.defaults.get(key, fallback)
|
||||
|
||||
@property
|
||||
def transforms(self) -> set[str]:
|
||||
"""
|
||||
Return the available transforms for this model.
|
||||
"""
|
||||
if self.get_default("no_transform", False):
|
||||
return set()
|
||||
if self.get_default(CONF_SWAP_XY) != cv.UNDEFINED:
|
||||
return {CONF_MIRROR_X, CONF_MIRROR_Y, CONF_SWAP_XY}
|
||||
return {CONF_MIRROR_X, CONF_MIRROR_Y}
|
||||
|
||||
def option(self, name, fallback=False) -> cv.Optional:
|
||||
return cv.Optional(name, default=self.get_default(name, fallback))
|
||||
|
||||
def rotation_as_transform(self, config) -> bool:
|
||||
"""
|
||||
Check if a rotation can be implemented in hardware using the MADCTL register.
|
||||
A rotation of 180 is always possible if x and y mirroring are supported, 90 and 270 are possible if the model supports swapping X and Y.
|
||||
A rotation of 180 is always possible, 90 and 270 are possible if the model supports swapping X and Y.
|
||||
"""
|
||||
transforms = self.transforms
|
||||
rotation = config.get(CONF_ROTATION, 0)
|
||||
if rotation == 0 or not transforms:
|
||||
return False
|
||||
if rotation == 180:
|
||||
return CONF_MIRROR_X in transforms and CONF_MIRROR_Y in transforms
|
||||
if rotation == 90:
|
||||
return CONF_SWAP_XY in transforms and CONF_MIRROR_X in transforms
|
||||
return CONF_SWAP_XY in transforms and CONF_MIRROR_Y in transforms
|
||||
return rotation and (
|
||||
self.get_default(CONF_SWAP_XY) != cv.UNDEFINED or rotation == 180
|
||||
)
|
||||
|
||||
def get_dimensions(self, config) -> tuple[int, int, int, int]:
|
||||
if CONF_DIMENSIONS in config:
|
||||
@@ -334,10 +301,10 @@ class DriverChip:
|
||||
|
||||
# if mirroring axes and there are offsets, also mirror the offsets to cater for situations where
|
||||
# the offset is asymmetric
|
||||
if transform.get(CONF_MIRROR_X):
|
||||
if transform[CONF_MIRROR_X]:
|
||||
native_width = self.get_default(CONF_NATIVE_WIDTH, width + offset_width * 2)
|
||||
offset_width = native_width - width - offset_width
|
||||
if transform.get(CONF_MIRROR_Y):
|
||||
if transform[CONF_MIRROR_Y]:
|
||||
native_height = self.get_default(
|
||||
CONF_NATIVE_HEIGHT, height + offset_height * 2
|
||||
)
|
||||
@@ -347,7 +314,7 @@ class DriverChip:
|
||||
90,
|
||||
270,
|
||||
)
|
||||
if transform.get(CONF_SWAP_XY) is True or rotated:
|
||||
if transform[CONF_SWAP_XY] is True or rotated:
|
||||
width, height = height, width
|
||||
offset_height, offset_width = offset_width, offset_height
|
||||
return width, height, offset_width, offset_height
|
||||
@@ -357,56 +324,27 @@ class DriverChip:
|
||||
transform = config.get(
|
||||
CONF_TRANSFORM,
|
||||
{
|
||||
CONF_MIRROR_X: self.get_default(CONF_MIRROR_X),
|
||||
CONF_MIRROR_Y: self.get_default(CONF_MIRROR_Y),
|
||||
CONF_SWAP_XY: self.get_default(CONF_SWAP_XY),
|
||||
CONF_MIRROR_X: self.get_default(CONF_MIRROR_X, False),
|
||||
CONF_MIRROR_Y: self.get_default(CONF_MIRROR_Y, False),
|
||||
CONF_SWAP_XY: self.get_default(CONF_SWAP_XY, False),
|
||||
},
|
||||
)
|
||||
# fill in defaults if not provided
|
||||
mirror_x = transform.get(CONF_MIRROR_X, self.get_default(CONF_MIRROR_X))
|
||||
mirror_y = transform.get(CONF_MIRROR_Y, self.get_default(CONF_MIRROR_Y))
|
||||
swap_xy = transform.get(CONF_SWAP_XY, self.get_default(CONF_SWAP_XY))
|
||||
transform[CONF_MIRROR_X] = mirror_x
|
||||
transform[CONF_MIRROR_Y] = mirror_y
|
||||
transform[CONF_SWAP_XY] = swap_xy
|
||||
|
||||
# Can we use the MADCTL register to set the rotation?
|
||||
if can_transform and CONF_TRANSFORM not in config:
|
||||
rotation = config[CONF_ROTATION]
|
||||
if rotation == 180:
|
||||
transform[CONF_MIRROR_X] = not mirror_x
|
||||
transform[CONF_MIRROR_Y] = not mirror_y
|
||||
transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X]
|
||||
transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y]
|
||||
elif rotation == 90:
|
||||
transform[CONF_SWAP_XY] = not swap_xy
|
||||
transform[CONF_MIRROR_X] = not mirror_x
|
||||
transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY]
|
||||
transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X]
|
||||
else:
|
||||
transform[CONF_SWAP_XY] = not swap_xy
|
||||
transform[CONF_MIRROR_Y] = not mirror_y
|
||||
transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY]
|
||||
transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y]
|
||||
transform[CONF_TRANSFORM] = True
|
||||
return transform
|
||||
|
||||
def add_madctl(self, sequence: list, config: dict):
|
||||
# Add the MADCTL command to the sequence based on the configuration.
|
||||
use_flip = config.get(CONF_USE_AXIS_FLIPS)
|
||||
madctl = 0
|
||||
transform = self.get_transform(config)
|
||||
if transform[CONF_MIRROR_X]:
|
||||
madctl |= MADCTL_XFLIP if use_flip else MADCTL_MX
|
||||
if transform[CONF_MIRROR_Y]:
|
||||
madctl |= MADCTL_YFLIP if use_flip else MADCTL_MY
|
||||
if transform.get(CONF_SWAP_XY) is True: # Exclude Undefined
|
||||
madctl |= MADCTL_MV
|
||||
if config[CONF_COLOR_ORDER] == MODE_BGR:
|
||||
madctl |= MADCTL_BGR
|
||||
sequence.append((MADCTL, madctl))
|
||||
return madctl
|
||||
|
||||
def skip_command(self, command: str):
|
||||
"""
|
||||
Allow suppressing a standard command in the init sequence.
|
||||
"""
|
||||
return self.get_default(f"no_{command.lower()}", False)
|
||||
|
||||
def get_sequence(self, config) -> tuple[tuple[int, ...], int]:
|
||||
"""
|
||||
Create the init sequence for the display.
|
||||
@@ -429,18 +367,28 @@ class DriverChip:
|
||||
pixel_mode = PIXEL_MODES[pixel_mode]
|
||||
sequence.append((PIXFMT, pixel_mode))
|
||||
|
||||
# Does the chip use the flipping bits for mirroring rather than the reverse order bits?
|
||||
use_flip = config.get(CONF_USE_AXIS_FLIPS)
|
||||
madctl = 0
|
||||
transform = self.get_transform(config)
|
||||
if self.rotation_as_transform(config):
|
||||
LOGGER.info("Using hardware transform to implement rotation")
|
||||
madctl = self.add_madctl(sequence, config)
|
||||
if transform.get(CONF_MIRROR_X):
|
||||
madctl |= MADCTL_XFLIP if use_flip else MADCTL_MX
|
||||
if transform.get(CONF_MIRROR_Y):
|
||||
madctl |= MADCTL_YFLIP if use_flip else MADCTL_MY
|
||||
if transform.get(CONF_SWAP_XY) is True: # Exclude Undefined
|
||||
madctl |= MADCTL_MV
|
||||
if config[CONF_COLOR_ORDER] == MODE_BGR:
|
||||
madctl |= MADCTL_BGR
|
||||
sequence.append((MADCTL, madctl))
|
||||
if config[CONF_INVERT_COLORS]:
|
||||
sequence.append((INVON,))
|
||||
else:
|
||||
sequence.append((INVOFF,))
|
||||
if brightness := config.get(CONF_BRIGHTNESS, self.get_default(CONF_BRIGHTNESS)):
|
||||
sequence.append((BRIGHTNESS, brightness))
|
||||
# Add a SLPOUT command if required.
|
||||
if not self.skip_command("SLPOUT"):
|
||||
sequence.append((SLPOUT,))
|
||||
sequence.append((SLPOUT,))
|
||||
sequence.append((DISPON,))
|
||||
|
||||
# Flatten the sequence into a list of bytes, with the length of each command
|
||||
|
@@ -1,2 +0,0 @@
|
||||
CODEOWNERS = ["@clydebarrow"]
|
||||
DOMAIN = "mipi_rgb"
|
@@ -1,321 +0,0 @@
|
||||
import importlib
|
||||
import pkgutil
|
||||
|
||||
from esphome import pins
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import display, spi
|
||||
from esphome.components.const import (
|
||||
BYTE_ORDER_BIG,
|
||||
BYTE_ORDER_LITTLE,
|
||||
CONF_BYTE_ORDER,
|
||||
CONF_DRAW_ROUNDING,
|
||||
)
|
||||
from esphome.components.display import CONF_SHOW_TEST_CARD
|
||||
from esphome.components.esp32 import const, only_on_variant
|
||||
from esphome.components.mipi import (
|
||||
COLOR_ORDERS,
|
||||
CONF_DE_PIN,
|
||||
CONF_HSYNC_BACK_PORCH,
|
||||
CONF_HSYNC_FRONT_PORCH,
|
||||
CONF_HSYNC_PULSE_WIDTH,
|
||||
CONF_PCLK_PIN,
|
||||
CONF_PIXEL_MODE,
|
||||
CONF_USE_AXIS_FLIPS,
|
||||
CONF_VSYNC_BACK_PORCH,
|
||||
CONF_VSYNC_FRONT_PORCH,
|
||||
CONF_VSYNC_PULSE_WIDTH,
|
||||
MODE_BGR,
|
||||
PIXEL_MODE_16BIT,
|
||||
PIXEL_MODE_18BIT,
|
||||
DriverChip,
|
||||
dimension_schema,
|
||||
map_sequence,
|
||||
power_of_two,
|
||||
requires_buffer,
|
||||
)
|
||||
from esphome.components.rpi_dpi_rgb.display import (
|
||||
CONF_PCLK_FREQUENCY,
|
||||
CONF_PCLK_INVERTED,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BLUE,
|
||||
CONF_COLOR_ORDER,
|
||||
CONF_CS_PIN,
|
||||
CONF_DATA_PINS,
|
||||
CONF_DATA_RATE,
|
||||
CONF_DC_PIN,
|
||||
CONF_DIMENSIONS,
|
||||
CONF_ENABLE_PIN,
|
||||
CONF_GREEN,
|
||||
CONF_HSYNC_PIN,
|
||||
CONF_ID,
|
||||
CONF_IGNORE_STRAPPING_WARNING,
|
||||
CONF_INIT_SEQUENCE,
|
||||
CONF_INVERT_COLORS,
|
||||
CONF_LAMBDA,
|
||||
CONF_MIRROR_X,
|
||||
CONF_MIRROR_Y,
|
||||
CONF_MODEL,
|
||||
CONF_NUMBER,
|
||||
CONF_RED,
|
||||
CONF_RESET_PIN,
|
||||
CONF_ROTATION,
|
||||
CONF_SPI_ID,
|
||||
CONF_SWAP_XY,
|
||||
CONF_TRANSFORM,
|
||||
CONF_VSYNC_PIN,
|
||||
CONF_WIDTH,
|
||||
)
|
||||
from esphome.final_validate import full_config
|
||||
|
||||
from ..spi import CONF_SPI_MODE, SPI_DATA_RATE_SCHEMA, SPI_MODE_OPTIONS, SPIComponent
|
||||
from . import models
|
||||
|
||||
DEPENDENCIES = ["esp32", "psram"]
|
||||
|
||||
mipi_rgb_ns = cg.esphome_ns.namespace("mipi_rgb")
|
||||
mipi_rgb = mipi_rgb_ns.class_("MipiRgb", display.Display, cg.Component)
|
||||
mipi_rgb_spi = mipi_rgb_ns.class_(
|
||||
"MipiRgbSpi", mipi_rgb, display.Display, cg.Component, spi.SPIDevice
|
||||
)
|
||||
ColorOrder = display.display_ns.enum("ColorMode")
|
||||
|
||||
DATA_PIN_SCHEMA = pins.internal_gpio_output_pin_schema
|
||||
|
||||
DriverChip("CUSTOM")
|
||||
|
||||
# Import all models dynamically from the models package
|
||||
|
||||
for module_info in pkgutil.iter_modules(models.__path__):
|
||||
importlib.import_module(f".models.{module_info.name}", package=__package__)
|
||||
|
||||
MODELS = DriverChip.get_models()
|
||||
|
||||
|
||||
def data_pin_validate(value):
|
||||
"""
|
||||
It is safe to use strapping pins as RGB output data bits, as they are outputs only,
|
||||
and not initialised until after boot.
|
||||
"""
|
||||
if not isinstance(value, dict):
|
||||
try:
|
||||
return DATA_PIN_SCHEMA(
|
||||
{CONF_NUMBER: value, CONF_IGNORE_STRAPPING_WARNING: True}
|
||||
)
|
||||
except cv.Invalid:
|
||||
pass
|
||||
return DATA_PIN_SCHEMA(value)
|
||||
|
||||
|
||||
def data_pin_set(length):
|
||||
return cv.All(
|
||||
[data_pin_validate],
|
||||
cv.Length(min=length, max=length, msg=f"Exactly {length} data pins required"),
|
||||
)
|
||||
|
||||
|
||||
def model_schema(config):
|
||||
model = MODELS[config[CONF_MODEL].upper()]
|
||||
if transforms := model.transforms:
|
||||
transform = cv.Schema({cv.Required(x): cv.boolean for x in transforms})
|
||||
for x in (CONF_SWAP_XY, CONF_MIRROR_X, CONF_MIRROR_Y):
|
||||
if x not in transforms:
|
||||
transform = transform.extend(
|
||||
{cv.Optional(x): cv.invalid(f"{x} not supported by this model")}
|
||||
)
|
||||
else:
|
||||
transform = cv.invalid("This model does not support transforms")
|
||||
|
||||
# RPI model does not use an init sequence, indicates with empty list
|
||||
if model.initsequence is None:
|
||||
# Custom model requires an init sequence
|
||||
iseqconf = cv.Required(CONF_INIT_SEQUENCE)
|
||||
uses_spi = True
|
||||
else:
|
||||
iseqconf = cv.Optional(CONF_INIT_SEQUENCE)
|
||||
uses_spi = CONF_INIT_SEQUENCE in config or len(model.initsequence) != 0
|
||||
swap_xy = config.get(CONF_TRANSFORM, {}).get(CONF_SWAP_XY, False)
|
||||
|
||||
# Dimensions are optional if the model has a default width and the swap_xy transform is not overridden
|
||||
cv_dimensions = (
|
||||
cv.Optional if model.get_default(CONF_WIDTH) and not swap_xy else cv.Required
|
||||
)
|
||||
pixel_modes = (PIXEL_MODE_16BIT, PIXEL_MODE_18BIT, "16", "18")
|
||||
schema = display.FULL_DISPLAY_SCHEMA.extend(
|
||||
{
|
||||
model.option(CONF_RESET_PIN, cv.UNDEFINED): pins.gpio_output_pin_schema,
|
||||
cv.GenerateID(): cv.declare_id(mipi_rgb_spi if uses_spi else mipi_rgb),
|
||||
cv_dimensions(CONF_DIMENSIONS): dimension_schema(
|
||||
model.get_default(CONF_DRAW_ROUNDING, 1)
|
||||
),
|
||||
model.option(CONF_ENABLE_PIN, cv.UNDEFINED): cv.ensure_list(
|
||||
pins.gpio_output_pin_schema
|
||||
),
|
||||
model.option(CONF_COLOR_ORDER, MODE_BGR): cv.enum(COLOR_ORDERS, upper=True),
|
||||
model.option(CONF_DRAW_ROUNDING, 2): power_of_two,
|
||||
model.option(CONF_PIXEL_MODE, PIXEL_MODE_16BIT): cv.one_of(
|
||||
*pixel_modes, lower=True
|
||||
),
|
||||
model.option(CONF_TRANSFORM, cv.UNDEFINED): transform,
|
||||
cv.Required(CONF_MODEL): cv.one_of(model.name, upper=True),
|
||||
model.option(CONF_INVERT_COLORS, False): cv.boolean,
|
||||
model.option(CONF_USE_AXIS_FLIPS, True): cv.boolean,
|
||||
model.option(CONF_PCLK_FREQUENCY, "40MHz"): cv.All(
|
||||
cv.frequency, cv.Range(min=4e6, max=100e6)
|
||||
),
|
||||
model.option(CONF_PCLK_INVERTED, True): cv.boolean,
|
||||
iseqconf: cv.ensure_list(map_sequence),
|
||||
model.option(CONF_BYTE_ORDER, BYTE_ORDER_BIG): cv.one_of(
|
||||
BYTE_ORDER_LITTLE, BYTE_ORDER_BIG, lower=True
|
||||
),
|
||||
model.option(CONF_HSYNC_PULSE_WIDTH): cv.int_,
|
||||
model.option(CONF_HSYNC_BACK_PORCH): cv.int_,
|
||||
model.option(CONF_HSYNC_FRONT_PORCH): cv.int_,
|
||||
model.option(CONF_VSYNC_PULSE_WIDTH): cv.int_,
|
||||
model.option(CONF_VSYNC_BACK_PORCH): cv.int_,
|
||||
model.option(CONF_VSYNC_FRONT_PORCH): cv.int_,
|
||||
model.option(CONF_DATA_PINS): cv.Any(
|
||||
data_pin_set(16),
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_RED): data_pin_set(5),
|
||||
cv.Required(CONF_GREEN): data_pin_set(6),
|
||||
cv.Required(CONF_BLUE): data_pin_set(5),
|
||||
}
|
||||
),
|
||||
),
|
||||
model.option(
|
||||
CONF_DE_PIN, cv.UNDEFINED
|
||||
): pins.internal_gpio_output_pin_schema,
|
||||
model.option(CONF_PCLK_PIN): pins.internal_gpio_output_pin_schema,
|
||||
model.option(CONF_HSYNC_PIN): pins.internal_gpio_output_pin_schema,
|
||||
model.option(CONF_VSYNC_PIN): pins.internal_gpio_output_pin_schema,
|
||||
model.option(CONF_RESET_PIN, cv.UNDEFINED): pins.gpio_output_pin_schema,
|
||||
}
|
||||
)
|
||||
if uses_spi:
|
||||
schema = schema.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_SPI_ID): cv.use_id(SPIComponent),
|
||||
model.option(CONF_DC_PIN, cv.UNDEFINED): pins.gpio_output_pin_schema,
|
||||
model.option(CONF_DATA_RATE, "1MHz"): SPI_DATA_RATE_SCHEMA,
|
||||
model.option(CONF_SPI_MODE, "MODE0"): cv.enum(
|
||||
SPI_MODE_OPTIONS, upper=True
|
||||
),
|
||||
model.option(CONF_CS_PIN, cv.UNDEFINED): pins.gpio_output_pin_schema,
|
||||
}
|
||||
)
|
||||
return schema
|
||||
|
||||
|
||||
def _config_schema(config):
|
||||
config = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_MODEL): cv.one_of(*MODELS, upper=True),
|
||||
},
|
||||
extra=cv.ALLOW_EXTRA,
|
||||
)(config)
|
||||
schema = model_schema(config)
|
||||
return cv.All(
|
||||
schema,
|
||||
only_on_variant(supported=[const.VARIANT_ESP32S3]),
|
||||
cv.only_with_esp_idf,
|
||||
)(config)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = _config_schema
|
||||
|
||||
|
||||
def _final_validate(config):
|
||||
global_config = full_config.get()
|
||||
|
||||
from esphome.components.lvgl import DOMAIN as LVGL_DOMAIN
|
||||
|
||||
if not requires_buffer(config) and LVGL_DOMAIN not in global_config:
|
||||
# If no drawing methods are configured, and LVGL is not enabled, show a test card
|
||||
config[CONF_SHOW_TEST_CARD] = True
|
||||
if CONF_SPI_ID in config:
|
||||
config = spi.final_validate_device_schema(
|
||||
"mipi_rgb", require_miso=False, require_mosi=True
|
||||
)(config)
|
||||
return config
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
model = MODELS[config[CONF_MODEL].upper()]
|
||||
width, height, _offset_width, _offset_height = model.get_dimensions(config)
|
||||
var = cg.new_Pvariable(config[CONF_ID], width, height)
|
||||
cg.add(var.set_model(model.name))
|
||||
if enable_pin := config.get(CONF_ENABLE_PIN):
|
||||
enable = [await cg.gpio_pin_expression(pin) for pin in enable_pin]
|
||||
cg.add(var.set_enable_pins(enable))
|
||||
|
||||
if CONF_SPI_ID in config:
|
||||
await spi.register_spi_device(var, config)
|
||||
sequence, madctl = model.get_sequence(config)
|
||||
cg.add(var.set_init_sequence(sequence))
|
||||
cg.add(var.set_madctl(madctl))
|
||||
|
||||
cg.add(var.set_color_mode(COLOR_ORDERS[config[CONF_COLOR_ORDER]]))
|
||||
cg.add(var.set_invert_colors(config[CONF_INVERT_COLORS]))
|
||||
cg.add(var.set_hsync_pulse_width(config[CONF_HSYNC_PULSE_WIDTH]))
|
||||
cg.add(var.set_hsync_back_porch(config[CONF_HSYNC_BACK_PORCH]))
|
||||
cg.add(var.set_hsync_front_porch(config[CONF_HSYNC_FRONT_PORCH]))
|
||||
cg.add(var.set_vsync_pulse_width(config[CONF_VSYNC_PULSE_WIDTH]))
|
||||
cg.add(var.set_vsync_back_porch(config[CONF_VSYNC_BACK_PORCH]))
|
||||
cg.add(var.set_vsync_front_porch(config[CONF_VSYNC_FRONT_PORCH]))
|
||||
cg.add(var.set_pclk_inverted(config[CONF_PCLK_INVERTED]))
|
||||
cg.add(var.set_pclk_frequency(config[CONF_PCLK_FREQUENCY]))
|
||||
index = 0
|
||||
dpins = []
|
||||
if CONF_RED in config[CONF_DATA_PINS]:
|
||||
red_pins = config[CONF_DATA_PINS][CONF_RED]
|
||||
green_pins = config[CONF_DATA_PINS][CONF_GREEN]
|
||||
blue_pins = config[CONF_DATA_PINS][CONF_BLUE]
|
||||
if config[CONF_COLOR_ORDER] == "BGR":
|
||||
dpins.extend(red_pins)
|
||||
dpins.extend(green_pins)
|
||||
dpins.extend(blue_pins)
|
||||
else:
|
||||
dpins.extend(blue_pins)
|
||||
dpins.extend(green_pins)
|
||||
dpins.extend(red_pins)
|
||||
# swap bytes to match big-endian format
|
||||
dpins = dpins[8:16] + dpins[0:8]
|
||||
else:
|
||||
dpins = config[CONF_DATA_PINS]
|
||||
for index, pin in enumerate(dpins):
|
||||
data_pin = await cg.gpio_pin_expression(pin)
|
||||
cg.add(var.add_data_pin(data_pin, index))
|
||||
|
||||
if dc_pin := config.get(CONF_DC_PIN):
|
||||
dc = await cg.gpio_pin_expression(dc_pin)
|
||||
cg.add(var.set_dc_pin(dc))
|
||||
|
||||
if reset_pin := config.get(CONF_RESET_PIN):
|
||||
reset = await cg.gpio_pin_expression(reset_pin)
|
||||
cg.add(var.set_reset_pin(reset))
|
||||
|
||||
if model.rotation_as_transform(config):
|
||||
config[CONF_ROTATION] = 0
|
||||
|
||||
if de_pin := config.get(CONF_DE_PIN):
|
||||
pin = await cg.gpio_pin_expression(de_pin)
|
||||
cg.add(var.set_de_pin(pin))
|
||||
pin = await cg.gpio_pin_expression(config[CONF_PCLK_PIN])
|
||||
cg.add(var.set_pclk_pin(pin))
|
||||
pin = await cg.gpio_pin_expression(config[CONF_HSYNC_PIN])
|
||||
cg.add(var.set_hsync_pin(pin))
|
||||
pin = await cg.gpio_pin_expression(config[CONF_VSYNC_PIN])
|
||||
cg.add(var.set_vsync_pin(pin))
|
||||
|
||||
await display.register_display(var, config)
|
||||
if lamb := config.get(CONF_LAMBDA):
|
||||
lambda_ = await cg.process_lambda(
|
||||
lamb, [(display.DisplayRef, "it")], return_type=cg.void
|
||||
)
|
||||
cg.add(var.set_writer(lambda_))
|
@@ -1,388 +0,0 @@
|
||||
#ifdef USE_ESP32_VARIANT_ESP32S3
|
||||
#include "mipi_rgb.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esp_lcd_panel_rgb.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace mipi_rgb {
|
||||
|
||||
static const uint8_t DELAY_FLAG = 0xFF;
|
||||
static constexpr uint8_t MADCTL_MY = 0x80; // Bit 7 Bottom to top
|
||||
static constexpr uint8_t MADCTL_MX = 0x40; // Bit 6 Right to left
|
||||
static constexpr uint8_t MADCTL_MV = 0x20; // Bit 5 Swap axes
|
||||
static constexpr uint8_t MADCTL_ML = 0x10; // Bit 4 Refresh bottom to top
|
||||
static constexpr uint8_t MADCTL_BGR = 0x08; // Bit 3 Blue-Green-Red pixel order
|
||||
static constexpr uint8_t MADCTL_XFLIP = 0x02; // Mirror the display horizontally
|
||||
static constexpr uint8_t MADCTL_YFLIP = 0x01; // Mirror the display vertically
|
||||
|
||||
void MipiRgb::setup_enables_() {
|
||||
if (!this->enable_pins_.empty()) {
|
||||
for (auto *pin : this->enable_pins_) {
|
||||
pin->setup();
|
||||
pin->digital_write(true);
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->setup();
|
||||
this->reset_pin_->digital_write(true);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(5);
|
||||
this->reset_pin_->digital_write(true);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_SPI
|
||||
void MipiRgbSpi::setup() {
|
||||
this->setup_enables_();
|
||||
this->spi_setup();
|
||||
this->write_init_sequence_();
|
||||
this->common_setup_();
|
||||
}
|
||||
void MipiRgbSpi::write_command_(uint8_t value) {
|
||||
this->enable();
|
||||
if (this->dc_pin_ == nullptr) {
|
||||
this->write(value, 9);
|
||||
} else {
|
||||
this->dc_pin_->digital_write(false);
|
||||
this->write_byte(value);
|
||||
this->dc_pin_->digital_write(true);
|
||||
}
|
||||
this->disable();
|
||||
}
|
||||
|
||||
void MipiRgbSpi::write_data_(uint8_t value) {
|
||||
this->enable();
|
||||
if (this->dc_pin_ == nullptr) {
|
||||
this->write(value | 0x100, 9);
|
||||
} else {
|
||||
this->dc_pin_->digital_write(true);
|
||||
this->write_byte(value);
|
||||
}
|
||||
this->disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* this relies upon the init sequence being well-formed, which is guaranteed by the Python init code.
|
||||
*/
|
||||
|
||||
void MipiRgbSpi::write_init_sequence_() {
|
||||
size_t index = 0;
|
||||
auto &vec = this->init_sequence_;
|
||||
while (index != vec.size()) {
|
||||
if (vec.size() - index < 2) {
|
||||
this->mark_failed("Malformed init sequence");
|
||||
return;
|
||||
}
|
||||
uint8_t cmd = vec[index++];
|
||||
uint8_t x = vec[index++];
|
||||
if (x == DELAY_FLAG) {
|
||||
ESP_LOGD(TAG, "Delay %dms", cmd);
|
||||
delay(cmd);
|
||||
} else {
|
||||
uint8_t num_args = x & 0x7F;
|
||||
if (vec.size() - index < num_args) {
|
||||
this->mark_failed("Malformed init sequence");
|
||||
return;
|
||||
}
|
||||
if (cmd == SLEEP_OUT) {
|
||||
delay(120); // NOLINT
|
||||
}
|
||||
const auto *ptr = vec.data() + index;
|
||||
ESP_LOGD(TAG, "Write command %02X, length %d, byte(s) %s", cmd, num_args,
|
||||
format_hex_pretty(ptr, num_args, '.', false).c_str());
|
||||
index += num_args;
|
||||
this->write_command_(cmd);
|
||||
while (num_args-- != 0)
|
||||
this->write_data_(*ptr++);
|
||||
if (cmd == SLEEP_OUT)
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
// this->spi_teardown(); // SPI not needed after this
|
||||
this->init_sequence_.clear();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
void MipiRgbSpi::dump_config() {
|
||||
MipiRgb::dump_config();
|
||||
LOG_PIN(" CS Pin: ", this->cs_);
|
||||
LOG_PIN(" DC Pin: ", this->dc_pin_);
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" SPI Data rate: %uMHz"
|
||||
"\n Mirror X: %s"
|
||||
"\n Mirror Y: %s"
|
||||
"\n Swap X/Y: %s"
|
||||
"\n Color Order: %s",
|
||||
(unsigned) (this->data_rate_ / 1000000), YESNO(this->madctl_ & (MADCTL_XFLIP | MADCTL_MX)),
|
||||
YESNO(this->madctl_ & (MADCTL_YFLIP | MADCTL_MY | MADCTL_ML)), YESNO(this->madctl_ & MADCTL_MV),
|
||||
this->madctl_ & MADCTL_BGR ? "BGR" : "RGB");
|
||||
}
|
||||
|
||||
#endif // USE_SPI
|
||||
|
||||
void MipiRgb::setup() {
|
||||
this->setup_enables_();
|
||||
this->common_setup_();
|
||||
}
|
||||
|
||||
void MipiRgb::common_setup_() {
|
||||
esp_lcd_rgb_panel_config_t config{};
|
||||
config.flags.fb_in_psram = 1;
|
||||
config.bounce_buffer_size_px = this->width_ * 10;
|
||||
config.num_fbs = 1;
|
||||
config.timings.h_res = this->width_;
|
||||
config.timings.v_res = this->height_;
|
||||
config.timings.hsync_pulse_width = this->hsync_pulse_width_;
|
||||
config.timings.hsync_back_porch = this->hsync_back_porch_;
|
||||
config.timings.hsync_front_porch = this->hsync_front_porch_;
|
||||
config.timings.vsync_pulse_width = this->vsync_pulse_width_;
|
||||
config.timings.vsync_back_porch = this->vsync_back_porch_;
|
||||
config.timings.vsync_front_porch = this->vsync_front_porch_;
|
||||
config.timings.flags.pclk_active_neg = this->pclk_inverted_;
|
||||
config.timings.pclk_hz = this->pclk_frequency_;
|
||||
config.clk_src = LCD_CLK_SRC_PLL160M;
|
||||
size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]);
|
||||
for (size_t i = 0; i != data_pin_count; i++) {
|
||||
config.data_gpio_nums[i] = this->data_pins_[i]->get_pin();
|
||||
}
|
||||
config.data_width = data_pin_count;
|
||||
config.disp_gpio_num = -1;
|
||||
config.hsync_gpio_num = this->hsync_pin_->get_pin();
|
||||
config.vsync_gpio_num = this->vsync_pin_->get_pin();
|
||||
if (this->de_pin_) {
|
||||
config.de_gpio_num = this->de_pin_->get_pin();
|
||||
} else {
|
||||
config.de_gpio_num = -1;
|
||||
}
|
||||
config.pclk_gpio_num = this->pclk_pin_->get_pin();
|
||||
esp_err_t err = esp_lcd_new_rgb_panel(&config, &this->handle_);
|
||||
if (err == ESP_OK)
|
||||
err = esp_lcd_panel_reset(this->handle_);
|
||||
if (err == ESP_OK)
|
||||
err = esp_lcd_panel_init(this->handle_);
|
||||
if (err != ESP_OK) {
|
||||
auto msg = str_sprintf("lcd setup failed: %s", esp_err_to_name(err));
|
||||
this->mark_failed(msg.c_str());
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, "MipiRgb setup complete");
|
||||
}
|
||||
|
||||
void MipiRgb::loop() {
|
||||
if (this->handle_ != nullptr)
|
||||
esp_lcd_rgb_panel_restart(this->handle_);
|
||||
}
|
||||
|
||||
void MipiRgb::update() {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
if (this->auto_clear_enabled_) {
|
||||
this->clear();
|
||||
}
|
||||
if (this->show_test_card_) {
|
||||
this->test_card();
|
||||
} else if (this->page_ != nullptr) {
|
||||
this->page_->get_writer()(*this);
|
||||
} else if (this->writer_.has_value()) {
|
||||
(*this->writer_)(*this);
|
||||
} else {
|
||||
this->stop_poller();
|
||||
}
|
||||
if (this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_)
|
||||
return;
|
||||
ESP_LOGV(TAG, "x_low %d, y_low %d, x_high %d, y_high %d", this->x_low_, this->y_low_, this->x_high_, this->y_high_);
|
||||
int w = this->x_high_ - this->x_low_ + 1;
|
||||
int h = this->y_high_ - this->y_low_ + 1;
|
||||
this->write_to_display_(this->x_low_, this->y_low_, w, h, reinterpret_cast<const uint8_t *>(this->buffer_),
|
||||
this->x_low_, this->y_low_, this->width_ - w - this->x_low_);
|
||||
// invalidate watermarks
|
||||
this->x_low_ = this->width_;
|
||||
this->y_low_ = this->height_;
|
||||
this->x_high_ = 0;
|
||||
this->y_high_ = 0;
|
||||
}
|
||||
|
||||
void MipiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,
|
||||
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) {
|
||||
if (w <= 0 || h <= 0 || this->is_failed())
|
||||
return;
|
||||
// if color mapping is required, pass the buck.
|
||||
// note that endianness is not considered here - it is assumed to match!
|
||||
if (bitness != display::COLOR_BITNESS_565) {
|
||||
Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad);
|
||||
this->write_to_display_(x_start, y_start, w, h, reinterpret_cast<const uint8_t *>(this->buffer_), x_start, y_start,
|
||||
this->width_ - w - x_start);
|
||||
} else {
|
||||
this->write_to_display_(x_start, y_start, w, h, ptr, x_offset, y_offset, x_pad);
|
||||
}
|
||||
}
|
||||
|
||||
void MipiRgb::write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset,
|
||||
int x_pad) {
|
||||
esp_err_t err = ESP_OK;
|
||||
auto stride = (x_offset + w + x_pad) * 2;
|
||||
ptr += y_offset * stride + x_offset * 2; // skip to the first pixel
|
||||
// x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display.
|
||||
if (x_offset == 0 && x_pad == 0) {
|
||||
err = esp_lcd_panel_draw_bitmap(this->handle_, x_start, y_start, x_start + w, y_start + h, ptr);
|
||||
} else {
|
||||
// draw line by line
|
||||
for (int y = 0; y != h; y++) {
|
||||
err = esp_lcd_panel_draw_bitmap(this->handle_, x_start, y + y_start, x_start + w, y + y_start + 1, ptr);
|
||||
if (err != ESP_OK)
|
||||
break;
|
||||
ptr += stride; // next line
|
||||
}
|
||||
}
|
||||
if (err != ESP_OK)
|
||||
ESP_LOGE(TAG, "lcd_lcd_panel_draw_bitmap failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
bool MipiRgb::check_buffer_() {
|
||||
if (this->is_failed())
|
||||
return false;
|
||||
if (this->buffer_ != nullptr)
|
||||
return true;
|
||||
// this is dependent on the enum values.
|
||||
RAMAllocator<uint16_t> allocator;
|
||||
this->buffer_ = allocator.allocate(this->height_ * this->width_);
|
||||
if (this->buffer_ == nullptr) {
|
||||
this->mark_failed("Could not allocate buffer for display!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MipiRgb::draw_pixel_at(int x, int y, Color color) {
|
||||
if (!this->get_clipping().inside(x, y) || this->is_failed())
|
||||
return;
|
||||
|
||||
switch (this->rotation_) {
|
||||
case display::DISPLAY_ROTATION_0_DEGREES:
|
||||
break;
|
||||
case display::DISPLAY_ROTATION_90_DEGREES:
|
||||
std::swap(x, y);
|
||||
x = this->width_ - x - 1;
|
||||
break;
|
||||
case display::DISPLAY_ROTATION_180_DEGREES:
|
||||
x = this->width_ - x - 1;
|
||||
y = this->height_ - y - 1;
|
||||
break;
|
||||
case display::DISPLAY_ROTATION_270_DEGREES:
|
||||
std::swap(x, y);
|
||||
y = this->height_ - y - 1;
|
||||
break;
|
||||
}
|
||||
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) {
|
||||
return;
|
||||
}
|
||||
if (!this->check_buffer_())
|
||||
return;
|
||||
size_t pos = (y * this->width_) + x;
|
||||
uint8_t hi_byte = static_cast<uint8_t>(color.r & 0xF8) | (color.g >> 5);
|
||||
uint8_t lo_byte = static_cast<uint8_t>((color.g & 0x1C) << 3) | (color.b >> 3);
|
||||
uint16_t new_color = hi_byte | (lo_byte << 8); // big endian
|
||||
if (this->buffer_[pos] == new_color)
|
||||
return;
|
||||
this->buffer_[pos] = new_color;
|
||||
// low and high watermark may speed up drawing from buffer
|
||||
if (x < this->x_low_)
|
||||
this->x_low_ = x;
|
||||
if (y < this->y_low_)
|
||||
this->y_low_ = y;
|
||||
if (x > this->x_high_)
|
||||
this->x_high_ = x;
|
||||
if (y > this->y_high_)
|
||||
this->y_high_ = y;
|
||||
}
|
||||
void MipiRgb::fill(Color color) {
|
||||
if (!this->check_buffer_())
|
||||
return;
|
||||
auto *ptr_16 = reinterpret_cast<uint16_t *>(this->buffer_);
|
||||
uint8_t hi_byte = static_cast<uint8_t>(color.r & 0xF8) | (color.g >> 5);
|
||||
uint8_t lo_byte = static_cast<uint8_t>((color.g & 0x1C) << 3) | (color.b >> 3);
|
||||
uint16_t new_color = lo_byte | (hi_byte << 8); // little endian
|
||||
std::fill_n(ptr_16, this->width_ * this->height_, new_color);
|
||||
}
|
||||
|
||||
int MipiRgb::get_width() {
|
||||
switch (this->rotation_) {
|
||||
case display::DISPLAY_ROTATION_90_DEGREES:
|
||||
case display::DISPLAY_ROTATION_270_DEGREES:
|
||||
return this->get_height_internal();
|
||||
case display::DISPLAY_ROTATION_0_DEGREES:
|
||||
case display::DISPLAY_ROTATION_180_DEGREES:
|
||||
default:
|
||||
return this->get_width_internal();
|
||||
}
|
||||
}
|
||||
|
||||
int MipiRgb::get_height() {
|
||||
switch (this->rotation_) {
|
||||
case display::DISPLAY_ROTATION_0_DEGREES:
|
||||
case display::DISPLAY_ROTATION_180_DEGREES:
|
||||
return this->get_height_internal();
|
||||
case display::DISPLAY_ROTATION_90_DEGREES:
|
||||
case display::DISPLAY_ROTATION_270_DEGREES:
|
||||
default:
|
||||
return this->get_width_internal();
|
||||
}
|
||||
}
|
||||
|
||||
static std::string get_pin_name(GPIOPin *pin) {
|
||||
if (pin == nullptr)
|
||||
return "None";
|
||||
return pin->dump_summary();
|
||||
}
|
||||
|
||||
void MipiRgb::dump_pins_(uint8_t start, uint8_t end, const char *name, uint8_t offset) {
|
||||
for (uint8_t i = start; i != end; i++) {
|
||||
ESP_LOGCONFIG(TAG, " %s pin %d: %s", name, offset++, this->data_pins_[i]->dump_summary().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void MipiRgb::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"MIPI_RGB LCD"
|
||||
"\n Model: %s"
|
||||
"\n Width: %u"
|
||||
"\n Height: %u"
|
||||
"\n Rotation: %d degrees"
|
||||
"\n HSync Pulse Width: %u"
|
||||
"\n HSync Back Porch: %u"
|
||||
"\n HSync Front Porch: %u"
|
||||
"\n VSync Pulse Width: %u"
|
||||
"\n VSync Back Porch: %u"
|
||||
"\n VSync Front Porch: %u"
|
||||
"\n Invert Colors: %s"
|
||||
"\n Pixel Clock: %dMHz"
|
||||
"\n Reset Pin: %s"
|
||||
"\n DE Pin: %s"
|
||||
"\n PCLK Pin: %s"
|
||||
"\n HSYNC Pin: %s"
|
||||
"\n VSYNC Pin: %s",
|
||||
this->model_, this->width_, this->height_, this->rotation_, this->hsync_pulse_width_,
|
||||
this->hsync_back_porch_, this->hsync_front_porch_, this->vsync_pulse_width_, this->vsync_back_porch_,
|
||||
this->vsync_front_porch_, YESNO(this->invert_colors_), this->pclk_frequency_ / 1000000,
|
||||
get_pin_name(this->reset_pin_).c_str(), get_pin_name(this->de_pin_).c_str(),
|
||||
get_pin_name(this->pclk_pin_).c_str(), get_pin_name(this->hsync_pin_).c_str(),
|
||||
get_pin_name(this->vsync_pin_).c_str());
|
||||
|
||||
if (this->madctl_ & MADCTL_BGR) {
|
||||
this->dump_pins_(8, 13, "Blue", 0);
|
||||
this->dump_pins_(13, 16, "Green", 0);
|
||||
this->dump_pins_(0, 3, "Green", 3);
|
||||
this->dump_pins_(3, 8, "Red", 0);
|
||||
} else {
|
||||
this->dump_pins_(8, 13, "Red", 0);
|
||||
this->dump_pins_(13, 16, "Green", 0);
|
||||
this->dump_pins_(0, 3, "Green", 3);
|
||||
this->dump_pins_(3, 8, "Blue", 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mipi_rgb
|
||||
} // namespace esphome
|
||||
#endif // USE_ESP32_VARIANT_ESP32S3
|
@@ -1,127 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32_VARIANT_ESP32S3
|
||||
#include "esphome/core/gpio.h"
|
||||
#include "esphome/components/display/display.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#ifdef USE_SPI
|
||||
#include "esphome/components/spi/spi.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace mipi_rgb {
|
||||
|
||||
constexpr static const char *const TAG = "display.mipi_rgb";
|
||||
const uint8_t SW_RESET_CMD = 0x01;
|
||||
const uint8_t SLEEP_OUT = 0x11;
|
||||
const uint8_t SDIR_CMD = 0xC7;
|
||||
const uint8_t MADCTL_CMD = 0x36;
|
||||
const uint8_t INVERT_OFF = 0x20;
|
||||
const uint8_t INVERT_ON = 0x21;
|
||||
const uint8_t DISPLAY_ON = 0x29;
|
||||
const uint8_t CMD2_BKSEL = 0xFF;
|
||||
const uint8_t CMD2_BK0[5] = {0x77, 0x01, 0x00, 0x00, 0x10};
|
||||
|
||||
class MipiRgb : public display::Display {
|
||||
public:
|
||||
MipiRgb(int width, int height) : width_(width), height_(height) {}
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void update() override;
|
||||
void fill(Color color);
|
||||
void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,
|
||||
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override;
|
||||
void write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset,
|
||||
int x_pad);
|
||||
bool check_buffer_();
|
||||
|
||||
display::ColorOrder get_color_mode() { return this->color_mode_; }
|
||||
void set_color_mode(display::ColorOrder color_mode) { this->color_mode_ = color_mode; }
|
||||
void set_invert_colors(bool invert_colors) { this->invert_colors_ = invert_colors; }
|
||||
void set_madctl(uint8_t madctl) { this->madctl_ = madctl; }
|
||||
|
||||
void add_data_pin(InternalGPIOPin *data_pin, size_t index) { this->data_pins_[index] = data_pin; };
|
||||
void set_de_pin(InternalGPIOPin *de_pin) { this->de_pin_ = de_pin; }
|
||||
void set_pclk_pin(InternalGPIOPin *pclk_pin) { this->pclk_pin_ = pclk_pin; }
|
||||
void set_vsync_pin(InternalGPIOPin *vsync_pin) { this->vsync_pin_ = vsync_pin; }
|
||||
void set_hsync_pin(InternalGPIOPin *hsync_pin) { this->hsync_pin_ = hsync_pin; }
|
||||
void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; }
|
||||
void set_width(uint16_t width) { this->width_ = width; }
|
||||
void set_pclk_frequency(uint32_t pclk_frequency) { this->pclk_frequency_ = pclk_frequency; }
|
||||
void set_pclk_inverted(bool inverted) { this->pclk_inverted_ = inverted; }
|
||||
void set_model(const char *model) { this->model_ = model; }
|
||||
int get_width() override;
|
||||
int get_height() override;
|
||||
void set_hsync_back_porch(uint16_t hsync_back_porch) { this->hsync_back_porch_ = hsync_back_porch; }
|
||||
void set_hsync_front_porch(uint16_t hsync_front_porch) { this->hsync_front_porch_ = hsync_front_porch; }
|
||||
void set_hsync_pulse_width(uint16_t hsync_pulse_width) { this->hsync_pulse_width_ = hsync_pulse_width; }
|
||||
void set_vsync_pulse_width(uint16_t vsync_pulse_width) { this->vsync_pulse_width_ = vsync_pulse_width; }
|
||||
void set_vsync_back_porch(uint16_t vsync_back_porch) { this->vsync_back_porch_ = vsync_back_porch; }
|
||||
void set_vsync_front_porch(uint16_t vsync_front_porch) { this->vsync_front_porch_ = vsync_front_porch; }
|
||||
void set_enable_pins(std::vector<GPIOPin *> enable_pins) { this->enable_pins_ = std::move(enable_pins); }
|
||||
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
|
||||
int get_width_internal() override { return this->width_; }
|
||||
int get_height_internal() override { return this->height_; }
|
||||
void dump_pins_(uint8_t start, uint8_t end, const char *name, uint8_t offset);
|
||||
void dump_config() override;
|
||||
void draw_pixel_at(int x, int y, Color color) override;
|
||||
|
||||
// this will be horribly slow.
|
||||
protected:
|
||||
void setup_enables_();
|
||||
void common_setup_();
|
||||
InternalGPIOPin *de_pin_{nullptr};
|
||||
InternalGPIOPin *pclk_pin_{nullptr};
|
||||
InternalGPIOPin *hsync_pin_{nullptr};
|
||||
InternalGPIOPin *vsync_pin_{nullptr};
|
||||
GPIOPin *reset_pin_{nullptr};
|
||||
InternalGPIOPin *data_pins_[16] = {};
|
||||
uint16_t hsync_pulse_width_ = 10;
|
||||
uint16_t hsync_back_porch_ = 10;
|
||||
uint16_t hsync_front_porch_ = 20;
|
||||
uint16_t vsync_pulse_width_ = 10;
|
||||
uint16_t vsync_back_porch_ = 10;
|
||||
uint16_t vsync_front_porch_ = 10;
|
||||
uint32_t pclk_frequency_ = 16 * 1000 * 1000;
|
||||
bool pclk_inverted_{true};
|
||||
uint8_t madctl_{};
|
||||
const char *model_{"Unknown"};
|
||||
bool invert_colors_{};
|
||||
display::ColorOrder color_mode_{display::COLOR_ORDER_BGR};
|
||||
size_t width_;
|
||||
size_t height_;
|
||||
uint16_t *buffer_{nullptr};
|
||||
std::vector<GPIOPin *> enable_pins_{};
|
||||
uint16_t x_low_{1};
|
||||
uint16_t y_low_{1};
|
||||
uint16_t x_high_{0};
|
||||
uint16_t y_high_{0};
|
||||
|
||||
esp_lcd_panel_handle_t handle_{};
|
||||
};
|
||||
|
||||
#ifdef USE_SPI
|
||||
class MipiRgbSpi : public MipiRgb,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
|
||||
spi::DATA_RATE_1MHZ> {
|
||||
public:
|
||||
MipiRgbSpi(int width, int height) : MipiRgb(width, height) {}
|
||||
|
||||
void set_init_sequence(const std::vector<uint8_t> &init_sequence) { this->init_sequence_ = init_sequence; }
|
||||
void set_dc_pin(GPIOPin *dc_pin) { this->dc_pin_ = dc_pin; }
|
||||
void setup() override;
|
||||
|
||||
protected:
|
||||
void write_command_(uint8_t value);
|
||||
void write_data_(uint8_t value);
|
||||
void write_init_sequence_();
|
||||
void dump_config();
|
||||
|
||||
GPIOPin *dc_pin_{nullptr};
|
||||
std::vector<uint8_t> init_sequence_;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace mipi_rgb
|
||||
} // namespace esphome
|
||||
#endif
|
@@ -1,24 +0,0 @@
|
||||
from .st7701s import st7701s
|
||||
|
||||
st7701s.extend(
|
||||
"GUITION-4848S040",
|
||||
width=480,
|
||||
height=480,
|
||||
data_rate="2MHz",
|
||||
cs_pin=39,
|
||||
de_pin=18,
|
||||
hsync_pin=16,
|
||||
vsync_pin=17,
|
||||
pclk_pin=21,
|
||||
pclk_frequency="12MHz",
|
||||
pixel_mode="18bit",
|
||||
mirror_x=True,
|
||||
mirror_y=True,
|
||||
data_pins={
|
||||
"red": [11, 12, 13, 14, 0],
|
||||
"green": [8, 20, 3, 46, 9, 10],
|
||||
"blue": [4, 5, 6, 7, 15],
|
||||
},
|
||||
# Additional configuration for Guition 4848S040, 16 bit bus config
|
||||
add_init_sequence=((0xCD, 0x00),),
|
||||
)
|
@@ -1,228 +0,0 @@
|
||||
from esphome.config_validation import UNDEFINED
|
||||
|
||||
from .st7701s import ST7701S
|
||||
|
||||
# fmt: off
|
||||
ST7701S(
|
||||
"T-PANEL-S3",
|
||||
width=480,
|
||||
height=480,
|
||||
color_order="BGR",
|
||||
invert_colors=False,
|
||||
swap_xy=UNDEFINED,
|
||||
spi_mode="MODE3",
|
||||
cs_pin={"xl9535": None, "number": 17},
|
||||
reset_pin={"xl9535": None, "number": 5},
|
||||
hsync_pin=39,
|
||||
vsync_pin=40,
|
||||
pclk_pin=41,
|
||||
data_pins={
|
||||
"red": [12, 13, 42, 46, 45],
|
||||
"green": [6, 7, 8, 9, 10, 11],
|
||||
"blue": [1, 2, 3, 4, 5],
|
||||
},
|
||||
hsync_front_porch=20,
|
||||
hsync_back_porch=0,
|
||||
hsync_pulse_width=2,
|
||||
vsync_front_porch=30,
|
||||
vsync_back_porch=1,
|
||||
vsync_pulse_width=8,
|
||||
pclk_frequency="6MHz",
|
||||
pclk_inverted=False,
|
||||
initsequence=(
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x13), (0xEF, 0x08), (0xFF, 0x77, 0x01, 0x00, 0x00, 0x10),
|
||||
(0xC0, 0x3B, 0x00), (0xC1, 0x0B, 0x02), (0xC2, 0x30, 0x02, 0x37), (0xCC, 0x10),
|
||||
(0xB0, 0x00, 0x0F, 0x16, 0x0E, 0x11, 0x07, 0x09, 0x09, 0x08, 0x23, 0x05, 0x11, 0x0F, 0x28, 0x2D, 0x18),
|
||||
(0xB1, 0x00, 0x0F, 0x16, 0x0E, 0x11, 0x07, 0x09, 0x08, 0x09, 0x23, 0x05, 0x11, 0x0F, 0x28, 0x2D, 0x18),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x11),
|
||||
(0xB0, 0x4D), (0xB1, 0x33), (0xB2, 0x87), (0xB5, 0x4B), (0xB7, 0x8C), (0xB8, 0x20), (0xC1, 0x78),
|
||||
(0xC2, 0x78), (0xD0, 0x88), (0xE0, 0x00, 0x00, 0x02),
|
||||
(0xE1, 0x02, 0xF0, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x44, 0x44),
|
||||
(0xE2, 0x10, 0x10, 0x40, 0x40, 0xF2, 0xF0, 0x00, 0x00, 0xF2, 0xF0, 0x00, 0x00),
|
||||
(0xE3, 0x00, 0x00, 0x11, 0x11), (0xE4, 0x44, 0x44),
|
||||
(0xE5, 0x07, 0xEF, 0xF0, 0xF0, 0x09, 0xF1, 0xF0, 0xF0, 0x03, 0xF3, 0xF0, 0xF0, 0x05, 0xED, 0xF0, 0xF0),
|
||||
(0xE6, 0x00, 0x00, 0x11, 0x11), (0xE7, 0x44, 0x44),
|
||||
(0xE8, 0x08, 0xF0, 0xF0, 0xF0, 0x0A, 0xF2, 0xF0, 0xF0, 0x04, 0xF4, 0xF0, 0xF0, 0x06, 0xEE, 0xF0, 0xF0),
|
||||
(0xEB, 0x00, 0x00, 0xE4, 0xE4, 0x44, 0x88, 0x40),
|
||||
(0xEC, 0x78, 0x00),
|
||||
(0xED, 0x20, 0xF9, 0x87, 0x76, 0x65, 0x54, 0x4F, 0xFF, 0xFF, 0xF4, 0x45, 0x56, 0x67, 0x78, 0x9F, 0x02),
|
||||
(0xEF, 0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x10),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
t_rgb = ST7701S(
|
||||
"T-RGB-2.1",
|
||||
width=480,
|
||||
height=480,
|
||||
color_order="BGR",
|
||||
pixel_mode="18bit",
|
||||
invert_colors=False,
|
||||
swap_xy=UNDEFINED,
|
||||
spi_mode="MODE3",
|
||||
cs_pin={"xl9535": None, "number": 3},
|
||||
de_pin=45,
|
||||
hsync_pin=47,
|
||||
vsync_pin=41,
|
||||
pclk_pin=42,
|
||||
data_pins={
|
||||
"red": [7, 6, 5, 3, 2],
|
||||
"green": [14, 13, 12, 11, 10, 9],
|
||||
"blue": [21, 18, 17, 16, 15],
|
||||
},
|
||||
hsync_front_porch=50,
|
||||
hsync_pulse_width=1,
|
||||
hsync_back_porch=30,
|
||||
vsync_front_porch=20,
|
||||
vsync_pulse_width=1,
|
||||
vsync_back_porch=30,
|
||||
pclk_frequency="12MHz",
|
||||
pclk_inverted=False,
|
||||
initsequence=(
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x10),
|
||||
|
||||
(0xC0, 0x3B, 0x00),
|
||||
(0xC1, 0x0B, 0x02),
|
||||
(0xC2, 0x07, 0x02),
|
||||
(0xCC, 0x10),
|
||||
(0xCD, 0x08),
|
||||
|
||||
(0xB0,
|
||||
0x00, 0x11, 0x16, 0x0e,
|
||||
0x11, 0x06, 0x05, 0x09,
|
||||
0x08, 0x21, 0x06, 0x13,
|
||||
0x10, 0x29, 0x31, 0x18),
|
||||
|
||||
(0xB1,
|
||||
0x00, 0x11, 0x16, 0x0e,
|
||||
0x11, 0x07, 0x05, 0x09,
|
||||
0x09, 0x21, 0x05, 0x13,
|
||||
0x11, 0x2a, 0x31, 0x18),
|
||||
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x11),
|
||||
|
||||
(0xB0, 0x6D),
|
||||
(0xB1, 0x37),
|
||||
(0xB2, 0x81),
|
||||
(0xB3, 0x80),
|
||||
(0xB5, 0x43),
|
||||
(0xB7, 0x85),
|
||||
(0xB8, 0x20),
|
||||
|
||||
(0xC1, 0x78),
|
||||
(0xC2, 0x78),
|
||||
(0xC3, 0x8C),
|
||||
|
||||
(0xD0, 0x88),
|
||||
|
||||
(0xE0, 0x00, 0x00, 0x02),
|
||||
(0xE1,
|
||||
0x03, 0xA0, 0x00, 0x00,
|
||||
0x04, 0xA0, 0x00, 0x00,
|
||||
0x00, 0x20, 0x20),
|
||||
|
||||
(0xE2,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00),
|
||||
|
||||
(0xE3, 0x00, 0x00, 0x11, 0x00),
|
||||
(0xE4, 0x22, 0x00),
|
||||
|
||||
(0xE5,
|
||||
0x05, 0xEC, 0xA0, 0xA0,
|
||||
0x07, 0xEE, 0xA0, 0xA0,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00),
|
||||
|
||||
(0xE6, 0x00, 0x00, 0x11, 0x00),
|
||||
(0xE7, 0x22, 0x00),
|
||||
|
||||
(0xE8,
|
||||
0x06, 0xED, 0xA0, 0xA0,
|
||||
0x08, 0xEF, 0xA0, 0xA0,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00),
|
||||
|
||||
(0xEB, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x10),
|
||||
|
||||
(0xED,
|
||||
0xFF, 0xFF, 0xFF, 0xBA,
|
||||
0x0A, 0xBF, 0x45, 0xFF,
|
||||
0xFF, 0x54, 0xFB, 0xA0,
|
||||
0xAB, 0xFF, 0xFF, 0xFF),
|
||||
|
||||
(0xEF, 0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F),
|
||||
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x13),
|
||||
(0xEF, 0x08),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x10)
|
||||
)
|
||||
|
||||
)
|
||||
t_rgb.extend(
|
||||
"T-RGB-2.8",
|
||||
initsequence=(
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x13),
|
||||
(0xEF, 0x08),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x10),
|
||||
(0xC0, 0x3B, 0x00),
|
||||
(0xC1, 0x10, 0x0C),
|
||||
(0xC2, 0x07, 0x0A),
|
||||
(0xC7, 0x00),
|
||||
(0xC7, 0x10),
|
||||
(0xCD, 0x08),
|
||||
(0xB0,
|
||||
0x05, 0x12, 0x98, 0x0e, 0x0F,
|
||||
0x07, 0x07, 0x09, 0x09, 0x23,
|
||||
0x05, 0x52, 0x0F, 0x67, 0x2C, 0x11),
|
||||
(0xB1,
|
||||
0x0B, 0x11, 0x97, 0x0C, 0x12,
|
||||
0x06, 0x06, 0x08, 0x08, 0x22,
|
||||
0x03, 0x51, 0x11, 0x66, 0x2B, 0x0F),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x11),
|
||||
(0xB0, 0x5D),
|
||||
(0xB1, 0x2D),
|
||||
(0xB2, 0x81),
|
||||
(0xB3, 0x80),
|
||||
(0xB5, 0x4E),
|
||||
(0xB7, 0x85),
|
||||
(0xB8, 0x20),
|
||||
(0xC1, 0x78),
|
||||
(0xC2, 0x78),
|
||||
(0xD0, 0x88),
|
||||
(0xE0, 0x00, 0x00, 0x02),
|
||||
(0xE1,
|
||||
0x06, 0x30, 0x08, 0x30, 0x05,
|
||||
0x30, 0x07, 0x30, 0x00, 0x33,
|
||||
0x33),
|
||||
(0xE2,
|
||||
0x11, 0x11, 0x33, 0x33, 0xf4,
|
||||
0x00, 0x00, 0x00, 0xf4, 0x00,
|
||||
0x00, 0x00),
|
||||
(0xE3, 0x00, 0x00, 0x11, 0x11),
|
||||
(0xE4, 0x44, 0x44),
|
||||
(0xE5,
|
||||
0x0d, 0xf5, 0x30, 0xf0, 0x0f,
|
||||
0xf7, 0x30, 0xf0, 0x09, 0xf1,
|
||||
0x30, 0xf0, 0x0b, 0xf3, 0x30, 0xf0),
|
||||
(0xE6, 0x00, 0x00, 0x11, 0x11),
|
||||
(0xE7, 0x44, 0x44),
|
||||
(0xE8,
|
||||
0x0c, 0xf4, 0x30, 0xf0,
|
||||
0x0e, 0xf6, 0x30, 0xf0,
|
||||
0x08, 0xf0, 0x30, 0xf0,
|
||||
0x0a, 0xf2, 0x30, 0xf0),
|
||||
(0xe9, 0x36),
|
||||
(0xEB, 0x00, 0x01, 0xe4, 0xe4, 0x44, 0x88, 0x40),
|
||||
(0xED,
|
||||
0xff, 0x10, 0xaf, 0x76,
|
||||
0x54, 0x2b, 0xcf, 0xff,
|
||||
0xff, 0xfc, 0xb2, 0x45,
|
||||
0x67, 0xfa, 0x01, 0xff),
|
||||
(0xEF, 0x08, 0x08, 0x08, 0x45, 0x3f, 0x54),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x10),
|
||||
)
|
||||
)
|
||||
|
@@ -1,9 +0,0 @@
|
||||
from esphome.components.mipi import DriverChip
|
||||
from esphome.config_validation import UNDEFINED
|
||||
|
||||
# A driver chip for Raspberry Pi MIPI RGB displays. These require no init sequence
|
||||
DriverChip(
|
||||
"RPI",
|
||||
swap_xy=UNDEFINED,
|
||||
initsequence=(),
|
||||
)
|
@@ -1,214 +0,0 @@
|
||||
from esphome.components.mipi import (
|
||||
MADCTL,
|
||||
MADCTL_ML,
|
||||
MADCTL_XFLIP,
|
||||
MODE_BGR,
|
||||
DriverChip,
|
||||
)
|
||||
from esphome.config_validation import UNDEFINED
|
||||
from esphome.const import CONF_COLOR_ORDER, CONF_HEIGHT, CONF_MIRROR_X, CONF_MIRROR_Y
|
||||
|
||||
SDIR_CMD = 0xC7
|
||||
|
||||
|
||||
class ST7701S(DriverChip):
|
||||
# The ST7701s does not use the standard MADCTL bits for x/y mirroring
|
||||
def add_madctl(self, sequence: list, config: dict):
|
||||
transform = self.get_transform(config)
|
||||
madctl = 0x00
|
||||
if config[CONF_COLOR_ORDER] == MODE_BGR:
|
||||
madctl |= 0x08
|
||||
if transform.get(CONF_MIRROR_Y):
|
||||
madctl |= MADCTL_ML
|
||||
sequence.append((MADCTL, madctl))
|
||||
sdir = 0
|
||||
if transform.get(CONF_MIRROR_X):
|
||||
sdir |= 0x04
|
||||
madctl |= MADCTL_XFLIP
|
||||
sequence.append((SDIR_CMD, sdir))
|
||||
return madctl
|
||||
|
||||
@property
|
||||
def transforms(self) -> set[str]:
|
||||
"""
|
||||
The ST7701 never supports axis swapping, and mirroring the y-axis only works for full height.
|
||||
"""
|
||||
if self.get_default(CONF_HEIGHT) != 864:
|
||||
return {CONF_MIRROR_X}
|
||||
return {CONF_MIRROR_X, CONF_MIRROR_Y}
|
||||
|
||||
|
||||
# fmt: off
|
||||
st7701s = ST7701S(
|
||||
"ST7701S",
|
||||
width=480,
|
||||
height=864,
|
||||
swap_xy=UNDEFINED,
|
||||
hsync_front_porch=20,
|
||||
hsync_back_porch=10,
|
||||
hsync_pulse_width=10,
|
||||
vsync_front_porch=10,
|
||||
vsync_back_porch=10,
|
||||
vsync_pulse_width=10,
|
||||
pclk_frequency="16MHz",
|
||||
pclk_inverted=True,
|
||||
initsequence=(
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x10), # Page 0
|
||||
(0xC0, 0x3B, 0x00), (0xC1, 0x0D, 0x02), (0xC2, 0x31, 0x05),
|
||||
(0xB0, 0x00, 0x11, 0x18, 0x0E, 0x11, 0x06, 0x07, 0x08, 0x07, 0x22, 0x04, 0x12, 0x0F, 0xAA, 0x31, 0x18,),
|
||||
(0xB1, 0x00, 0x11, 0x19, 0x0E, 0x12, 0x07, 0x08, 0x08, 0x08, 0x22, 0x04, 0x11, 0x11, 0xA9, 0x32, 0x18,),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x11), # page 1
|
||||
(0xB0, 0x60), (0xB1, 0x32), (0xB2, 0x07), (0xB3, 0x80), (0xB5, 0x49), (0xB7, 0x85), (0xB8, 0x21), (0xC1, 0x78),
|
||||
(0xC2, 0x78), (0xE0, 0x00, 0x1B, 0x02),
|
||||
(0xE1, 0x08, 0xA0, 0x00, 0x00, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x44, 0x44),
|
||||
(0xE2, 0x11, 0x11, 0x44, 0x44, 0xED, 0xA0, 0x00, 0x00, 0xEC, 0xA0, 0x00, 0x00),
|
||||
(0xE3, 0x00, 0x00, 0x11, 0x11),
|
||||
(0xE4, 0x44, 0x44),
|
||||
(0xE5, 0x0A, 0xE9, 0xD8, 0xA0, 0x0C, 0xEB, 0xD8, 0xA0, 0x0E, 0xED, 0xD8, 0xA0, 0x10, 0xEF, 0xD8, 0xA0,),
|
||||
(0xE6, 0x00, 0x00, 0x11, 0x11), (0xE7, 0x44, 0x44),
|
||||
(0xE8, 0x09, 0xE8, 0xD8, 0xA0, 0x0B, 0xEA, 0xD8, 0xA0, 0x0D, 0xEC, 0xD8, 0xA0, 0x0F, 0xEE, 0xD8, 0xA0,),
|
||||
(0xEB, 0x02, 0x00, 0xE4, 0xE4, 0x88, 0x00, 0x40), (0xEC, 0x3C, 0x00),
|
||||
(0xED, 0xAB, 0x89, 0x76, 0x54, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x45, 0x67, 0x98, 0xBA,),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x13), # Page 3
|
||||
(0xE5, 0xE4),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x10), # Page 0
|
||||
(0xCD, 0x08),
|
||||
)
|
||||
)
|
||||
|
||||
st7701s.extend(
|
||||
"MAKERFABS-4",
|
||||
width=480,
|
||||
height=480,
|
||||
color_order="RGB",
|
||||
invert_colors=True,
|
||||
pixel_mode="18bit",
|
||||
cs_pin=1,
|
||||
de_pin={
|
||||
"number": 45,
|
||||
"ignore_strapping_warning": True
|
||||
},
|
||||
hsync_pin=5,
|
||||
vsync_pin=4,
|
||||
pclk_pin=21,
|
||||
data_pins={
|
||||
"red": [39, 40, 41, 42, 2],
|
||||
"green": [0, 9, 14, 47, 48, 3],
|
||||
"blue": [6, 7, 15, 16, 8]
|
||||
}
|
||||
)
|
||||
|
||||
st7701s.extend(
|
||||
"SEEED-INDICATOR-D1",
|
||||
width=480,
|
||||
height=480,
|
||||
mirror_x=True,
|
||||
mirror_y=True,
|
||||
invert_colors=True,
|
||||
pixel_mode="18bit",
|
||||
spi_mode="MODE3",
|
||||
data_rate="2MHz",
|
||||
hsync_front_porch=10,
|
||||
hsync_pulse_width=8,
|
||||
hsync_back_porch=50,
|
||||
vsync_front_porch=10,
|
||||
vsync_pulse_width=8,
|
||||
vsync_back_porch=20,
|
||||
cs_pin={"pca9554": None, "number": 4},
|
||||
de_pin=18,
|
||||
hsync_pin=16,
|
||||
vsync_pin=17,
|
||||
pclk_pin=21,
|
||||
pclk_inverted=False,
|
||||
data_pins={
|
||||
"red": [4, 3, 2, 1, 0],
|
||||
"green": [10, 9, 8, 7, 6, 5],
|
||||
"blue": [15, 14, 13, 12, 11]
|
||||
},
|
||||
)
|
||||
|
||||
st7701s.extend(
|
||||
"UEDX48480021-MD80ET",
|
||||
width=480,
|
||||
height=480,
|
||||
pixel_mode="18bit",
|
||||
cs_pin=18,
|
||||
reset_pin=8,
|
||||
de_pin=17,
|
||||
vsync_pin={"number": 3, "ignore_strapping_warning": True},
|
||||
hsync_pin={"number": 46, "ignore_strapping_warning": True},
|
||||
pclk_pin=9,
|
||||
data_pins={
|
||||
"red": [40, 41, 42, 2, 1],
|
||||
"green": [21, 47, 48, 45, 38, 39],
|
||||
"blue": [10, 11, {"number": 12, "allow_other_uses": True}, {"number": 13, "allow_other_uses": True}, 14]
|
||||
},
|
||||
initsequence=(
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x13), (0xEF, 0x08), (0xFF, 0x77, 0x01, 0x00, 0x00, 0x10),
|
||||
(0xC0, 0x3B, 0x00), (0xC1, 0x0B, 0x02), (0xC2, 0x07, 0x02), (0xC7, 0x00), (0xCC, 0x10), (0xCD, 0x08),
|
||||
(0xB0, 0x00, 0x11, 0x16, 0x0E, 0x11, 0x06, 0x05, 0x09, 0x08, 0x21, 0x06, 0x13, 0x10, 0x29, 0x31, 0x18),
|
||||
(0xB1, 0x00, 0x11, 0x16, 0x0E, 0x11, 0x07, 0x05, 0x09, 0x09, 0x21, 0x05, 0x13, 0x11, 0x2A, 0x31, 0x18),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x11),
|
||||
(0xB0, 0x6D), (0xB1, 0x37), (0xB2, 0x8B), (0xB3, 0x80), (0xB5, 0x43), (0xB7, 0x85),
|
||||
(0xB8, 0x20), (0xC0, 0x09), (0xC1, 0x78), (0xC2, 0x78), (0xD0, 0x88),
|
||||
(0xE0, 0x00, 0x00, 0x02),
|
||||
(0xE1, 0x03, 0xA0, 0x00, 0x00, 0x04, 0xA0, 0x00, 0x00, 0x00, 0x20, 0x20),
|
||||
(0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
|
||||
(0xE3, 0x00, 0x00, 0x11, 0x00),
|
||||
(0xE4, 0x22, 0x00),
|
||||
(0xE5, 0x05, 0xEC, 0xF6, 0xCA, 0x07, 0xEE, 0xF6, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
|
||||
(0xE6, 0x00, 0x00, 0x11, 0x00),
|
||||
(0xE7, 0x22, 0x00),
|
||||
(0xE8, 0x06, 0xED, 0xF6, 0xCA, 0x08, 0xEF, 0xF6, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
|
||||
(0xE9, 0x36, 0x00),
|
||||
(0xEB, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00),
|
||||
(0xED, 0xFF, 0xFF, 0xFF, 0xBA, 0x0A, 0xFF, 0x45, 0xFF, 0xFF, 0x54, 0xFF, 0xA0, 0xAB, 0xFF, 0xFF, 0xFF),
|
||||
(0xEF, 0x08, 0x08, 0x08, 0x45, 0x3F, 0x54),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x13), (0xE8, 0x00, 0x0E), (0xFF, 0x77, 0x01, 0x00, 0x00, 0x00),
|
||||
(0x11, 0x00), (0xFF, 0x77, 0x01, 0x00, 0x00, 0x13), (0xE8, 0x00, 0x0C),
|
||||
(0xE8, 0x00, 0x00), (0xFF, 0x77, 0x01, 0x00, 0x00, 0x00)
|
||||
)
|
||||
)
|
||||
|
||||
st7701s.extend(
|
||||
"ZX2D10GE01R-V4848",
|
||||
width=480,
|
||||
height=480,
|
||||
pixel_mode="18bit",
|
||||
cs_pin=21,
|
||||
de_pin=39,
|
||||
vsync_pin=48,
|
||||
hsync_pin=40,
|
||||
pclk_pin={"number": 45, "ignore_strapping_warning": True},
|
||||
pclk_frequency="15MHz",
|
||||
pclk_inverted=True,
|
||||
hsync_pulse_width=10,
|
||||
hsync_back_porch=10,
|
||||
hsync_front_porch=10,
|
||||
vsync_pulse_width=2,
|
||||
vsync_back_porch=12,
|
||||
vsync_front_porch=14,
|
||||
data_pins={
|
||||
"red": [10, 16, 9, 15, 46],
|
||||
"green": [8, 13, 18, 12, 11, 17],
|
||||
"blue": [{"number": 47, "allow_other_uses": True}, {"number": 41, "allow_other_uses": True}, 0, 42, 14]
|
||||
},
|
||||
initsequence=(
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x13), (0xEF, 0x08), (0xFF, 0x77, 0x01, 0x00, 0x00, 0x10), (0xC0, 0x3B, 0x00),
|
||||
(0xC1, 0x0B, 0x02), (0xC2, 0x07, 0x02), (0xCC, 0x10), (0xCD, 0x08),
|
||||
(0xB0, 0x00, 0x11, 0x16, 0x0e, 0x11, 0x06, 0x05, 0x09, 0x08, 0x21, 0x06, 0x13, 0x10, 0x29, 0x31, 0x18),
|
||||
(0xB1, 0x00, 0x11, 0x16, 0x0e, 0x11, 0x07, 0x05, 0x09, 0x09, 0x21, 0x05, 0x13, 0x11, 0x2a, 0x31, 0x18),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x11), (0xB0, 0x6d), (0xB1, 0x37), (0xB2, 0x81), (0xB3, 0x80), (0xB5, 0x43),
|
||||
(0xB7, 0x85), (0xB8, 0x20), (0xC1, 0x78), (0xC2, 0x78), (0xD0, 0x88), (0xE0, 0x00, 0x00, 0x02),
|
||||
(0xE1, 0x03, 0xA0, 0x00, 0x00, 0x04, 0xA0, 0x00, 0x00, 0x00, 0x20, 0x20),
|
||||
(0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
|
||||
(0xE3, 0x00, 0x00, 0x11, 0x00), (0xE4, 0x22, 0x00),
|
||||
(0xE5, 0x05, 0xEC, 0xA0, 0xA0, 0x07, 0xEE, 0xA0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
|
||||
(0xE6, 0x00, 0x00, 0x11, 0x00), (0xE7, 0x22, 0x00),
|
||||
(0xE8, 0x06, 0xED, 0xA0, 0xA0, 0x08, 0xEF, 0xA0, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
|
||||
(0xEB, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00),
|
||||
(0xED, 0xFF, 0xFF, 0xFF, 0xBA, 0x0A, 0xBF, 0x45, 0xFF, 0xFF, 0x54, 0xFB, 0xA0, 0xAB, 0xFF, 0xFF, 0xFF),
|
||||
(0xEF, 0x10, 0x0D, 0x04, 0x08, 0x3F, 0x1F),
|
||||
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x13), (0xEF, 0x08), (0xFF, 0x77, 0x01, 0x00, 0x00, 0x00)
|
||||
)
|
||||
)
|
@@ -1,65 +0,0 @@
|
||||
from esphome.components.mipi import DriverChip
|
||||
from esphome.config_validation import UNDEFINED
|
||||
|
||||
from .st7701s import st7701s
|
||||
|
||||
wave_4_3 = DriverChip(
|
||||
"ESP32-S3-TOUCH-LCD-4.3",
|
||||
swap_xy=UNDEFINED,
|
||||
initsequence=(),
|
||||
color_order="RGB",
|
||||
width=800,
|
||||
height=480,
|
||||
pclk_frequency="16MHz",
|
||||
reset_pin={"ch422g": None, "number": 3},
|
||||
enable_pin={"ch422g": None, "number": 2},
|
||||
de_pin=5,
|
||||
hsync_pin={"number": 46, "ignore_strapping_warning": True},
|
||||
vsync_pin={"number": 3, "ignore_strapping_warning": True},
|
||||
pclk_pin=7,
|
||||
pclk_inverted=True,
|
||||
hsync_front_porch=210,
|
||||
hsync_pulse_width=30,
|
||||
hsync_back_porch=30,
|
||||
vsync_front_porch=4,
|
||||
vsync_pulse_width=4,
|
||||
vsync_back_porch=4,
|
||||
data_pins={
|
||||
"red": [1, 2, 42, 41, 40],
|
||||
"green": [39, 0, 45, 48, 47, 21],
|
||||
"blue": [14, 38, 18, 17, 10],
|
||||
},
|
||||
)
|
||||
wave_4_3.extend(
|
||||
"ESP32-S3-TOUCH-LCD-7-800X480",
|
||||
enable_pin=[{"ch422g": None, "number": 2}, {"ch422g": None, "number": 6}],
|
||||
hsync_back_porch=8,
|
||||
hsync_front_porch=8,
|
||||
hsync_pulse_width=4,
|
||||
vsync_back_porch=16,
|
||||
vsync_front_porch=16,
|
||||
vsync_pulse_width=4,
|
||||
)
|
||||
|
||||
st7701s.extend(
|
||||
"WAVESHARE-4-480x480",
|
||||
data_rate="2MHz",
|
||||
spi_mode="MODE3",
|
||||
color_order="BGR",
|
||||
pixel_mode="18bit",
|
||||
width=480,
|
||||
height=480,
|
||||
invert_colors=True,
|
||||
cs_pin=42,
|
||||
de_pin=40,
|
||||
hsync_pin=38,
|
||||
vsync_pin=39,
|
||||
pclk_pin=41,
|
||||
pclk_frequency="12MHz",
|
||||
pclk_inverted=False,
|
||||
data_pins={
|
||||
"red": [46, 3, 8, 18, 17],
|
||||
"green": [14, 13, 12, 11, 10, 9],
|
||||
"blue": [5, 45, 48, 47, 21],
|
||||
},
|
||||
)
|
@@ -27,8 +27,7 @@ DriverChip(
|
||||
bus_mode=TYPE_QUAD,
|
||||
brightness=0xD0,
|
||||
color_order=MODE_RGB,
|
||||
no_slpout=True, # SLPOUT is in the init sequence, early
|
||||
initsequence=(SLPOUT,),
|
||||
initsequence=(SLPOUT,), # Requires early SLPOUT
|
||||
)
|
||||
|
||||
DriverChip(
|
||||
@@ -96,7 +95,6 @@ CO5300 = DriverChip(
|
||||
brightness=0xD0,
|
||||
color_order=MODE_RGB,
|
||||
bus_mode=TYPE_QUAD,
|
||||
no_slpout=True,
|
||||
initsequence=(
|
||||
(SLPOUT,), # Requires early SLPOUT
|
||||
(PAGESEL, 0x00),
|
||||
|
@@ -491,7 +491,7 @@ bool MQTTClientComponent::publish(const std::string &topic, const std::string &p
|
||||
|
||||
bool MQTTClientComponent::publish(const std::string &topic, const char *payload, size_t payload_length, uint8_t qos,
|
||||
bool retain) {
|
||||
return publish({.topic = topic, .payload = std::string(payload, payload_length), .qos = qos, .retain = retain});
|
||||
return publish({.topic = topic, .payload = payload, .qos = qos, .retain = retain});
|
||||
}
|
||||
|
||||
bool MQTTClientComponent::publish(const MQTTMessage &message) {
|
||||
|
@@ -19,14 +19,13 @@ void MS5611Component::setup() {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
this->set_timeout(100, [this]() {
|
||||
for (uint8_t offset = 0; offset < 6; offset++) {
|
||||
if (!this->read_byte_16(MS5611_CMD_READ_PROM + (offset * 2), &this->prom_[offset])) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
delay(100); // NOLINT
|
||||
for (uint8_t offset = 0; offset < 6; offset++) {
|
||||
if (!this->read_byte_16(MS5611_CMD_READ_PROM + (offset * 2), &this->prom_[offset])) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
void MS5611Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "MS5611:");
|
||||
|
@@ -11,10 +11,7 @@ static const char *const TAG = "nextion.upload";
|
||||
bool Nextion::upload_end_(bool successful) {
|
||||
if (successful) {
|
||||
ESP_LOGD(TAG, "Upload successful");
|
||||
for (uint8_t i = 0; i <= 5; i++) {
|
||||
delay(1000); // NOLINT
|
||||
App.feed_wdt(); // Feed the watchdog timer.
|
||||
}
|
||||
delay(1500); // NOLINT
|
||||
App.safe_reboot();
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Upload failed");
|
||||
|
@@ -11,6 +11,8 @@
|
||||
#include <openthread/instance.h>
|
||||
#include <openthread/logging.h>
|
||||
#include <openthread/netdata.h>
|
||||
#include <openthread/srp_client.h>
|
||||
#include <openthread/srp_client_buffers.h>
|
||||
#include <openthread/tasklet.h>
|
||||
|
||||
#include <cstring>
|
||||
@@ -75,14 +77,8 @@ std::optional<otIp6Address> OpenThreadComponent::get_omr_address_(InstanceLock &
|
||||
return {};
|
||||
}
|
||||
|
||||
void OpenThreadComponent::defer_factory_reset_external_callback() {
|
||||
ESP_LOGD(TAG, "Defer factory_reset_external_callback_");
|
||||
this->defer([this]() { this->factory_reset_external_callback_(); });
|
||||
}
|
||||
|
||||
void OpenThreadSrpComponent::srp_callback(otError err, const otSrpClientHostInfo *host_info,
|
||||
const otSrpClientService *services,
|
||||
const otSrpClientService *removed_services, void *context) {
|
||||
void srp_callback(otError err, const otSrpClientHostInfo *host_info, const otSrpClientService *services,
|
||||
const otSrpClientService *removed_services, void *context) {
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "SRP client reported an error: %s", otThreadErrorToString(err));
|
||||
for (const otSrpClientHostInfo *host = host_info; host; host = nullptr) {
|
||||
@@ -94,30 +90,16 @@ void OpenThreadSrpComponent::srp_callback(otError err, const otSrpClientHostInfo
|
||||
}
|
||||
}
|
||||
|
||||
void OpenThreadSrpComponent::srp_start_callback(const otSockAddr *server_socket_address, void *context) {
|
||||
void srp_start_callback(const otSockAddr *server_socket_address, void *context) {
|
||||
ESP_LOGI(TAG, "SRP client has started");
|
||||
}
|
||||
|
||||
void OpenThreadSrpComponent::srp_factory_reset_callback(otError err, const otSrpClientHostInfo *host_info,
|
||||
const otSrpClientService *services,
|
||||
const otSrpClientService *removed_services, void *context) {
|
||||
OpenThreadComponent *obj = (OpenThreadComponent *) context;
|
||||
if (err == OT_ERROR_NONE && removed_services != NULL && host_info != NULL &&
|
||||
host_info->mState == OT_SRP_CLIENT_ITEM_STATE_REMOVED) {
|
||||
ESP_LOGD(TAG, "Successful Removal SRP Host and Services");
|
||||
} else if (err != OT_ERROR_NONE) {
|
||||
// Handle other SRP client events or errors
|
||||
ESP_LOGW(TAG, "SRP client event/error: %s", otThreadErrorToString(err));
|
||||
}
|
||||
obj->defer_factory_reset_external_callback();
|
||||
}
|
||||
|
||||
void OpenThreadSrpComponent::setup() {
|
||||
otError error;
|
||||
InstanceLock lock = InstanceLock::acquire();
|
||||
otInstance *instance = lock.get_instance();
|
||||
|
||||
otSrpClientSetCallback(instance, OpenThreadSrpComponent::srp_callback, nullptr);
|
||||
otSrpClientSetCallback(instance, srp_callback, nullptr);
|
||||
|
||||
// set the host name
|
||||
uint16_t size;
|
||||
@@ -197,8 +179,7 @@ void OpenThreadSrpComponent::setup() {
|
||||
ESP_LOGD(TAG, "Added service: %s", full_service.c_str());
|
||||
}
|
||||
|
||||
otSrpClientEnableAutoStartMode(instance, OpenThreadSrpComponent::srp_start_callback, nullptr);
|
||||
ESP_LOGD(TAG, "Finished SRP setup");
|
||||
otSrpClientEnableAutoStartMode(instance, srp_start_callback, nullptr);
|
||||
}
|
||||
|
||||
void *OpenThreadSrpComponent::pool_alloc_(size_t size) {
|
||||
@@ -236,21 +217,6 @@ bool OpenThreadComponent::teardown() {
|
||||
return this->teardown_complete_;
|
||||
}
|
||||
|
||||
void OpenThreadComponent::on_factory_reset(std::function<void()> callback) {
|
||||
factory_reset_external_callback_ = callback;
|
||||
ESP_LOGD(TAG, "Start Removal SRP Host and Services");
|
||||
otError error;
|
||||
InstanceLock lock = InstanceLock::acquire();
|
||||
otInstance *instance = lock.get_instance();
|
||||
otSrpClientSetCallback(instance, OpenThreadSrpComponent::srp_factory_reset_callback, this);
|
||||
error = otSrpClientRemoveHostAndServices(instance, true, true);
|
||||
if (error != OT_ERROR_NONE) {
|
||||
ESP_LOGW(TAG, "Failed to Remove SRP Host and Services");
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Waiting on Confirmation Removal SRP Host and Services");
|
||||
}
|
||||
|
||||
} // namespace openthread
|
||||
} // namespace esphome
|
||||
|
||||
|
@@ -6,8 +6,6 @@
|
||||
#include "esphome/components/network/ip_address.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include <openthread/srp_client.h>
|
||||
#include <openthread/srp_client_buffers.h>
|
||||
#include <openthread/thread.h>
|
||||
|
||||
#include <optional>
|
||||
@@ -30,14 +28,11 @@ class OpenThreadComponent : public Component {
|
||||
network::IPAddresses get_ip_addresses();
|
||||
std::optional<otIp6Address> get_omr_address();
|
||||
void ot_main();
|
||||
void on_factory_reset(std::function<void()> callback);
|
||||
void defer_factory_reset_external_callback();
|
||||
|
||||
protected:
|
||||
std::optional<otIp6Address> get_omr_address_(InstanceLock &lock);
|
||||
bool teardown_started_{false};
|
||||
bool teardown_complete_{false};
|
||||
std::function<void()> factory_reset_external_callback_;
|
||||
};
|
||||
|
||||
extern OpenThreadComponent *global_openthread_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@@ -48,12 +43,6 @@ class OpenThreadSrpComponent : public Component {
|
||||
// This has to run after the mdns component or else no services are available to advertise
|
||||
float get_setup_priority() const override { return this->mdns_->get_setup_priority() - 1.0; }
|
||||
void setup() override;
|
||||
static void srp_callback(otError err, const otSrpClientHostInfo *host_info, const otSrpClientService *services,
|
||||
const otSrpClientService *removed_services, void *context);
|
||||
static void srp_start_callback(const otSockAddr *server_socket_address, void *context);
|
||||
static void srp_factory_reset_callback(otError err, const otSrpClientHostInfo *host_info,
|
||||
const otSrpClientService *services, const otSrpClientService *removed_services,
|
||||
void *context);
|
||||
|
||||
protected:
|
||||
esphome::mdns::MDNSComponent *mdns_{nullptr};
|
||||
|
@@ -10,8 +10,7 @@ from esphome.const import (
|
||||
CONF_TRIGGER_ID,
|
||||
PlatformFramework,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.coroutine import CoroPriority
|
||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
AUTO_LOAD = ["md5", "safe_mode"]
|
||||
@@ -83,7 +82,7 @@ BASE_OTA_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.OTA_UPDATES)
|
||||
@coroutine_with_priority(CoroPriority.COMMUNICATION)
|
||||
async def to_code(config):
|
||||
cg.add_define("USE_OTA")
|
||||
|
||||
|
@@ -121,11 +121,15 @@ def transport_schema(cls):
|
||||
return TRANSPORT_SCHEMA.extend({cv.GenerateID(): cv.declare_id(cls)})
|
||||
|
||||
|
||||
# Build a list of sensors for this platform
|
||||
CORE.data[DOMAIN] = {CONF_SENSORS: []}
|
||||
|
||||
|
||||
def get_sensors(transport_id):
|
||||
"""Return the list of sensors for this platform."""
|
||||
return (
|
||||
sensor
|
||||
for sensor in CORE.data.setdefault(DOMAIN, {}).setdefault(CONF_SENSORS, [])
|
||||
for sensor in CORE.data[DOMAIN][CONF_SENSORS]
|
||||
if sensor[CONF_TRANSPORT_ID] == transport_id
|
||||
)
|
||||
|
||||
@@ -133,8 +137,7 @@ def get_sensors(transport_id):
|
||||
def validate_packet_transport_sensor(config):
|
||||
if CONF_NAME in config and CONF_INTERNAL not in config:
|
||||
raise cv.Invalid("Must provide internal: config when using name:")
|
||||
conf_sensors = CORE.data.setdefault(DOMAIN, {}).setdefault(CONF_SENSORS, [])
|
||||
conf_sensors.append(config)
|
||||
CORE.data[DOMAIN][CONF_SENSORS].append(config)
|
||||
return config
|
||||
|
||||
|
||||
|
@@ -14,7 +14,6 @@ from esphome.const import (
|
||||
|
||||
CODEOWNERS = ["@Mat931"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
AUTO_LOAD = ["gpio_expander"]
|
||||
MULTI_CONF = True
|
||||
pca6416a_ns = cg.esphome_ns.namespace("pca6416a")
|
||||
|
||||
|
@@ -51,11 +51,6 @@ void PCA6416AComponent::setup() {
|
||||
this->status_has_error());
|
||||
}
|
||||
|
||||
void PCA6416AComponent::loop() {
|
||||
// Invalidate cache at the start of each loop
|
||||
this->reset_pin_cache_();
|
||||
}
|
||||
|
||||
void PCA6416AComponent::dump_config() {
|
||||
if (this->has_pullup_) {
|
||||
ESP_LOGCONFIG(TAG, "PCAL6416A:");
|
||||
@@ -68,25 +63,15 @@ void PCA6416AComponent::dump_config() {
|
||||
}
|
||||
}
|
||||
|
||||
bool PCA6416AComponent::digital_read_hw(uint8_t pin) {
|
||||
bool PCA6416AComponent::digital_read(uint8_t pin) {
|
||||
uint8_t bit = pin % 8;
|
||||
uint8_t reg_addr = pin < 8 ? PCA6416A_INPUT0 : PCA6416A_INPUT1;
|
||||
uint8_t value = 0;
|
||||
if (!this->read_register_(reg_addr, &value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the appropriate part of input_mask_
|
||||
if (pin < 8) {
|
||||
this->input_mask_ = (this->input_mask_ & 0xFF00) | value;
|
||||
} else {
|
||||
this->input_mask_ = (this->input_mask_ & 0x00FF) | (uint16_t(value) << 8);
|
||||
}
|
||||
return true;
|
||||
this->read_register_(reg_addr, &value);
|
||||
return value & (1 << bit);
|
||||
}
|
||||
|
||||
bool PCA6416AComponent::digital_read_cache(uint8_t pin) { return this->input_mask_ & (1 << pin); }
|
||||
|
||||
void PCA6416AComponent::digital_write_hw(uint8_t pin, bool value) {
|
||||
void PCA6416AComponent::digital_write(uint8_t pin, bool value) {
|
||||
uint8_t reg_addr = pin < 8 ? PCA6416A_OUTPUT0 : PCA6416A_OUTPUT1;
|
||||
this->update_register_(pin, value, reg_addr);
|
||||
}
|
||||
|
@@ -3,20 +3,20 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/gpio_expander/cached_gpio.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace pca6416a {
|
||||
|
||||
class PCA6416AComponent : public Component,
|
||||
public i2c::I2CDevice,
|
||||
public gpio_expander::CachedGpioExpander<uint8_t, 16> {
|
||||
class PCA6416AComponent : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
PCA6416AComponent() = default;
|
||||
|
||||
/// Check i2c availability and setup masks
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
/// Helper function to read the value of a pin.
|
||||
bool digital_read(uint8_t pin);
|
||||
/// Helper function to write the value of a pin.
|
||||
void digital_write(uint8_t pin, bool value);
|
||||
/// Helper function to set the pin mode of a pin.
|
||||
void pin_mode(uint8_t pin, gpio::Flags flags);
|
||||
|
||||
@@ -25,11 +25,6 @@ class PCA6416AComponent : public Component,
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
// Virtual methods from CachedGpioExpander
|
||||
bool digital_read_hw(uint8_t pin) override;
|
||||
bool digital_read_cache(uint8_t pin) override;
|
||||
void digital_write_hw(uint8_t pin, bool value) override;
|
||||
|
||||
bool read_register_(uint8_t reg, uint8_t *value);
|
||||
bool write_register_(uint8_t reg, uint8_t value);
|
||||
void update_register_(uint8_t pin, bool pin_value, uint8_t reg_addr);
|
||||
@@ -37,8 +32,6 @@ class PCA6416AComponent : public Component,
|
||||
/// The mask to write as output state - 1 means HIGH, 0 means LOW
|
||||
uint8_t output_0_{0x00};
|
||||
uint8_t output_1_{0x00};
|
||||
/// Cache for input values (16-bit combined for both banks)
|
||||
uint16_t input_mask_{0x00};
|
||||
/// Storage for last I2C error seen
|
||||
esphome::i2c::ErrorCode last_error_;
|
||||
/// Only the PCAL6416A has pull-up resistors
|
||||
|
@@ -11,8 +11,7 @@ from esphome.const import (
|
||||
CONF_OUTPUT,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@hwstar", "@clydebarrow", "@bdraco"]
|
||||
AUTO_LOAD = ["gpio_expander"]
|
||||
CODEOWNERS = ["@hwstar", "@clydebarrow"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
CONF_PIN_COUNT = "pin_count"
|
||||
|
@@ -37,9 +37,10 @@ void PCA9554Component::setup() {
|
||||
}
|
||||
|
||||
void PCA9554Component::loop() {
|
||||
// Invalidate the cache at the start of each loop.
|
||||
// The actual read will happen on demand when digital_read() is called
|
||||
this->reset_pin_cache_();
|
||||
// The read_inputs_() method will cache the input values from the chip.
|
||||
this->read_inputs_();
|
||||
// Clear all the previously read flags.
|
||||
this->was_previously_read_ = 0x00;
|
||||
}
|
||||
|
||||
void PCA9554Component::dump_config() {
|
||||
@@ -53,17 +54,21 @@ void PCA9554Component::dump_config() {
|
||||
}
|
||||
}
|
||||
|
||||
bool PCA9554Component::digital_read_hw(uint8_t pin) {
|
||||
// Read all pins from hardware into input_mask_
|
||||
return this->read_inputs_(); // Return true if I2C read succeeded, false on error
|
||||
}
|
||||
|
||||
bool PCA9554Component::digital_read_cache(uint8_t pin) {
|
||||
// Return the cached pin state from input_mask_
|
||||
bool PCA9554Component::digital_read(uint8_t pin) {
|
||||
// Note: We want to try and avoid doing any I2C bus read transactions here
|
||||
// to conserve I2C bus bandwidth. So what we do is check to see if we
|
||||
// have seen a read during the time esphome is running this loop. If we have,
|
||||
// we do an I2C bus transaction to get the latest value. If we haven't
|
||||
// we return a cached value which was read at the time loop() was called.
|
||||
if (this->was_previously_read_ & (1 << pin))
|
||||
this->read_inputs_(); // Force a read of a new value
|
||||
// Indicate we saw a read request for this pin in case a
|
||||
// read happens later in the same loop.
|
||||
this->was_previously_read_ |= (1 << pin);
|
||||
return this->input_mask_ & (1 << pin);
|
||||
}
|
||||
|
||||
void PCA9554Component::digital_write_hw(uint8_t pin, bool value) {
|
||||
void PCA9554Component::digital_write(uint8_t pin, bool value) {
|
||||
if (value) {
|
||||
this->output_mask_ |= (1 << pin);
|
||||
} else {
|
||||
@@ -122,7 +127,8 @@ bool PCA9554Component::write_register_(uint8_t reg, uint16_t value) {
|
||||
|
||||
float PCA9554Component::get_setup_priority() const { return setup_priority::IO; }
|
||||
|
||||
// Run our loop() method early to invalidate cache before any other components access the pins
|
||||
// Run our loop() method very early in the loop, so that we cache read values before
|
||||
// before other components call our digital_read() method.
|
||||
float PCA9554Component::get_loop_priority() const { return 9.0f; } // Just after WIFI
|
||||
|
||||
void PCA9554GPIOPin::setup() { pin_mode(flags_); }
|
||||
|
@@ -3,21 +3,22 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/gpio_expander/cached_gpio.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace pca9554 {
|
||||
|
||||
class PCA9554Component : public Component,
|
||||
public i2c::I2CDevice,
|
||||
public gpio_expander::CachedGpioExpander<uint16_t, 16> {
|
||||
class PCA9554Component : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
PCA9554Component() = default;
|
||||
|
||||
/// Check i2c availability and setup masks
|
||||
void setup() override;
|
||||
/// Invalidate cache at start of each loop
|
||||
/// Poll for input changes periodically
|
||||
void loop() override;
|
||||
/// Helper function to read the value of a pin.
|
||||
bool digital_read(uint8_t pin);
|
||||
/// Helper function to write the value of a pin.
|
||||
void digital_write(uint8_t pin, bool value);
|
||||
/// Helper function to set the pin mode of a pin.
|
||||
void pin_mode(uint8_t pin, gpio::Flags flags);
|
||||
|
||||
@@ -31,12 +32,8 @@ class PCA9554Component : public Component,
|
||||
|
||||
protected:
|
||||
bool read_inputs_();
|
||||
bool write_register_(uint8_t reg, uint16_t value);
|
||||
|
||||
// Virtual methods from CachedGpioExpander
|
||||
bool digital_read_hw(uint8_t pin) override;
|
||||
bool digital_read_cache(uint8_t pin) override;
|
||||
void digital_write_hw(uint8_t pin, bool value) override;
|
||||
bool write_register_(uint8_t reg, uint16_t value);
|
||||
|
||||
/// number of bits the expander has
|
||||
size_t pin_count_{8};
|
||||
@@ -48,6 +45,8 @@ class PCA9554Component : public Component,
|
||||
uint16_t output_mask_{0x00};
|
||||
/// The state of the actual input pin states - 1 means HIGH, 0 means LOW
|
||||
uint16_t input_mask_{0x00};
|
||||
/// Flags to check if read previously during this loop
|
||||
uint16_t was_previously_read_ = {0x00};
|
||||
/// Storage for last I2C error seen
|
||||
esphome::i2c::ErrorCode last_error_;
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user