1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-15 01:32:19 +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

@@ -0,0 +1,379 @@
from dataclasses import dataclass
from typing import Union
from pathlib import Path
import logging
from esphome.helpers import write_file_if_changed
from esphome.const import (
CONF_BOARD,
CONF_FRAMEWORK,
CONF_TYPE,
CONF_VARIANT,
CONF_VERSION,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
KEY_TARGET_FRAMEWORK,
KEY_TARGET_PLATFORM,
)
from esphome.core import CORE, HexInt
import esphome.config_validation as cv
import esphome.codegen as cg
from .const import (
KEY_BOARD,
KEY_ESP32,
KEY_SDKCONFIG_OPTIONS,
KEY_VARIANT,
VARIANT_ESP32C3,
VARIANTS,
)
# force import gpio to register pin schema
from .gpio import esp32_pin_to_code # noqa
_LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@esphome/core"]
def set_core_data(config):
CORE.data[KEY_ESP32] = {}
CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = "esp32"
conf = config[CONF_FRAMEWORK]
if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF:
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "esp-idf"
CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS] = {}
elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO:
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse(
config[CONF_FRAMEWORK][CONF_VERSION_HINT]
)
CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD]
CORE.data[KEY_ESP32][KEY_VARIANT] = config[CONF_VARIANT]
return config
def get_esp32_variant():
return CORE.data[KEY_ESP32][KEY_VARIANT]
def is_esp32c3():
return get_esp32_variant() == VARIANT_ESP32C3
@dataclass
class RawSdkconfigValue:
"""An sdkconfig value that won't be auto-formatted"""
value: str
SdkconfigValueType = Union[bool, int, HexInt, str, RawSdkconfigValue]
def add_idf_sdkconfig_option(name: str, value: SdkconfigValueType):
"""Set an esp-idf sdkconfig value."""
if not CORE.using_esp_idf:
raise ValueError("Not an esp-idf project")
CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS][name] = value
def _format_framework_arduino_version(ver: cv.Version) -> str:
# format the given arduino (https://github.com/espressif/arduino-esp32/releases) version to
# a PIO platformio/framework-arduinoespressif32 value
# List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32
if ver <= cv.Version(1, 0, 3):
return f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
# NOTE: Keep this in mind when updating the recommended version:
# * New framework historically have had some regressions, especially for WiFi.
# The new version needs to be thoroughly validated before changing the
# recommended version as otherwise a bunch of devices could be bricked
# * For all constants below, update platformio.ini (in this repo)
# and platformio.ini/platformio-lint.ini in the esphome-docker-base repository
# The default/recommended arduino framework version
# - https://github.com/espressif/arduino-esp32/releases
# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(1, 0, 6)
# The platformio/espressif32 version to use for arduino frameworks
# - https://github.com/platformio/platform-espressif32/releases
# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
ARDUINO_PLATFORM_VERSION = cv.Version(3, 3, 2)
# The default/recommended esp-idf framework version
# - https://github.com/espressif/esp-idf/releases
# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf
RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 3, 0)
# The platformio/espressif32 version to use for esp-idf frameworks
# - https://github.com/platformio/platform-espressif32/releases
# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
ESP_IDF_PLATFORM_VERSION = cv.Version(3, 3, 2)
def _arduino_check_versions(value):
value = value.copy()
lookups = {
"dev": ("https://github.com/espressif/arduino-esp32.git", cv.Version(2, 0, 0)),
"latest": ("", cv.Version(1, 0, 3)),
"recommended": (
_format_framework_arduino_version(RECOMMENDED_ARDUINO_FRAMEWORK_VERSION),
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION,
),
}
ver_value = value[CONF_VERSION]
default_ver_hint = None
if ver_value.lower() in lookups:
default_ver_hint = str(lookups[ver_value.lower()][1])
ver_value = lookups[ver_value.lower()][0]
else:
with cv.suppress_invalid():
ver = cv.Version.parse(cv.version_number(value))
if ver <= cv.Version(1, 0, 3):
ver_value = f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
else:
ver_value = f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
default_ver_hint = str(ver)
value[CONF_VERSION] = ver_value
if CONF_VERSION_HINT not in value and default_ver_hint is None:
raise cv.Invalid("Needs a version hint to understand the framework version")
ver_hint_s = value.get(CONF_VERSION_HINT, default_ver_hint)
value[CONF_VERSION_HINT] = ver_hint_s
plat_ver = value.get(CONF_PLATFORM_VERSION, ARDUINO_PLATFORM_VERSION)
value[CONF_PLATFORM_VERSION] = str(plat_ver)
if cv.Version.parse(ver_hint_s) != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION:
_LOGGER.warning(
"The selected arduino framework version is not the recommended one"
)
_LOGGER.warning(
"If there are connectivity or build issues please remove the manual version"
)
return value
def _format_framework_espidf_version(ver: cv.Version) -> str:
# format the given arduino (https://github.com/espressif/esp-idf/releases) version to
# a PIO platformio/framework-espidf value
# List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf
return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
def _esp_idf_check_versions(value):
value = value.copy()
lookups = {
"dev": ("https://github.com/espressif/esp-idf.git", cv.Version(4, 3, 1)),
"latest": ("", cv.Version(4, 3, 0)),
"recommended": (
_format_framework_espidf_version(RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION),
RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION,
),
}
ver_value = value[CONF_VERSION]
default_ver_hint = None
if ver_value.lower() in lookups:
default_ver_hint = str(lookups[ver_value.lower()][1])
ver_value = lookups[ver_value.lower()][0]
else:
with cv.suppress_invalid():
ver = cv.Version.parse(cv.version_number(value))
ver_value = f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
default_ver_hint = str(ver)
value[CONF_VERSION] = ver_value
if CONF_VERSION_HINT not in value and default_ver_hint is None:
raise cv.Invalid("Needs a version hint to understand the framework version")
ver_hint_s = value.get(CONF_VERSION_HINT, default_ver_hint)
value[CONF_VERSION_HINT] = ver_hint_s
if cv.Version.parse(ver_hint_s) < cv.Version(4, 0, 0):
raise cv.Invalid("Only ESP-IDF 4.0+ is supported")
if cv.Version.parse(ver_hint_s) != RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION:
_LOGGER.warning(
"The selected esp-idf framework version is not the recommended one"
)
_LOGGER.warning(
"If there are connectivity or build issues please remove the manual version"
)
plat_ver = value.get(CONF_PLATFORM_VERSION, ESP_IDF_PLATFORM_VERSION)
value[CONF_PLATFORM_VERSION] = str(plat_ver)
return value
CONF_VERSION_HINT = "version_hint"
CONF_PLATFORM_VERSION = "platform_version"
ARDUINO_FRAMEWORK_SCHEMA = cv.All(
cv.Schema(
{
cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict,
cv.Optional(CONF_VERSION_HINT): cv.version_number,
cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict,
}
),
_arduino_check_versions,
)
CONF_SDKCONFIG_OPTIONS = "sdkconfig_options"
ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
cv.Schema(
{
cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict,
cv.Optional(CONF_VERSION_HINT): cv.version_number,
cv.Optional(CONF_SDKCONFIG_OPTIONS, default={}): {
cv.string_strict: cv.string_strict
},
cv.Optional(CONF_PLATFORM_VERSION): cv.string_strict,
}
),
_esp_idf_check_versions,
)
FRAMEWORK_ESP_IDF = "esp-idf"
FRAMEWORK_ARDUINO = "arduino"
FRAMEWORK_SCHEMA = cv.typed_schema(
{
FRAMEWORK_ESP_IDF: ESP_IDF_FRAMEWORK_SCHEMA,
FRAMEWORK_ARDUINO: ARDUINO_FRAMEWORK_SCHEMA,
},
lower=True,
space="-",
default_type=FRAMEWORK_ARDUINO,
)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.Required(CONF_BOARD): cv.string_strict,
cv.Optional(CONF_VARIANT, default="ESP32"): cv.one_of(
*VARIANTS, upper=True
),
cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA,
}
),
set_core_data,
)
async def to_code(config):
cg.add_platformio_option("board", config[CONF_BOARD])
cg.add_build_flag("-DUSE_ESP32")
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}")
conf = config[CONF_FRAMEWORK]
if conf[CONF_TYPE] == FRAMEWORK_ESP_IDF:
cg.add_platformio_option(
"platform", f"espressif32 @ {conf[CONF_PLATFORM_VERSION]}"
)
cg.add_platformio_option("framework", "espidf")
cg.add_build_flag("-DUSE_ESP_IDF")
cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ESP_IDF")
cg.add_build_flag("-Wno-nonnull-compare")
cg.add_platformio_option(
"platform_packages",
[f"platformio/framework-espidf @ {conf[CONF_VERSION]}"],
)
add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_SINGLE_APP", False)
add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM", True)
add_idf_sdkconfig_option(
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME", "partitions.csv"
)
add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False)
add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_SIZE", True)
cg.add_platformio_option("board_build.partitions", "partitions.csv")
for name, value in conf[CONF_SDKCONFIG_OPTIONS].items():
add_idf_sdkconfig_option(name, RawSdkconfigValue(value))
elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO:
cg.add_platformio_option(
"platform", f"espressif32 @ {conf[CONF_PLATFORM_VERSION]}"
)
cg.add_platformio_option("framework", "arduino")
cg.add_build_flag("-DUSE_ARDUINO")
cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO")
cg.add_platformio_option(
"platform_packages",
[f"platformio/framework-arduinoespressif32 @ {conf[CONF_VERSION]}"],
)
cg.add_platformio_option("board_build.partitions", "partitions.csv")
ARDUINO_PARTITIONS_CSV = """\
nvs, data, nvs, 0x009000, 0x005000,
otadata, data, ota, 0x00e000, 0x002000,
app0, app, ota_0, 0x010000, 0x1C0000,
app1, app, ota_1, 0x1D0000, 0x1C0000,
eeprom, data, 0x99, 0x390000, 0x001000,
spiffs, data, spiffs, 0x391000, 0x00F000
"""
IDF_PARTITIONS_CSV = """\
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, , 0x4000,
otadata, data, ota, , 0x2000,
phy_init, data, phy, , 0x1000,
app0, app, ota_0, , 0x1C0000,
app1, app, ota_1, , 0x1C0000,
"""
def _format_sdkconfig_val(value: SdkconfigValueType) -> str:
if isinstance(value, bool):
return "y" if value else "n"
if isinstance(value, int):
return str(value)
if isinstance(value, str):
return f'"{value}"'
if isinstance(value, RawSdkconfigValue):
return value.value
raise ValueError
def _write_sdkconfig():
# sdkconfig.{name} stores the real sdkconfig (modified by esp-idf with default)
# sdkconfig.{name}.esphomeinternal stores what esphome last wrote
# we use the internal one to detect if there were any changes, and if so write them to the
# real sdkconfig
sdk_path = Path(CORE.relative_build_path(f"sdkconfig.{CORE.name}"))
internal_path = Path(
CORE.relative_build_path(f"sdkconfig.{CORE.name}.esphomeinternal")
)
want_opts = CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS]
contents = (
"\n".join(
f"{name}={_format_sdkconfig_val(value)}"
for name, value in sorted(want_opts.items())
)
+ "\n"
)
if write_file_if_changed(internal_path, contents):
# internal changed, update real one
write_file_if_changed(sdk_path, contents)
# Called by writer.py
def copy_files():
if CORE.using_arduino:
write_file_if_changed(
CORE.relative_build_path("partitions.csv"),
ARDUINO_PARTITIONS_CSV,
)
if CORE.using_esp_idf:
_write_sdkconfig()
write_file_if_changed(
CORE.relative_build_path("partitions.csv"),
IDF_PARTITIONS_CSV,
)

