From 5a0b8328d81264e38ca9d486f857b36a7cbdadb2 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 17 Feb 2022 04:59:46 +0100 Subject: [PATCH] ESP8266 early init for pins (#3144) --- esphome/components/esp8266/__init__.py | 16 ++++++-- esphome/components/esp8266/const.py | 1 + esphome/components/esp8266/core.cpp | 10 +++++ esphome/components/esp8266/core.h | 14 +++++++ esphome/components/esp8266/gpio.py | 54 ++++++++++++++++++++++++-- 5 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 esphome/components/esp8266/core.h diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 3c83400c1d..7b1be32e38 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -17,11 +17,16 @@ import esphome.config_validation as cv import esphome.codegen as cg from esphome.helpers import copy_file_if_changed -from .const import CONF_RESTORE_FROM_FLASH, KEY_BOARD, KEY_ESP8266, esp8266_ns +from .const import ( + CONF_RESTORE_FROM_FLASH, + KEY_BOARD, + KEY_ESP8266, + KEY_PIN_INITIAL_STATES, + esp8266_ns, +) from .boards import ESP8266_FLASH_SIZES, ESP8266_LD_SCRIPTS -# force import gpio to register pin schema -from .gpio import esp8266_pin_to_code # noqa +from .gpio import PinInitialState, add_pin_initial_states_array CODEOWNERS = ["@esphome/core"] @@ -37,6 +42,9 @@ def set_core_data(config): config[CONF_FRAMEWORK][CONF_VERSION] ) CORE.data[KEY_ESP8266][KEY_BOARD] = config[CONF_BOARD] + CORE.data[KEY_ESP8266][KEY_PIN_INITIAL_STATES] = [ + PinInitialState() for _ in range(16) + ] return config @@ -221,6 +229,8 @@ async def to_code(config): if ld_script is not None: cg.add_platformio_option("board_build.ldscript", ld_script) + CORE.add_job(add_pin_initial_states_array) + # Called by writer.py def copy_files(): diff --git a/esphome/components/esp8266/const.py b/esphome/components/esp8266/const.py index 16a050360c..70429297e0 100644 --- a/esphome/components/esp8266/const.py +++ b/esphome/components/esp8266/const.py @@ -2,6 +2,7 @@ import esphome.codegen as cg KEY_ESP8266 = "esp8266" KEY_BOARD = "board" +KEY_PIN_INITIAL_STATES = "pin_initial_states" CONF_RESTORE_FROM_FLASH = "restore_from_flash" # esp8266 namespace is already defined by arduino, manually prefix esphome diff --git a/esphome/components/esp8266/core.cpp b/esphome/components/esp8266/core.cpp index 828d71a3bd..a9460f51f2 100644 --- a/esphome/components/esp8266/core.cpp +++ b/esphome/components/esp8266/core.cpp @@ -1,5 +1,6 @@ #ifdef USE_ESP8266 +#include "core.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "preferences.h" @@ -53,6 +54,15 @@ extern "C" void resetPins() { // NOLINT // however, not strictly needed as we set up the pins properly // ourselves and this causes pins to toggle during reboot. force_link_symbols(); + + for (int i = 0; i < 16; i++) { + uint8_t mode = ESPHOME_ESP8266_GPIO_INITIAL_MODE[i]; + uint8_t level = ESPHOME_ESP8266_GPIO_INITIAL_LEVEL[i]; + if (mode != 255) + pinMode(i, mode); // NOLINT + if (level != 255) + digitalWrite(i, level); // NOLINT + } } } // namespace esphome diff --git a/esphome/components/esp8266/core.h b/esphome/components/esp8266/core.h new file mode 100644 index 0000000000..ac33305669 --- /dev/null +++ b/esphome/components/esp8266/core.h @@ -0,0 +1,14 @@ +#pragma once + +#ifdef USE_ESP8266 + +#include + +extern const uint8_t ESPHOME_ESP8266_GPIO_INITIAL_MODE[16]; +extern const uint8_t ESPHOME_ESP8266_GPIO_INITIAL_LEVEL[16]; + +namespace esphome { +namespace esp8266 {} // namespace esp8266 +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/esp8266/gpio.py b/esphome/components/esp8266/gpio.py index fa5c94dff5..cf33ec126b 100644 --- a/esphome/components/esp8266/gpio.py +++ b/esphome/components/esp8266/gpio.py @@ -1,4 +1,6 @@ import logging +from dataclasses import dataclass +from typing import List from esphome.const import ( CONF_ID, @@ -12,12 +14,12 @@ from esphome.const import ( CONF_PULLUP, ) from esphome import pins -from esphome.core import CORE +from esphome.core import CORE, coroutine_with_priority import esphome.config_validation as cv import esphome.codegen as cg from . import boards -from .const import KEY_BOARD, KEY_ESP8266, esp8266_ns +from .const import KEY_BOARD, KEY_ESP8266, KEY_PIN_INITIAL_STATES, esp8266_ns _LOGGER = logging.getLogger(__name__) @@ -160,11 +162,57 @@ ESP8266_PIN_SCHEMA = cv.All( ) +@dataclass +class PinInitialState: + mode = 255 + level: int = 255 + + @pins.PIN_SCHEMA_REGISTRY.register("esp8266", ESP8266_PIN_SCHEMA) async def esp8266_pin_to_code(config): var = cg.new_Pvariable(config[CONF_ID]) num = config[CONF_NUMBER] + mode = config[CONF_MODE] cg.add(var.set_pin(num)) cg.add(var.set_inverted(config[CONF_INVERTED])) - cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + cg.add(var.set_flags(pins.gpio_flags_expr(mode))) + if num < 16: + initial_state: PinInitialState = CORE.data[KEY_ESP8266][KEY_PIN_INITIAL_STATES][ + num + ] + if mode[CONF_INPUT]: + if mode[CONF_PULLDOWN]: + initial_state.mode = cg.global_ns.INPUT_PULLDOWN_16 + elif mode[CONF_PULLUP]: + initial_state.mode = cg.global_ns.INPUT_PULLUP + else: + initial_state.mode = cg.global_ns.INPUT + elif mode[CONF_OUTPUT]: + if mode[CONF_OPEN_DRAIN]: + initial_state.mode = cg.global_ns.OUTPUT_OPEN_DRAIN + else: + initial_state.mode = cg.global_ns.OUTPUT + initial_state.level = int(config[CONF_INVERTED]) + return var + + +@coroutine_with_priority(-999.0) +async def add_pin_initial_states_array(): + # Add includes at the very end, so that they override everything + initial_states: List[PinInitialState] = CORE.data[KEY_ESP8266][ + KEY_PIN_INITIAL_STATES + ] + initial_modes_s = ", ".join(str(x.mode) for x in initial_states) + initial_levels_s = ", ".join(str(x.level) for x in initial_states) + + cg.add_global( + cg.RawExpression( + f"const uint8_t ESPHOME_ESP8266_GPIO_INITIAL_MODE[16] = {{{initial_modes_s}}}" + ) + ) + cg.add_global( + cg.RawExpression( + f"const uint8_t ESPHOME_ESP8266_GPIO_INITIAL_LEVEL[16] = {{{initial_levels_s}}}" + ) + )