1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-13 08:42:18 +01:00

ESP-IDF support and generic target platforms (#2303)

* Socket refactor and SSL

* esp-idf temp

* Fixes

* Echo component and noise

* Add noise API transport support

* Updates

* ESP-IDF

* Complete

* Fixes

* Fixes

* Versions update

* New i2c APIs

* Complete i2c refactor

* SPI migration

* Revert ESP Preferences migration, too complex for now

* OTA support

* Remove echo again

* Remove ssl again

* GPIOFlags updates

* Rename esphal and ICACHE_RAM_ATTR

* Make ESP32 arduino compilable again

* Fix GPIO flags

* Complete pin registry refactor and fixes

* Fixes to make test1 compile

* Remove sdkconfig file

* Ignore sdkconfig file

* Fixes in reviewing

* Make test2 compile

* Make test4 compile

* Make test5 compile

* Run clang-format

* Fix lint errors

* Use esp-idf APIs instead of btStart

* Another round of fixes

* Start implementing ESP8266

* Make test3 compile

* Guard esp8266 code

* Lint

* Reformat

* Fixes

* Fixes v2

* more fixes

* ESP-IDF tidy target

* Convert ARDUINO_ARCH_ESPxx

* Update WiFiSignalSensor

* Update time ifdefs

* OTA needs millis from hal

* RestartSwitch needs delay from hal

* ESP-IDF Uart

* Fix OTA blank password

* Allow setting sdkconfig

* Fix idf partitions and allow setting sdkconfig from yaml

* Re-add read/write compat APIs and fix esp8266 uart

* Fix esp8266 store log strings in flash

* Fix ESP32 arduino preferences not initialized

* Update ifdefs

* Change how sdkconfig change is detected

* Add checks to ci-custom and fix them

* Run clang-format

* Add esp-idf clang-tidy target and fix errors

* Fixes from clang-tidy idf round 2

* Fixes from compiling tests with esp-idf

* Run clang-format

* Switch test5.yaml to esp-idf

* Implement ESP8266 Preferences

* Lint

* Re-do PIO package version selection a bit

* Fix arduinoespressif32 package version

* Fix unit tests

* Lint

* Lint fixes

* Fix readv/writev not defined

* Fix graphing component

* Re-add all old options from core/config.py

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Otto Winter
2021-09-20 11:47:51 +02:00
committed by GitHub
parent 1e8e471dec
commit ac0d921413
583 changed files with 9008 additions and 5420 deletions

View File

@@ -1,330 +1,143 @@
import logging
import operator
from functools import reduce
import esphome.config_validation as cv
from esphome.const import CONF_INVERTED, CONF_MODE, CONF_NUMBER
from esphome.core import CORE
from esphome.const import (
CONF_INPUT,
CONF_MODE,
CONF_NUMBER,
CONF_OPEN_DRAIN,
CONF_OUTPUT,
CONF_PULLDOWN,
CONF_PULLUP,
)
from esphome.util import SimpleRegistry
from esphome import boards
_LOGGER = logging.getLogger(__name__)
def _lookup_pin(value):
if CORE.is_esp8266:
board_pins_dict = boards.ESP8266_BOARD_PINS
base_pins = boards.ESP8266_BASE_PINS
elif CORE.is_esp32:
if CORE.board in boards.ESP32_C3_BOARD_PINS:
board_pins_dict = boards.ESP32_C3_BOARD_PINS
base_pins = boards.ESP32_C3_BASE_PINS
else:
board_pins_dict = boards.ESP32_BOARD_PINS
base_pins = boards.ESP32_BASE_PINS
else:
raise NotImplementedError
board_pins = board_pins_dict.get(CORE.board, {})
# Resolved aliased board pins (shorthand when two boards have the same pin configuration)
while isinstance(board_pins, str):
board_pins = board_pins_dict[board_pins]
if value in board_pins:
return board_pins[value]
if value in base_pins:
return base_pins[value]
raise cv.Invalid(f"Cannot resolve pin name '{value}' for board {CORE.board}.")
def _translate_pin(value):
if isinstance(value, dict) or value is None:
raise cv.Invalid(
"This variable only supports pin numbers, not full pin schemas "
"(with inverted and mode)."
)
if isinstance(value, int):
return value
try:
return int(value)
except ValueError:
pass
if value.startswith("GPIO"):
return cv.Coerce(int)(value[len("GPIO") :].strip())
return _lookup_pin(value)
_ESP_SDIO_PINS = {
6: "Flash Clock",
7: "Flash Data 0",
8: "Flash Data 1",
11: "Flash Command",
}
_ESP32C3_SDIO_PINS = {
12: "Flash IO3/HOLD#",
13: "Flash IO2/WP#",
14: "Flash CS#",
15: "Flash CLK",
16: "Flash IO0/DI",
17: "Flash IO1/DO",
}
def validate_gpio_pin(value):
value = _translate_pin(value)
if CORE.is_esp32_c3:
if value < 0 or value > 22:
raise cv.Invalid(f"ESP32-C3: Invalid pin number: {value}")
if value in _ESP32C3_SDIO_PINS:
raise cv.Invalid(
f"This pin cannot be used on ESP32-C3s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})"
)
return value
if CORE.is_esp32:
if value < 0 or value > 39:
raise cv.Invalid(f"ESP32: Invalid pin number: {value}")
if value in _ESP_SDIO_PINS:
raise cv.Invalid(
f"This pin cannot be used on ESP32s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})"
)
if 9 <= value <= 10:
_LOGGER.warning(
"ESP32: Pin %s (9-10) might already be used by the "
"flash interface in QUAD IO flash mode.",
value,
)
if value in (20, 24, 28, 29, 30, 31):
# These pins are not exposed in GPIO mux (reason unknown)
# but they're missing from IO_MUX list in datasheet
raise cv.Invalid(f"The pin GPIO{value} is not usable on ESP32s.")
return value
if CORE.is_esp8266:
if value < 0 or value > 17:
raise cv.Invalid(f"ESP8266: Invalid pin number: {value}")
if value in _ESP_SDIO_PINS:
raise cv.Invalid(
f"This pin cannot be used on ESP8266s and is already used by the flash interface (function: {_ESP_SDIO_PINS[value]})"
)
if 9 <= value <= 10:
_LOGGER.warning(
"ESP8266: Pin %s (9-10) might already be used by the "
"flash interface in QUAD IO flash mode.",
value,
)
return value
raise NotImplementedError
def input_pin(value):
value = validate_gpio_pin(value)
if CORE.is_esp8266 and value == 17:
raise cv.Invalid("GPIO17 (TOUT) is an analog-only pin on the ESP8266.")
return value
def input_pullup_pin(value):
value = input_pin(value)
if CORE.is_esp32:
return output_pin(value)
if CORE.is_esp8266:
if value == 0:
raise cv.Invalid(
"GPIO Pin 0 does not support pullup pin mode. "
"Please choose another pin."
)
return value
raise NotImplementedError
def output_pin(value):
value = validate_gpio_pin(value)
if CORE.is_esp32:
if 34 <= value <= 39:
raise cv.Invalid(
f"ESP32: GPIO{value} (34-39) can only be used as an input pin."
)
return value
if CORE.is_esp8266:
if value == 17:
raise cv.Invalid("GPIO17 (TOUT) is an analog-only pin on the ESP8266.")
return value
raise NotImplementedError
def analog_pin(value):
value = validate_gpio_pin(value)
if CORE.is_esp32:
if CORE.is_esp32_c3:
if 0 <= value <= 4: # ADC1
return value
raise cv.Invalid("ESP32-C3: Only pins 0 though 4 support ADC.")
if 32 <= value <= 39: # ADC1
return value
raise cv.Invalid("ESP32: Only pins 32 though 39 support ADC.")
if CORE.is_esp8266:
if value == 17: # A0
return value
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.")
raise NotImplementedError
input_output_pin = cv.All(input_pin, output_pin)
PIN_MODES_ESP8266 = [
"INPUT",
"OUTPUT",
"INPUT_PULLUP",
"OUTPUT_OPEN_DRAIN",
"SPECIAL",
"FUNCTION_1",
"FUNCTION_2",
"FUNCTION_3",
"FUNCTION_4",
"FUNCTION_0",
"WAKEUP_PULLUP",
"WAKEUP_PULLDOWN",
"INPUT_PULLDOWN_16",
]
PIN_MODES_ESP32 = [
"INPUT",
"OUTPUT",
"INPUT_PULLUP",
"OUTPUT_OPEN_DRAIN",
"SPECIAL",
"FUNCTION_1",
"FUNCTION_2",
"FUNCTION_3",
"FUNCTION_4",
"PULLUP",
"PULLDOWN",
"INPUT_PULLDOWN",
"OPEN_DRAIN",
"FUNCTION_5",
"FUNCTION_6",
"ANALOG",
]
def pin_mode(value):
if CORE.is_esp32:
return cv.one_of(*PIN_MODES_ESP32, upper=True)(value)
if CORE.is_esp8266:
return cv.one_of(*PIN_MODES_ESP8266, upper=True)(value)
raise NotImplementedError
GPIO_FULL_OUTPUT_PIN_SCHEMA = cv.Schema(
{
cv.Required(CONF_NUMBER): output_pin,
cv.Optional(CONF_MODE, default="OUTPUT"): pin_mode,
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
}
)
GPIO_FULL_INPUT_PIN_SCHEMA = cv.Schema(
{
cv.Required(CONF_NUMBER): input_pin,
cv.Optional(CONF_MODE, default="INPUT"): pin_mode,
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
}
)
GPIO_FULL_INPUT_PULLUP_PIN_SCHEMA = cv.Schema(
{
cv.Required(CONF_NUMBER): input_pin,
cv.Optional(CONF_MODE, default="INPUT_PULLUP"): pin_mode,
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
}
)
GPIO_FULL_ANALOG_PIN_SCHEMA = cv.Schema(
{
cv.Required(CONF_NUMBER): analog_pin,
cv.Optional(CONF_MODE, default="INPUT"): pin_mode,
}
)
def shorthand_output_pin(value):
value = output_pin(value)
return GPIO_FULL_OUTPUT_PIN_SCHEMA({CONF_NUMBER: value})
def shorthand_input_pin(value):
value = input_pin(value)
return GPIO_FULL_INPUT_PIN_SCHEMA({CONF_NUMBER: value})
def shorthand_input_pullup_pin(value):
value = input_pullup_pin(value)
return GPIO_FULL_INPUT_PIN_SCHEMA(
{
CONF_NUMBER: value,
CONF_MODE: "INPUT_PULLUP",
}
)
def shorthand_analog_pin(value):
value = analog_pin(value)
return GPIO_FULL_ANALOG_PIN_SCHEMA({CONF_NUMBER: value})
def validate_has_interrupt(value):
if CORE.is_esp8266:
if value[CONF_NUMBER] >= 16:
raise cv.Invalid(
f"Pins GPIO16 and GPIO17 do not support interrupts and cannot be used here, got {value[CONF_NUMBER]}"
)
return value
from esphome.core import CORE
PIN_SCHEMA_REGISTRY = SimpleRegistry()
def internal_gpio_output_pin_schema(value):
if isinstance(value, dict):
return GPIO_FULL_OUTPUT_PIN_SCHEMA(value)
return shorthand_output_pin(value)
def _set_mode(value, default_mode):
import esphome.config_validation as cv
if CONF_MODE not in value:
return {**value, CONF_MODE: default_mode}
mode = value[CONF_MODE]
if not isinstance(mode, str):
return value
# mode is a string, try parsing it like arduino pin modes
PIN_MODES = {
"INPUT": {
CONF_INPUT: True,
},
"OUTPUT": {
CONF_OUTPUT: True,
},
"INPUT_PULLUP": {
CONF_INPUT: True,
CONF_PULLUP: True,
},
"OUTPUT_OPEN_DRAIN": {
CONF_OUTPUT: True,
CONF_OPEN_DRAIN: True,
},
"INPUT_PULLDOWN_16": {
CONF_INPUT: True,
CONF_PULLDOWN: True,
},
"INPUT_PULLDOWN": {
CONF_INPUT: True,
CONF_PULLDOWN: True,
},
}
if mode.upper() not in PIN_MODES:
raise cv.Invalid(f"Unknown pin mode {mode}", [CONF_MODE])
return {**value, CONF_MODE: PIN_MODES[mode.upper()]}
def gpio_output_pin_schema(value):
if isinstance(value, dict):
for key, entry in PIN_SCHEMA_REGISTRY.items():
if key in value:
return entry[1][0](value)
return internal_gpio_output_pin_schema(value)
def _schema_creator(default_mode, internal: bool = False):
def validator(value):
if not isinstance(value, dict):
return validator({CONF_NUMBER: value})
value = _set_mode(value, default_mode)
if not internal:
for key, entry in PIN_SCHEMA_REGISTRY.items():
if key != CORE.target_platform and key in value:
return entry[1](value)
return PIN_SCHEMA_REGISTRY[CORE.target_platform][1](value)
return validator
def internal_gpio_input_pin_schema(value):
if isinstance(value, dict):
return GPIO_FULL_INPUT_PIN_SCHEMA(value)
return shorthand_input_pin(value)
def _internal_number_creator(mode):
def validator(value):
value_d = {CONF_NUMBER: value}
value_d = _set_mode(value_d, mode)
return PIN_SCHEMA_REGISTRY[CORE.target_platform][1](value_d)[CONF_NUMBER]
return validator
def internal_gpio_analog_pin_schema(value):
if isinstance(value, dict):
return GPIO_FULL_ANALOG_PIN_SCHEMA(value)
return shorthand_analog_pin(value)
def gpio_flags_expr(mode):
"""Convert the given mode dict to a gpio Flags expression"""
import esphome.codegen as cg
FLAGS_MAPPING = {
CONF_INPUT: cg.gpio_Flags.FLAG_INPUT,
CONF_OUTPUT: cg.gpio_Flags.FLAG_OUTPUT,
CONF_OPEN_DRAIN: cg.gpio_Flags.FLAG_OPEN_DRAIN,
CONF_PULLUP: cg.gpio_Flags.FLAG_PULLUP,
CONF_PULLDOWN: cg.gpio_Flags.FLAG_PULLDOWN,
}
active_flags = [v for k, v in FLAGS_MAPPING.items() if mode.get(k)]
if active_flags:
return cg.gpio_Flags.FLAG_NONE
return reduce(operator.or_, active_flags)
def gpio_input_pin_schema(value):
if isinstance(value, dict):
for key, entry in PIN_SCHEMA_REGISTRY.items():
if key in value:
return entry[1][1](value)
return internal_gpio_input_pin_schema(value)
def internal_gpio_input_pullup_pin_schema(value):
if isinstance(value, dict):
return GPIO_FULL_INPUT_PULLUP_PIN_SCHEMA(value)
return shorthand_input_pullup_pin(value)
def gpio_input_pullup_pin_schema(value):
if isinstance(value, dict):
for key, entry in PIN_SCHEMA_REGISTRY.items():
if key in value:
return entry[1][1](value)
return internal_gpio_input_pullup_pin_schema(value)
gpio_pin_schema = _schema_creator
internal_gpio_pin_number = _internal_number_creator
gpio_output_pin_schema = _schema_creator(
{
CONF_OUTPUT: True,
}
)
gpio_input_pin_schema = _schema_creator(
{
CONF_INPUT: True,
}
)
gpio_input_pullup_pin_schema = _schema_creator(
{
CONF_INPUT: True,
CONF_PULLUP: True,
}
)
internal_gpio_output_pin_schema = _schema_creator(
{
CONF_OUTPUT: True,
},
internal=True,
)
internal_gpio_output_pin_number = _internal_number_creator({CONF_OUTPUT: True})
internal_gpio_input_pin_schema = _schema_creator(
{
CONF_INPUT: True,
},
internal=True,
)
internal_gpio_input_pin_number = _internal_number_creator({CONF_INPUT: True})
internal_gpio_input_pullup_pin_schema = _schema_creator(
{
CONF_INPUT: True,
CONF_PULLUP: True,
},
internal=True,
)
internal_gpio_input_pullup_pin_number = _internal_number_creator(
{
CONF_INPUT: True,
CONF_PULLUP: True,
}
)