View File

@@ -0,0 +1,927 @@
ESP32_BASE_PINS = {
"TX": 1,
"RX": 3,
"SDA": 21,
"SCL": 22,
"SS": 5,
"MOSI": 23,
"MISO": 19,
"SCK": 18,
"A0": 36,
"A3": 39,
"A4": 32,
"A5": 33,
"A6": 34,
"A7": 35,
"A10": 4,
"A11": 0,
"A12": 2,
"A13": 15,
"A14": 13,
"A15": 12,
"A16": 14,
"A17": 27,
"A18": 25,
"A19": 26,
"T0": 4,
"T1": 0,
"T2": 2,
"T3": 15,
"T4": 13,
"T5": 12,
"T6": 14,
"T7": 27,
"T8": 33,
"T9": 32,
"DAC1": 25,
"DAC2": 26,
"SVP": 36,
"SVN": 39,
}
ESP32_BOARD_PINS = {
"alksesp32": {
"A0": 32,
"A1": 33,
"A2": 25,
"A3": 26,
"A4": 27,
"A5": 14,
"A6": 12,
"A7": 15,
"D0": 40,
"D1": 41,
"D10": 19,
"D11": 21,
"D12": 22,
"D13": 23,
"D2": 15,
"D3": 2,
"D4": 0,
"D5": 4,
"D6": 16,
"D7": 17,
"D8": 5,
"D9": 18,
"DHT_PIN": 26,
"LED": 23,
"L_B": 5,
"L_G": 17,
"L_R": 22,
"L_RGB_B": 16,
"L_RGB_G": 21,
"L_RGB_R": 4,
"L_Y": 23,
"MISO": 22,
"MOSI": 21,
"PHOTO": 25,
"PIEZO1": 19,
"PIEZO2": 18,
"POT1": 32,
"POT2": 33,
"S1": 4,
"S2": 16,
"S3": 18,
"S4": 19,
"S5": 21,
"SCK": 23,
"SCL": 14,
"SDA": 27,
"SS": 19,
"SW1": 15,
"SW2": 2,
"SW3": 0,
},
"az-delivery-devkit-v4": {},
"bpi-bit": {
"BUTTON_A": 35,
"BUTTON_B": 27,
"BUZZER": 25,
"LIGHT_SENSOR1": 36,
"LIGHT_SENSOR2": 39,
"MPU9250_INT": 0,
"P0": 25,
"P1": 32,
"P10": 26,
"P11": 27,
"P12": 2,
"P13": 18,
"P14": 19,
"P15": 23,
"P16": 5,
"P19": 22,
"P2": 33,
"P20": 21,
"P3": 13,
"P4": 15,
"P5": 35,
"P6": 12,
"P7": 14,
"P8": 16,
"P9": 17,
"RGB_LED": 4,
"TEMPERATURE_SENSOR": 34,
},
"briki_abc_esp32": {},
"briki_mbc-wb_esp32": {},
"d-duino-32": {
"D1": 5,
"D10": 1,
"D2": 4,
"D3": 0,
"D4": 2,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 15,
"D9": 3,
"MISO": 12,
"MOSI": 13,
"SCK": 14,
"SCL": 4,
"SDA": 5,
"SS": 15,
},
"esp-wrover-kit": {},
"esp32-devkitlipo": {},
"esp32-evb": {
"BUTTON": 34,
"MISO": 15,
"MOSI": 2,
"SCK": 14,
"SCL": 16,
"SDA": 13,
"SS": 17,
},
"esp32-gateway": {"BUTTON": 34, "LED": 33, "SCL": 16, "SDA": 32},
"esp32-poe-iso": {
"BUTTON": 34,
"MISO": 15,
"MOSI": 2,
"SCK": 14,
"SCL": 16,
"SDA": 13,
},
"esp32-poe": {"BUTTON": 34, "MISO": 15, "MOSI": 2, "SCK": 14, "SCL": 16, "SDA": 13},
"esp32-pro": {
"BUTTON": 34,
"MISO": 15,
"MOSI": 2,
"SCK": 14,
"SCL": 16,
"SDA": 13,
"SS": 17,
},
"esp320": {
"LED": 5,
"MISO": 12,
"MOSI": 13,
"SCK": 14,
"SCL": 14,
"SDA": 2,
"SS": 15,
},
"esp32cam": {},
"esp32dev": {},
"esp32doit-devkit-v1": {"LED": 2},
"esp32doit-espduino": {"TX0": 1, "RX0": 3, "CMD": 11, "CLK": 6, "SD0": 7, "SD1": 8},
"esp32thing": {"BUTTON": 0, "LED": 5, "SS": 2},
"esp32thing_plus": {
"SDA": 23,
"SCL": 22,
"SS": 33,
"MOSI": 18,
"MISO": 19,
"SCK": 5,
"A0": 26,
"A1": 25,
"A2": 34,
"A3": 39,
"A4": 36,
"A5": 4,
"A6": 14,
"A7": 32,
"A8": 15,
"A9": 33,
"A10": 27,
"A11": 12,
"A12": 13,
},
"esp32vn-iot-uno": {},
"espea32": {"BUTTON": 0, "LED": 5},
"espectro32": {"LED": 15, "SD_SS": 33},
"espino32": {"BUTTON": 0, "LED": 16},
"etboard": {
"LED_BUILTIN": 5,
"TX": 34,
"RX": 35,
"SS": 29,
"MOSI": 37,
"MISO": 31,
"SCK": 30,
"A0": 36,
"A1": 39,
"A2": 32,
"A3": 33,
"A4": 34,
"A5": 35,
"A6": 25,
"A7": 26,
"D2": 27,
"D3": 14,
"D4": 12,
"D5": 13,
"D6": 15,
"D7": 16,
"D8": 17,
"D9": 4,
},
"featheresp32": {
"A0": 26,
"A1": 25,
"A10": 27,
"A11": 12,
"A12": 13,
"A13": 35,
"A2": 34,
"A4": 36,
"A5": 4,
"A6": 14,
"A7": 32,
"A8": 15,
"A9": 33,
"Ax": 2,
"LED": 13,
"MOSI": 18,
"RX": 16,
"SCK": 5,
"SDA": 23,
"SS": 33,
"TX": 17,
},
"firebeetle32": {"LED": 2},
"fm-devkit": {
"D0": 34,
"D1": 35,
"D10": 0,
"D2": 32,
"D3": 33,
"D4": 27,
"D5": 14,
"D6": 12,
"D7": 13,
"D8": 15,
"D9": 23,
"I2S_DOUT": 22,
"I2S_LRCLK": 25,
"I2S_MCLK": 2,
"I2S_SCLK": 26,
"LED": 5,
"SCL": 17,
"SDA": 16,
"SW1": 4,
"SW2": 18,
"SW3": 19,
"SW4": 21,
},
"frogboard": {},
"healtypi4": {
"KEY_BUILTIN": 17,
"ADS1292_DRDY_PIN": 26,
"ADS1292_CS_PIN": 13,
"ADS1292_START_PIN": 14,
"ADS1292_PWDN_PIN": 27,
"AFE4490_CS_PIN": 21,
"AFE4490_DRDY_PIN": 39,
"AFE4490_PWDN_PIN": 4,
"PUSH_BUTTON": 17,
"SLIDE_SWITCH": 16,
},
"heltec_wifi_kit_32": {
"A1": 37,
"A2": 38,
"BUTTON": 0,
"LED": 25,
"RST_OLED": 16,
"SCL_OLED": 15,
"SDA_OLED": 4,
"Vext": 21,
},
"heltec_wifi_kit_32_v2": "heltec_wifi_kit_32",
"heltec_wifi_lora_32": {
"BUTTON": 0,
"DIO0": 26,
"DIO1": 33,
"DIO2": 32,
"LED": 25,
"MOSI": 27,
"RST_LoRa": 14,
"RST_OLED": 16,
"SCK": 5,
"SCL_OLED": 15,
"SDA_OLED": 4,
"SS": 18,
"Vext": 21,
},
"heltec_wifi_lora_32_V2": {
"BUTTON": 0,
"DIO0": 26,
"DIO1": 35,
"DIO2": 34,
"LED": 25,
"MOSI": 27,
"RST_LoRa": 14,
"RST_OLED": 16,
"SCK": 5,
"SCL_OLED": 15,
"SDA_OLED": 4,
"SS": 18,
"Vext": 21,
},
"heltec_wireless_stick": {
"BUTTON": 0,
"DIO0": 26,
"DIO1": 35,
"DIO2": 34,
"LED": 25,
"MOSI": 27,
"RST_LoRa": 14,
"RST_OLED": 16,
"SCK": 5,
"SCL_OLED": 15,
"SDA_OLED": 4,
"SS": 18,
"Vext": 21,
},
"heltec_wireless_stick_lite": {
"LED_BUILTIN": 25,
"KEY_BUILTIN": 0,
"SS": 18,
"MOSI": 27,
"MISO": 19,
"SCK": 5,
"Vext": 21,
"LED": 25,
"RST_LoRa": 14,
"DIO0": 26,
"DIO1": 35,
"DIO2": 34,
},
"honeylemon": {
"LED_BUILTIN": 2,
"BUILTIN_KEY": 0,
},
"hornbill32dev": {"BUTTON": 0, "LED": 13},
"hornbill32minima": {"SS": 2},
"imbrios-logsens-v1p1": {
"LED_BUILTIN": 33,
"UART2_TX": 17,
"UART2_RX": 16,
"UART2_RTS": 4,
"CAN_TX": 17,
"CAN_RX": 16,
"CAN_TXDE": 4,
"SS": 15,
"MOSI": 13,
"MISO": 12,
"SCK": 14,
"SPI_SS1": 23,
"BUZZER_CTRL": 19,
"SD_CARD_DETECT": 35,
"SW2_BUILDIN": 0,
"SW3_BUILDIN": 36,
"SW4_BUILDIN": 34,
"LED1_BUILDIN": 32,
"LED2_BUILDIN": 33,
},
"inex_openkb": {
"LED_BUILTIN": 16,
"LDR_PIN": 36,
"SW1": 16,
"SW2": 14,
"BT_LED": 17,
"WIFI_LED": 2,
"NTP_LED": 15,
"IOT_LED": 12,
"BUZZER": 13,
"INPUT1": 32,
"INPUT2": 33,
"INPUT3": 34,
"INPUT4": 35,
"OUTPUT1": 26,
"OUTPUT2": 27,
"SDA0": 21,
"SCL0": 22,
"SDA1": 4,
"SCL1": 5,
},
"intorobot": {
"A1": 39,
"A2": 35,
"A3": 25,
"A4": 26,
"A5": 14,
"A6": 12,
"A7": 15,
"A8": 13,
"A9": 2,
"BUTTON": 0,
"D0": 19,
"D1": 23,
"D2": 18,
"D3": 17,
"D4": 16,
"D5": 5,
"D6": 4,
"LED": 4,
"MISO": 17,
"MOSI": 16,
"RGB_B_BUILTIN": 22,
"RGB_G_BUILTIN": 21,
"RGB_R_BUILTIN": 27,
"SCL": 19,
"SDA": 23,
"T0": 19,
"T1": 23,
"T2": 18,
"T3": 17,
"T4": 16,
"T5": 5,
"T6": 4,
},
"iotaap_magnolia": {},
"iotbusio": {},
"iotbusproteus": {},
"kits-edu": {},
"labplus_mpython": {
"SDA": 23,
"SCL": 22,
"P0": 33,
"P1": 32,
"P2": 35,
"P3": 34,
"P4": 39,
"P5": 0,
"P6": 16,
"P7": 17,
"P8": 26,
"P9": 25,
"P10": 36,
"P11": 2,
"P13": 18,
"P14": 19,
"P15": 21,
"P16": 5,
"P19": 22,
"P20": 23,
"P": 27,
"Y": 14,
"T": 12,
"H": 13,
"O": 15,
"N": 4,
"BTN_A": 0,
"BTN_B": 2,
"SOUND": 36,
"LIGHT": 39,
"BUZZER": 16,
},
"lolin32": {"LED": 5},
"lolin32_lite": {"LED": 22},
"lolin_d32": {"LED": 5, "_VBAT": 35},
"lolin_d32_pro": {"LED": 5, "_VBAT": 35},
"lopy": {
"A1": 37,
"A2": 38,
"LED": 0,
"MISO": 37,
"MOSI": 22,
"SCK": 13,
"SCL": 13,
"SDA": 12,
"SS": 17,
},
"lopy4": {
"A1": 37,
"A2": 38,
"LED": 0,
"MISO": 37,
"MOSI": 22,
"SCK": 13,
"SCL": 13,
"SDA": 12,
"SS": 18,
},
"m5stack-atom": {
"SDA": 26,
"SCL": 32,
"ADC1": 35,
"ADC2": 36,
"SS": 19,
"MOSI": 33,
"MISO": 23,
"SCK": 22,
},
"m5stack-core-esp32": {
"ADC1": 35,
"ADC2": 36,
"G0": 0,
"G1": 1,
"G12": 12,
"G13": 13,
"G15": 15,
"G16": 16,
"G17": 17,
"G18": 18,
"G19": 19,
"G2": 2,
"G21": 21,
"G22": 22,
"G23": 23,
"G25": 25,
"G26": 26,
"G3": 3,
"G34": 34,
"G35": 35,
"G36": 36,
"G5": 5,
"RXD2": 16,
"TXD2": 17,
},
"m5stack-core2": {
"SDA": 32,
"SCL": 33,
"SS": 5,
"MOSI": 23,
"MISO": 38,
"SCK": 18,
"ADC1": 35,
"ADC2": 36,
},
"m5stack-coreink": {
"SDA": 32,
"SCL": 33,
"SS": 9,
"MOSI": 23,
"MISO": 34,
"SCK": 18,
"ADC1": 35,
"ADC2": 36,
},
"m5stack-fire": {
"ADC1": 35,
"ADC2": 36,
"G0": 0,
"G1": 1,
"G12": 12,
"G13": 13,
"G15": 15,
"G16": 16,
"G17": 17,
"G18": 18,
"G19": 19,
"G2": 2,
"G21": 21,
"G22": 22,
"G23": 23,
"G25": 25,
"G26": 26,
"G3": 3,
"G34": 34,
"G35": 35,
"G36": 36,
"G5": 5,
},
"m5stack-grey": {
"ADC1": 35,
"ADC2": 36,
"G0": 0,
"G1": 1,
"G12": 12,
"G13": 13,
"G15": 15,
"G16": 16,
"G17": 17,
"G18": 18,
"G19": 19,
"G2": 2,
"G21": 21,
"G22": 22,
"G23": 23,
"G25": 25,
"G26": 26,
"G3": 3,
"G34": 34,
"G35": 35,
"G36": 36,
"G5": 5,
"RXD2": 16,
"TXD2": 17,
},
"m5stack-timer-cam": {
"LED_BUILTIN": 2,
"SDA": 4,
"SCL": 13,
"SS": 5,
"MOSI": 23,
"MISO": 19,
"SCK": 18,
"ADC1": 35,
"ADC2": 36,
},
"m5stick-c": {
"ADC1": 35,
"ADC2": 36,
"G0": 0,
"G10": 10,
"G26": 26,
"G32": 32,
"G33": 33,
"G36": 36,
"G37": 37,
"G39": 39,
"G9": 9,
"MISO": 36,
"MOSI": 15,
"SCK": 13,
"SCL": 33,
"SDA": 32,
},
"magicbit": {
"BLUE_LED": 17,
"BUZZER": 25,
"GREEN_LED": 16,
"LDR": 36,
"LED": 16,
"LEFT_BUTTON": 35,
"MOTOR1A": 27,
"MOTOR1B": 18,
"MOTOR2A": 16,
"MOTOR2B": 17,
"POT": 39,
"RED_LED": 27,
"RIGHT_PUTTON": 34,
"YELLOW_LED": 18,
},
"mgbot-iotik32a": {
"LED_BUILTIN": 4,
"TX2": 17,
"RX2": 16,
},
"mgbot-iotik32b": {
"LED_BUILTIN": 18,
"IR": 27,
"TX2": 17,
"RX2": 16,
},
"mhetesp32devkit": {"LED": 2},
"mhetesp32minikit": {"LED": 2},
"microduino-core-esp32": {
"A0": 12,
"A1": 13,
"A10": 25,
"A11": 26,
"A12": 27,
"A13": 14,
"A2": 15,
"A3": 4,
"A6": 38,
"A7": 37,
"A8": 32,
"A9": 33,
"D0": 3,
"D1": 1,
"D10": 5,
"D11": 23,
"D12": 19,
"D13": 18,
"D14": 12,
"D15": 13,
"D16": 15,
"D17": 4,
"D18": 22,
"D19": 21,
"D2": 16,
"D20": 38,
"D21": 37,
"D3": 17,
"D4": 32,
"D5": 33,
"D6": 25,
"D7": 26,
"D8": 27,
"D9": 14,
"SCL": 21,
"SCL1": 13,
"SDA": 22,
"SDA1": 12,
},
"nano32": {"BUTTON": 0, "LED": 16},
"nina_w10": {
"D0": 3,
"D1": 1,
"D10": 5,
"D11": 19,
"D12": 23,
"D13": 18,
"D14": 13,
"D15": 12,
"D16": 32,
"D17": 33,
"D18": 21,
"D19": 34,
"D2": 26,
"D20": 36,
"D21": 39,
"D3": 25,
"D4": 35,
"D5": 27,
"D6": 22,
"D7": 0,
"D8": 15,
"D9": 14,
"LED_BLUE": 21,
"LED_GREEN": 33,
"LED_RED": 23,
"SCL": 13,
"SDA": 12,
"SW1": 33,
"SW2": 27,
},
"node32s": {},
"nodemcu-32s": {"BUTTON": 0, "LED": 2},
"nscreen-32": {},
"odroid_esp32": {"ADC1": 35, "ADC2": 36, "LED": 2, "SCL": 4, "SDA": 15, "SS": 22},
"onehorse32dev": {"A1": 37, "A2": 38, "BUTTON": 0, "LED": 5},
"oroca_edubot": {
"A0": 34,
"A1": 39,
"A2": 36,
"A3": 33,
"D0": 4,
"D1": 16,
"D2": 17,
"D3": 22,
"D4": 23,
"D5": 5,
"D6": 18,
"D7": 19,
"D8": 33,
"LED": 13,
"MOSI": 18,
"RX": 16,
"SCK": 5,
"SDA": 23,
"SS": 2,
"TX": 17,
"VBAT": 35,
},
"pico32": {},
"piranha_esp32": {
"LED_BUILTIN": 2,
"KEY_BUILTIN": 0,
},
"pocket_32": {"LED": 16},
"pycom_gpy": {
"A1": 37,
"A2": 38,
"LED": 0,
"MISO": 37,
"MOSI": 22,
"SCK": 13,
"SCL": 13,
"SDA": 12,
"SS": 17,
},
"qchip": "heltec_wifi_kit_32",
"quantum": {},
"s_odi_ultra": {
"LED_BUILTIN": 2,
"LED_BUILTINB": 4,
},
"sensesiot_weizen": {},
"sg-o_airMon": {},
"sparkfun_lora_gateway_1-channel": {"MISO": 12, "MOSI": 13, "SCK": 14, "SS": 16},
"tinypico": {},
"ttgo-lora32-v1": {
"A1": 37,
"A2": 38,
"BUTTON": 0,
"LED": 2,
"MOSI": 27,
"SCK": 5,
"SS": 18,
},
"ttgo-lora32-v2": {
"LED_BUILTIN": 22,
"KEY_BUILTIN": 0,
"SS": 18,
"MOSI": 27,
"MISO": 19,
"SCK": 5,
"A1": 37,
"A2": 38,
},
"ttgo-lora32-v21": {
"LED_BUILTIN": 25,
"KEY_BUILTIN": 0,
"SS": 18,
"MOSI": 27,
"MISO": 19,
"SCK": 5,
"A1": 37,
"A2": 38,
},
"ttgo-t-beam": {"BUTTON": 39, "LED": 14, "MOSI": 27, "SCK": 5, "SS": 18},
"ttgo-t-watch": {"BUTTON": 36, "MISO": 2, "MOSI": 15, "SCK": 14, "SS": 13},
"ttgo-t1": {"LED": 22, "MISO": 2, "MOSI": 15, "SCK": 14, "SCL": 23, "SS": 13},
"ttgo-t7-v13-mini32": {"LED": 22},
"ttgo-t7-v14-mini32": {"LED": 19},
"turta_iot_node": {},
"vintlabs-devkit-v1": {
"LED": 2,
"PWM0": 12,
"PWM1": 13,
"PWM2": 14,
"PWM3": 15,
"PWM4": 16,
"PWM5": 17,
"PWM6": 18,
"PWM7": 19,
},
"wemos_d1_mini32": {
"D0": 26,
"D1": 22,
"D2": 21,
"D3": 17,
"D4": 16,
"D5": 18,
"D6": 19,
"D7": 23,
"D8": 5,
"LED": 2,
"RXD": 3,
"TXD": 1,
"_VBAT": 35,
},
"wemosbat": {"LED": 16},
"wesp32": {"MISO": 32, "SCL": 4, "SDA": 15},
"widora-air": {
"A1": 39,
"A2": 35,
"A3": 25,
"A4": 26,
"A5": 14,
"A6": 12,
"A7": 15,
"A8": 13,
"A9": 2,
"BUTTON": 0,
"D0": 19,
"D1": 23,
"D2": 18,
"D3": 17,
"D4": 16,
"D5": 5,
"D6": 4,
"LED": 25,
"MISO": 17,
"MOSI": 16,
"SCL": 19,
"SDA": 23,
"T0": 19,
"T1": 23,
"T2": 18,
"T3": 17,
"T4": 16,
"T5": 5,
"T6": 4,
},
"wifiduino32": {
"LED_BUILTIN": 2,
"KEY_BUILTIN": 0,
"SDA": 5,
"SCL": 16,
"A0": 27,
"A1": 14,
"A2": 12,
"A3": 35,
"A4": 13,
"A5": 4,
"D0": 3,
"D1": 1,
"D2": 17,
"D3": 15,
"D4": 32,
"D5": 33,
"D6": 25,
"D7": 26,
"D8": 23,
"D9": 22,
"D10": 21,
"D11": 19,
"D12": 18,
"D13": 2,
},
"xinabox_cw02": {"LED": 27},
}

View File

@@ -0,0 +1,21 @@
import esphome.codegen as cg
KEY_ESP32 = "esp32"
KEY_BOARD = "board"
KEY_VARIANT = "variant"
KEY_SDKCONFIG_OPTIONS = "sdkconfig_options"
VARIANT_ESP32 = "ESP32"
VARIANT_ESP32S2 = "ESP32S2"
VARIANT_ESP32S3 = "ESP32S3"
VARIANT_ESP32C3 = "ESP32C3"
VARIANT_ESP32H2 = "ESP32H2"
VARIANTS = [
VARIANT_ESP32,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
VARIANT_ESP32C3,
VARIANT_ESP32H2,
]
esp32_ns = cg.esphome_ns.namespace("esp32")

View File

@@ -0,0 +1,89 @@
#ifdef USE_ESP32
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "preferences.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_idf_version.h>
#include <soc/rtc.h>
#if ESP_IDF_VERSION_MAJOR >= 4
#include <hal/cpu_hal.h>
#endif
void setup();
void loop();
namespace esphome {
void IRAM_ATTR HOT yield() { vPortYield(); }
uint32_t IRAM_ATTR HOT millis() { return (uint32_t)(esp_timer_get_time() / 1000ULL); }
void IRAM_ATTR HOT delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); }
uint32_t IRAM_ATTR HOT micros() { return (uint32_t) esp_timer_get_time(); }
void IRAM_ATTR HOT delayMicroseconds(uint32_t us) {
auto start = (uint64_t) esp_timer_get_time();
while (((uint64_t) esp_timer_get_time()) - start < us)
;
}
void arch_restart() {
esp_restart();
// restart() doesn't always end execution
while (true) { // NOLINT(clang-diagnostic-unreachable-code)
yield();
}
}
void IRAM_ATTR HOT arch_feed_wdt() {
#ifdef USE_ARDUINO
#if CONFIG_ARDUINO_RUNNING_CORE == 0
#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
// ESP32 uses "Task Watchdog" which is hooked to the FreeRTOS idle task.
// To cause the Watchdog to be triggered we need to put the current task
// to sleep to get the idle task scheduled.
delay(1);
#endif
#endif
#endif // USE_ARDUINO
#ifdef USE_ESP_IDF
#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
delay(1);
#endif
#endif // USE_ESP_IDF
}
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
uint32_t arch_get_cpu_cycle_count() {
#if ESP_IDF_VERSION_MAJOR >= 4
return cpu_hal_get_cycle_count();
#else
uint32_t ccount;
__asm__ __volatile__("esync; rsr %0,ccount" : "=a"(ccount));
return ccount;
#endif
}
uint32_t arch_get_cpu_freq_hz() { return rtc_clk_apb_freq_get(); }
#ifdef USE_ESP_IDF
TaskHandle_t loop_task_handle = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
void loop_task(void *pv_params) {
setup();
while (true) {
loop();
}
}
extern "C" void app_main() {
esp32::setup_preferences();
xTaskCreate(loop_task, "loopTask", 8192, nullptr, 1, &loop_task_handle);
}
#endif // USE_ESP_IDF
#ifdef USE_ARDUINO
extern "C" void init() { esp32::setup_preferences(); }
#endif // USE_ARDUINO
} // namespace esphome
#endif // USE_ESP32

View File

@@ -0,0 +1,201 @@
import logging
from esphome.const import (
CONF_ID,
CONF_INPUT,
CONF_INVERTED,
CONF_MODE,
CONF_NUMBER,
CONF_OPEN_DRAIN,
CONF_OUTPUT,
CONF_PULLDOWN,
CONF_PULLUP,
)
from esphome import pins
from esphome.core import CORE
import esphome.config_validation as cv
import esphome.codegen as cg
from . import boards
from .const import KEY_BOARD, KEY_ESP32, esp32_ns
_LOGGER = logging.getLogger(__name__)
IDFInternalGPIOPin = esp32_ns.class_("IDFInternalGPIOPin", cg.InternalGPIOPin)
ArduinoInternalGPIOPin = esp32_ns.class_("ArduinoInternalGPIOPin", cg.InternalGPIOPin)
def _lookup_pin(value):
board = CORE.data[KEY_ESP32][KEY_BOARD]
board_pins = boards.ESP32_BOARD_PINS.get(board, {})
# Resolved aliased board pins (shorthand when two boards have the same pin configuration)
while isinstance(board_pins, str):
board_pins = boards.ESP32_BOARD_PINS[board_pins]
if value in board_pins:
return board_pins[value]
if value in boards.ESP32_BASE_PINS:
return boards.ESP32_BASE_PINS[value]
raise cv.Invalid(f"Cannot resolve pin name '{value}' for board {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.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",
}
def validate_gpio_pin(value):
value = _translate_pin(value)
if value < 0 or value > 39:
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-39)")
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(
"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
def validate_supports(value):
num = value[CONF_NUMBER]
mode = value[CONF_MODE]
is_input = mode[CONF_INPUT]
is_output = mode[CONF_OUTPUT]
is_open_drain = mode[CONF_OPEN_DRAIN]
is_pullup = mode[CONF_PULLUP]
is_pulldown = mode[CONF_PULLDOWN]
if is_input:
# All ESP32 pins support input mode
pass
if is_output and 34 <= num <= 39:
raise cv.Invalid(
f"GPIO{num} (34-39) does not support output pin mode.",
[CONF_MODE, CONF_OUTPUT],
)
if is_open_drain and not is_output:
raise cv.Invalid(
"Open-drain only works with output mode", [CONF_MODE, CONF_OPEN_DRAIN]
)
if is_pullup and 34 <= num <= 39:
raise cv.Invalid(
f"GPIO{num} (34-39) does not support pullups.", [CONF_MODE, CONF_PULLUP]
)
if is_pulldown and 34 <= num <= 39:
raise cv.Invalid(
f"GPIO{num} (34-39) does not support pulldowns.", [CONF_MODE, CONF_PULLDOWN]
)
if CORE.using_arduino:
# (input, output, open_drain, pullup, pulldown)
supported_modes = {
# INPUT
(True, False, False, False, False),
# OUTPUT
(False, True, False, False, False),
# INPUT_PULLUP
(True, False, False, True, False),
# INPUT_PULLDOWN
(True, False, False, False, True),
# OUTPUT_OPEN_DRAIN
(False, True, True, False, False),
}
key = (is_input, is_output, is_open_drain, is_pullup, is_pulldown)
if key not in supported_modes:
raise cv.Invalid(
"This pin mode is not supported on ESP32 for arduino frameworks",
[CONF_MODE],
)
return value
# https://docs.espressif.com/projects/esp-idf/en/v3.3.5/api-reference/peripherals/gpio.html#_CPPv416gpio_drive_cap_t
gpio_drive_cap_t = cg.global_ns.enum("gpio_drive_cap_t")
DRIVE_STRENGTHS = {
5.0: gpio_drive_cap_t.GPIO_DRIVE_CAP_0,
10.0: gpio_drive_cap_t.GPIO_DRIVE_CAP_1,
20.0: gpio_drive_cap_t.GPIO_DRIVE_CAP_2,
40.0: gpio_drive_cap_t.GPIO_DRIVE_CAP_3,
}
gpio_num_t = cg.global_ns.enum("gpio_num_t")
def _choose_pin_declaration(value):
if CORE.using_esp_idf:
return cv.declare_id(IDFInternalGPIOPin)(value)
if CORE.using_arduino:
return cv.declare_id(ArduinoInternalGPIOPin)(value)
raise NotImplementedError
CONF_DRIVE_STRENGTH = "drive_strength"
ESP32_PIN_SCHEMA = cv.All(
{
cv.GenerateID(): _choose_pin_declaration,
cv.Required(CONF_NUMBER): validate_gpio_pin,
cv.Optional(CONF_MODE, default={}): cv.Schema(
{
cv.Optional(CONF_INPUT, default=False): cv.boolean,
cv.Optional(CONF_OUTPUT, default=False): cv.boolean,
cv.Optional(CONF_OPEN_DRAIN, default=False): cv.boolean,
cv.Optional(CONF_PULLUP, default=False): cv.boolean,
cv.Optional(CONF_PULLDOWN, default=False): cv.boolean,
}
),
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
cv.SplitDefault(CONF_DRIVE_STRENGTH, esp32_idf="20mA"): cv.All(
cv.only_with_esp_idf,
cv.float_with_unit("current", "mA", optional_unit=True),
cv.enum(DRIVE_STRENGTHS),
),
},
validate_supports,
)
@pins.PIN_SCHEMA_REGISTRY.register("esp32", ESP32_PIN_SCHEMA)
async def esp32_pin_to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
num = config[CONF_NUMBER]
if CORE.using_esp_idf:
cg.add(var.set_pin(getattr(gpio_num_t, f"GPIO_NUM_{num}")))
else:
cg.add(var.set_pin(num))
cg.add(var.set_inverted(config[CONF_INVERTED]))
if CONF_DRIVE_STRENGTH in config:
cg.add(var.set_drive_strength(config[CONF_DRIVE_STRENGTH]))
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
return var

View File

@@ -0,0 +1,107 @@
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#include "gpio_arduino.h"
#include "esphome/core/log.h"
#include <esp32-hal-gpio.h>
namespace esphome {
namespace esp32 {
static const char *const TAG = "esp32";
struct ISRPinArg {
uint8_t pin;
bool inverted;
};
ISRInternalGPIOPin ArduinoInternalGPIOPin::to_isr() const {
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
arg->pin = pin_;
arg->inverted = inverted_;
return ISRInternalGPIOPin((void *) arg);
}
void ArduinoInternalGPIOPin::attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const {
uint8_t arduino_mode = DISABLED;
switch (type) {
case gpio::INTERRUPT_RISING_EDGE:
arduino_mode = inverted_ ? FALLING : RISING;
break;
case gpio::INTERRUPT_FALLING_EDGE:
arduino_mode = inverted_ ? RISING : FALLING;
break;
case gpio::INTERRUPT_ANY_EDGE:
arduino_mode = CHANGE;
break;
case gpio::INTERRUPT_LOW_LEVEL:
arduino_mode = inverted_ ? ONHIGH : ONLOW;
break;
case gpio::INTERRUPT_HIGH_LEVEL:
arduino_mode = inverted_ ? ONLOW : ONHIGH;
break;
}
attachInterruptArg(pin_, func, arg, arduino_mode);
}
void ArduinoInternalGPIOPin::pin_mode(gpio::Flags flags) {
uint8_t mode;
if (flags == gpio::FLAG_INPUT) {
mode = INPUT;
} else if (flags == gpio::FLAG_OUTPUT) {
mode = OUTPUT;
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) {
mode = INPUT_PULLUP;
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLDOWN)) {
mode = INPUT_PULLDOWN;
} else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) {
mode = OUTPUT_OPEN_DRAIN;
} else {
return;
}
pinMode(pin_, mode); // NOLINT
}
std::string ArduinoInternalGPIOPin::dump_summary() const {
char buffer[32];
snprintf(buffer, sizeof(buffer), "GPIO%u", pin_);
return buffer;
}
bool ArduinoInternalGPIOPin::digital_read() {
return bool(digitalRead(pin_)) != inverted_; // NOLINT
}
void ArduinoInternalGPIOPin::digital_write(bool value) {
digitalWrite(pin_, value != inverted_ ? 1 : 0); // NOLINT
}
void ArduinoInternalGPIOPin::detach_interrupt() const {
detachInterrupt(pin_); // NOLINT
}
} // namespace esp32
using namespace esp32;
bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
return bool(digitalRead(arg->pin)) != arg->inverted; // NOLINT
}
void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) {
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
digitalWrite(arg->pin, value != arg->inverted ? 1 : 0); // NOLINT
}
void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() {
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
#ifdef CONFIG_IDF_TARGET_ESP32C3
GPIO.status_w1tc.val = 1UL << arg->pin;
#else
if (arg->pin < 32) {
GPIO.status_w1tc = 1UL << arg->pin;
} else {
GPIO.status1_w1tc.intr_st = 1UL << (arg->pin - 32);
}
#endif
}
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO

View File

@@ -0,0 +1,36 @@
#pragma once
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#include "esphome/core/hal.h"
namespace esphome {
namespace esp32 {
class ArduinoInternalGPIOPin : public InternalGPIOPin {
public:
void set_pin(uint8_t pin) { pin_ = pin; }
void set_inverted(bool inverted) { inverted_ = inverted; }
void set_flags(gpio::Flags flags) { flags_ = flags; }
void setup() override { pin_mode(flags_); }
void pin_mode(gpio::Flags flags) override;
bool digital_read() override;
void digital_write(bool value) override;
std::string dump_summary() const override;
void detach_interrupt() const override;
ISRInternalGPIOPin to_isr() const override;
uint8_t get_pin() const override { return pin_; }
bool is_inverted() const override { return inverted_; }
protected:
void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
uint8_t pin_;
bool inverted_;
gpio::Flags flags_;
};
} // namespace esp32
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO

View File

@@ -0,0 +1,49 @@
#ifdef USE_ESP32_FRAMEWORK_ESP_IDF
#include "gpio_idf.h"
#include "esphome/core/log.h"
namespace esphome {
namespace esp32 {
static const char *const TAG = "esp32";
bool IDFInternalGPIOPin::isr_service_installed_ = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
struct ISRPinArg {
gpio_num_t pin;
bool inverted;
};
ISRInternalGPIOPin IDFInternalGPIOPin::to_isr() const {
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
arg->pin = pin_;
arg->inverted = inverted_;
return ISRInternalGPIOPin((void *) arg);
}
std::string IDFInternalGPIOPin::dump_summary() const {
char buffer[32];
snprintf(buffer, sizeof(buffer), "GPIO%u", static_cast<uint32_t>(pin_));
return buffer;
}
} // namespace esp32
using namespace esp32;
bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
return bool(gpio_get_level(arg->pin)) != arg->inverted;
}
void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) {
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
gpio_set_level(arg->pin, value != arg->inverted ? 1 : 0);
}
void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() {
// not supported
}
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ESP_IDF

View File

@@ -0,0 +1,96 @@
#pragma once
#ifdef USE_ESP32_FRAMEWORK_ESP_IDF
#include "esphome/core/hal.h"
#include <driver/gpio.h>
namespace esphome {
namespace esp32 {
class IDFInternalGPIOPin : public InternalGPIOPin {
public:
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 {
pin_mode(flags_);
gpio_set_drive_capability(pin_, drive_strength_);
}
void pin_mode(gpio::Flags flags) override {
gpio_config_t conf{};
conf.pin_bit_mask = 1 << 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);
}
bool digital_read() override { return bool(gpio_get_level(pin_)) != inverted_; }
void digital_write(bool value) override { gpio_set_level(pin_, value != inverted_ ? 1 : 0); }
std::string dump_summary() const override;
void detach_interrupt() const override { gpio_intr_disable(pin_); }
ISRInternalGPIOPin to_isr() const override;
uint8_t get_pin() const override { return (uint8_t) pin_; }
bool is_inverted() const override { return inverted_; }
protected:
static gpio_mode_t flags_to_mode_(gpio::Flags flags) {
flags = (gpio::Flags)(flags & ~(gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN));
if (flags == gpio::FLAG_NONE) {
return GPIO_MODE_DISABLE;
} else if (flags == gpio::FLAG_INPUT) {
return GPIO_MODE_INPUT;
} else if (flags == gpio::FLAG_OUTPUT) {
return GPIO_MODE_OUTPUT;
} else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) {
return GPIO_MODE_OUTPUT_OD;
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) {
return GPIO_MODE_INPUT_OUTPUT_OD;
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT)) {
return GPIO_MODE_INPUT_OUTPUT;
} else {
// unsupported
return GPIO_MODE_DISABLE;
}
}
void attach_interrupt_(void (*func)(void *), void *arg, gpio::InterruptType type) const override {
gpio_int_type_t idf_type = GPIO_INTR_ANYEDGE;
switch (type) {
case gpio::INTERRUPT_RISING_EDGE:
idf_type = inverted_ ? GPIO_INTR_NEGEDGE : GPIO_INTR_POSEDGE;
break;
case gpio::INTERRUPT_FALLING_EDGE:
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 = inverted_ ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL;
break;
case gpio::INTERRUPT_HIGH_LEVEL:
idf_type = inverted_ ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL;
break;
}
gpio_set_intr_type(pin_, idf_type);
gpio_intr_enable(pin_);
if (!isr_service_installed_) {
gpio_install_isr_service(ESP_INTR_FLAG_LEVEL5);
isr_service_installed_ = true;
}
gpio_isr_handler_add(pin_, func, arg);
}
gpio_num_t pin_;
bool inverted_;
gpio_drive_cap_t drive_strength_;
gpio::Flags flags_;
static bool isr_service_installed_;
};
} // namespace esp32
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ESP_IDF

View File

@@ -0,0 +1,99 @@
#ifdef USE_ESP32
#include "esphome/core/preferences.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <nvs_flash.h>
namespace esphome {
namespace esp32 {
static const char *const TAG = "esp32.preferences";
class ESP32PreferenceBackend : public ESPPreferenceBackend {
public:
std::string key;
uint32_t nvs_handle;
bool save(const uint8_t *data, size_t len) override {
esp_err_t err = nvs_set_blob(nvs_handle, key.c_str(), data, len);
if (err != 0) {
ESP_LOGV(TAG, "nvs_set_blob('%s', len=%u) failed: %s", key.c_str(), len, esp_err_to_name(err));
return false;
}
err = nvs_commit(nvs_handle);
if (err != 0) {
ESP_LOGV(TAG, "nvs_commit('%s', len=%u) failed: %s", key.c_str(), len, esp_err_to_name(err));
return false;
}
return true;
}
bool load(uint8_t *data, size_t len) override {
size_t actual_len;
esp_err_t err = nvs_get_blob(nvs_handle, key.c_str(), nullptr, &actual_len);
if (err != 0) {
ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key.c_str(), esp_err_to_name(err));
return false;
}
if (actual_len != len) {
ESP_LOGVV(TAG, "NVS length does not match (%u!=%u)", actual_len, len);
return false;
}
err = nvs_get_blob(nvs_handle, key.c_str(), data, &len);
if (err != 0) {
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key.c_str(), esp_err_to_name(err));
return false;
}
return true;
}
};
class ESP32Preferences : public ESPPreferences {
public:
uint32_t nvs_handle;
uint32_t current_offset = 0;
void open() {
esp_err_t err = nvs_open("esphome", NVS_READWRITE, &nvs_handle);
if (err == 0)
return;
ESP_LOGW(TAG, "nvs_open failed: %s - erasing NVS...", esp_err_to_name(err));
nvs_flash_deinit();
nvs_flash_erase();
nvs_flash_init();
err = nvs_open("esphome", NVS_READWRITE, &nvs_handle);
if (err != 0) {
nvs_handle = 0;
}
}
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override {
return make_preference(length, type);
}
ESPPreferenceObject make_preference(size_t length, uint32_t type) override {
auto *pref = new ESP32PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
pref->nvs_handle = nvs_handle;
current_offset += length;
uint32_t keyval = current_offset ^ type;
char keybuf[16];
snprintf(keybuf, sizeof(keybuf), "%d", keyval);
pref->key = keybuf; // copied to std::string
return ESPPreferenceObject(pref);
}
};
void setup_preferences() {
auto *prefs = new ESP32Preferences(); // NOLINT(cppcoreguidelines-owning-memory)
prefs->open();
global_preferences = prefs;
}
} // namespace esp32
ESPPreferences *global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace esphome
#endif // USE_ESP32

View File

@@ -0,0 +1,12 @@
#pragma once
#ifdef USE_ESP32
namespace esphome {
namespace esp32 {
void setup_preferences();
} // namespace esp32
} // namespace esphome
#endif // USE_ESP32