Arduino core 3.2.1

This commit is contained in:
Jason2866
2025-07-03 17:12:25 +02:00
parent da5cb384a5
commit 9bf8b3f10a
76 changed files with 493576 additions and 1697 deletions
+20 -8
View File
@@ -1,4 +1,4 @@
name: CI Examples
name: CI Examples Arduino IDF 5.4.x based
on:
workflow_dispatch: # Manually start a workflow
@@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, windows-2022, macos-15]
os: [ubuntu-latest, windows-latest, macos-15]
example:
- "examples/arduino-blink"
- "examples/arduino-rmt-blink"
@@ -20,10 +20,10 @@ jobs:
- "examples/arduino-wifiscan"
- "examples/arduino-zigbee-light"
- "examples/arduino-zigbee-switch"
- "examples/tasmota"
- "examples/espidf-arduino-h2zero-BLE_scan"
#- "examples/espidf-arduino-matter-light"
- "examples/arduino-NimBLE-ext_client"
- "examples/arduino-matter-light"
- "examples/tasmota"
- "examples/espidf-arduino-matter-light"
- "examples/espidf-arduino-blink"
- "examples/espidf-arduino-littlefs"
- "examples/espidf-blink"
@@ -37,6 +37,15 @@ jobs:
- "examples/espidf-ulp"
- "examples/espidf-ulp-riscv"
- "examples/espidf-ulp-lp"
exclude:
- os: windows-latest
example: "examples/espidf-ulp"
- os: windows-latest
example: "examples/espidf-ulp-lp"
- os: windows-latest
example: "examples/espidf-ulp-riscv"
- os: windows-latest
example: "examples/espidf-arduino-matter-light"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
@@ -48,13 +57,16 @@ jobs:
python-version: "3.13"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install wheel
pip install -U https://github.com/pioarduino/platformio-core/archive/refs/tags/v6.1.18.zip
pip install uv
uv pip install --system -U https://github.com/pioarduino/platformio-core/archive/refs/tags/v6.1.18.zip
pio pkg install --global --platform file://.
- name: git clone Tasmota and add to examples
if: "matrix.example == 'examples/tasmota'"
run: |
git clone -b development --depth 1 https://github.com/arendst/Tasmota.git examples/tasmota
cp examples/tasmota_platformio_override.ini examples/tasmota/platformio_override.ini
- name: Build examples
env:
PYTHONIOENCODING: utf-8
PYTHONUTF8: '1'
run: pio run -d ${{ matrix.example }}
+4 -3
View File
@@ -4,12 +4,13 @@
[![Discord](https://img.shields.io/discord/1263397951829708871.svg?logo=discord&logoColor=white&color=5865F2&label=Discord)](https://discord.gg/Nutz9crnZr)
[![GitHub Releases](https://img.shields.io/github/downloads/pioarduino/platform-espressif32/total?label=downloads)](https://github.com/pioarduino/platform-espressif32/releases/latest)
ESP32 is a series of low-cost, low-power system on a chip microcontrollers with integrated Wi-Fi and Bluetooth. ESP32 integrates an antenna switch, RF balun, power amplifier, low-noise receive amplifier, filters, and power management modules.
Espressif Systems is a privately held, fabless semiconductor company renowned for delivering cost-effective wireless communication microcontrollers. Their innovative solutions are widely adopted in mobile devices and Internet of Things (IoT) applications around the globe.
## General
* Issues with boards (wrong / missing). All issues caused from boards will not be fixed from the maintainer(s). A PR needs to be provided against branch `develop` to solve.
* No support for the Arduino Nora Nano board, issues needs to be solved by the community
## IDE Preparation
- [Download and install Microsoft Visual Studio Code](https://code.visualstudio.com/). pioarduino IDE is on top of it.
- Open the extension manager.
- Search for the `pioarduino ide` extension.
@@ -24,7 +25,7 @@ ESP32 is a series of low-cost, low-power system on a chip microcontrollers with
The Wiki is AI generated and insane detailed and accurate.
### Stable Arduino
currently espressif Arduino 3.2.0 and IDF 5.4.1
currently espressif Arduino 3.2.1 and IDF 5.4.2
```ini
[env:stable]
+1 -1
View File
@@ -42,7 +42,7 @@
"platforms": [
"espressif32"
],
"name": "Espressif ESP32-S3-DevKitC-1-N16R8V (16 MB Flash Quad, 16 MB PSRAM Octal)",
"name": "Espressif ESP32-S3-DevKitC-1-N16R16V (16 MB Flash Quad, 16 MB PSRAM Octal)",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
+1 -1
View File
@@ -42,7 +42,7 @@
"platforms": [
"espressif32"
],
"name": "Espressif ESP32-S3-DevKitC-1-N8R8 (16 MB Flash Quad, 2 MB PSRAM Quad)",
"name": "Espressif ESP32-S3-DevKitC-1-N16R2 (16 MB Flash Quad, 2 MB PSRAM Quad)",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 327680,
+1 -1
View File
@@ -42,7 +42,7 @@
"platforms": [
"espressif32"
],
"name": "Espressif ESP32-S3-DevKitC-1-N8R8 (4 MB Flash Quad, 2 MB PSRAM Quad)",
"name": "Espressif ESP32-S3-DevKitC-1-N4R2 (4 MB Flash Quad, 2 MB PSRAM Quad)",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 327680,
+1 -1
View File
@@ -42,7 +42,7 @@
"platforms": [
"espressif32"
],
"name": "Espressif ESP32-S3-DevKitC-1-N8R8 (4 MB Flash Quad, 8 MB PSRAM Octal)",
"name": "Espressif ESP32-S3-DevKitC-1-N4R8 (4 MB Flash Quad, 8 MB PSRAM Octal)",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 327680,
+1 -1
View File
@@ -42,7 +42,7 @@
"platforms": [
"espressif32"
],
"name": "Espressif ESP32-S3-DevKitC-1-N8R8 (8 MB Flash Quad, 2 MB PSRAM quad)",
"name": "Espressif ESP32-S3-DevKitC-1-N8R2 (8 MB Flash Quad, 2 MB PSRAM quad)",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 327680,
+42
View File
@@ -0,0 +1,42 @@
{
"build": {
"arduino": {
"memory_type": "qio_opi",
"partitions": "default_8MB.csv"
},
"core": "esp32",
"extra_flags": [
"-DARDUINO_M5Stack_ATOMS3U",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "dio",
"mcu": "esp32s3",
"variant": "m5stack_atoms3"
},
"connectivity": [
"bluetooth",
"wifi"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "M5Stack AtomS3U",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"require_upload_port": true,
"speed": 460800
},
"url": "https://docs.m5stack.com/en/core/AtomS3U",
"vendor": "M5Stack"
}
-2
View File
@@ -41,8 +41,6 @@
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
+49
View File
@@ -0,0 +1,49 @@
{
"build": {
"arduino":{
"partitions": "default_8MB.csv",
"memory_type": "qio_qspi"
},
"core": "esp32",
"extra_flags": [
"-DARDUINO_EDGES3[D]",
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0X303A",
"0x82DC"
]
],
"mcu": "esp32s3",
"variant": "um_edges3_d"
},
"connectivity": [
"bluetooth",
"wifi"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "Unexpected Maker EDGES3[D]",
"upload": {
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"require_upload_port": true,
"speed": 460800
},
"url": "https://unexpectedmaker.com/",
"vendor": "Unexpected Maker"
}
-2
View File
@@ -36,8 +36,6 @@
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 921600
},
-2
View File
@@ -36,8 +36,6 @@
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 921600
},
-2
View File
@@ -41,8 +41,6 @@
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
-2
View File
@@ -41,8 +41,6 @@
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
-2
View File
@@ -41,8 +41,6 @@
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
-2
View File
@@ -41,8 +41,6 @@
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
-2
View File
@@ -41,8 +41,6 @@
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
-2
View File
@@ -36,8 +36,6 @@
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 921600
},
+49
View File
@@ -0,0 +1,49 @@
{
"build": {
"arduino": {
"partitions": "default_16MB.csv",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_SQUIXL",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
[
"0x303A",
"0x82DF"
]
],
"mcu": "esp32s3",
"variant": "um_squixl"
},
"connectivity": [
"bluetooth",
"wifi"
],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": [
"arduino",
"espidf"
],
"name": "Unexpected Maker SQUiXL",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"require_upload_port": true,
"speed": 460800
},
"url": "https://unexpectedmaker.com/shop/squixl",
"vendor": "Unexpected Maker"
}
-2
View File
@@ -37,8 +37,6 @@
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 921600
},
-2
View File
@@ -41,8 +41,6 @@
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 460800
},
+3 -3
View File
@@ -110,14 +110,14 @@ env.Append(
" ".join(
[
"riscv32-esp-elf-objcopy"
if mcu in ("esp32c2","esp32c3","esp32c6","esp32h2","esp32p4")
if mcu in ("esp32c2","esp32c3","esp32c5","esp32c6","esp32h2","esp32p4")
else "xtensa-%s-elf-objcopy" % mcu,
"--input-target",
"binary",
"--output-target",
"elf32-littleriscv" if mcu in ("esp32c2","esp32c3","esp32c6","esp32h2","esp32p4") else "elf32-xtensa-le",
"elf32-littleriscv" if mcu in ("esp32c2","esp32c3","esp32c5","esp32c6","esp32h2","esp32p4") else "elf32-xtensa-le",
"--binary-architecture",
"riscv" if mcu in ("esp32c2","esp32c3","esp32c6","esp32h2","esp32p4") else "xtensa",
"riscv" if mcu in ("esp32c2","esp32c3","esp32c5","esp32c6","esp32h2","esp32p4") else "xtensa",
"--rename-section",
".data=.rodata.embedded",
"$SOURCE",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+269 -183
View File
@@ -40,22 +40,22 @@ from SCons.Script import (
DefaultEnvironment,
)
from platformio import fs, __version__
from platformio import fs
from platformio.compat import IS_WINDOWS
from platformio.proc import exec_command
from platformio.builder.tools.piolib import ProjectAsLibBuilder
from platformio.project.config import ProjectConfig
from platformio.package.version import get_original_version, pepver_to_semver
# Added to avoid conflicts between installed Python packages from
# the IDF virtual environment and PlatformIO Core
# Note: This workaround can be safely deleted when PlatformIO 6.1.7 is released
if os.environ.get("PYTHONPATH"):
del os.environ["PYTHONPATH"]
env = DefaultEnvironment()
env.SConscript("_embed_files.py", exports="env")
# remove maybe existing old map file in project root
map_file = os.path.join(env.subst("$PROJECT_DIR"), env.subst("$PROGNAME") + ".map")
if os.path.exists(map_file):
os.remove(map_file)
def install_standard_python_deps():
def _get_installed_standard_pip_packages():
result = {}
@@ -83,7 +83,10 @@ def install_standard_python_deps():
deps = {
"wheel": ">=0.35.1",
"rich-click": ">=1.8.6",
"PyYAML": ">=6.0.2"
"PyYAML": ">=6.0.2",
"intelhex": ">=2.3.0",
"rich": ">=14.0.0",
"esp-idf-size": ">=1.6.1"
}
installed_packages = _get_installed_standard_pip_packages()
@@ -100,7 +103,7 @@ def install_standard_python_deps():
env.Execute(
env.VerboseAction(
(
'"$PYTHONEXE" -m pip install -U '
'"$PYTHONEXE" -m pip install -U -q -q -q '
+ " ".join(
[
'"%s%s"' % (p, deps[p])
@@ -147,18 +150,20 @@ TOOLCHAIN_DIR = platform.get_package_dir(
assert os.path.isdir(FRAMEWORK_DIR)
assert os.path.isdir(TOOLCHAIN_DIR)
if (
["espidf"] == env.get("PIOFRAMEWORK")
and semantic_version.Version.coerce(__version__)
<= semantic_version.Version("6.1.10")
and "__debug" in COMMAND_LINE_TARGETS
):
print("Warning! Debugging an IDF project requires PlatformIO Core >= 6.1.11!")
def create_silent_action(action_func):
"""Create a silent SCons action that suppresses output"""
silent_action = env.Action(action_func)
silent_action.strfunction = lambda target, source, env: ''
return silent_action
# Arduino framework as a component is not compatible with ESP-IDF >5.3
if "arduino" in env.subst("$PIOFRAMEWORK"):
ARDUINO_FRAMEWORK_DIR = platform.get_package_dir("framework-arduinoespressif32")
ARDUINO_FRMWRK_LIB_DIR = platform.get_package_dir("framework-arduinoespressif32-libs")
if mcu == "esp32c2":
ARDUINO_FRMWRK_C2_LIB_DIR = join(ARDUINO_FRMWRK_LIB_DIR, mcu)
if not os.path.exists(ARDUINO_FRMWRK_C2_LIB_DIR):
ARDUINO_C2_DIR = join(platform.get_package_dir("framework-arduino-c2-skeleton-lib"),mcu)
shutil.copytree(ARDUINO_C2_DIR, ARDUINO_FRMWRK_C2_LIB_DIR, dirs_exist_ok=True)
# Possible package names in 'package@version' format is not compatible with CMake
if "@" in os.path.basename(ARDUINO_FRAMEWORK_DIR):
new_path = os.path.join(
@@ -168,6 +173,7 @@ if "arduino" in env.subst("$PIOFRAMEWORK"):
os.rename(ARDUINO_FRAMEWORK_DIR, new_path)
ARDUINO_FRAMEWORK_DIR = new_path
assert ARDUINO_FRAMEWORK_DIR and os.path.isdir(ARDUINO_FRAMEWORK_DIR)
arduino_libs_mcu = join(platform.get_package_dir("framework-arduinoespressif32-libs"),mcu)
BUILD_DIR = env.subst("$BUILD_DIR")
PROJECT_DIR = env.subst("$PROJECT_DIR")
@@ -178,6 +184,18 @@ SDKCONFIG_PATH = os.path.expandvars(board.get(
os.path.join(PROJECT_DIR, "sdkconfig.%s" % env.subst("$PIOENV")),
))
def contains_path_traversal(url):
"""Check for Path Traversal patterns"""
dangerous_patterns = [
'../', '..\\', # Standard Path Traversal
'%2e%2e%2f', '%2e%2e%5c', # URL-encoded
'..%2f', '..%5c', # Mixed
'%252e%252e%252f', # Double encoded
]
url_lower = url.lower()
return any(pattern in url_lower for pattern in dangerous_patterns)
#
# generate modified Arduino IDF sdkconfig, applying settings from "custom_sdkconfig"
#
@@ -192,180 +210,208 @@ if "espidf.custom_sdkconfig" in board:
flag_custom_sdkonfig = True
def HandleArduinoIDFsettings(env):
def get_MD5_hash(phrase):
import hashlib
return hashlib.md5((phrase).encode('utf-8')).hexdigest()[:16]
"""
Handles Arduino IDF settings configuration with custom sdkconfig support.
"""
def custom_sdkconfig_file(string):
if not config.has_option("env:"+env["PIOENV"], "custom_sdkconfig"):
def get_MD5_hash(phrase):
"""Generate MD5 hash for checksum validation."""
import hashlib
return hashlib.md5(phrase.encode('utf-8')).hexdigest()[:16]
def load_custom_sdkconfig_file():
"""Load custom sdkconfig from file or URL if specified."""
if not config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
return ""
sdkconfig_entrys = env.GetProjectOption("custom_sdkconfig").splitlines()
for file in sdkconfig_entrys:
if "http" in file and "://" in file:
response = requests.get(file.split(" ")[0])
if response.ok:
target = str(response.content.decode('utf-8'))
sdkconfig_entries = env.GetProjectOption("custom_sdkconfig").splitlines()
for file_entry in sdkconfig_entries:
# Handle HTTP/HTTPS URLs
if "http" in file_entry and "://" in file_entry:
url = file_entry.split(" ")[0]
# Path Traversal protection
if contains_path_traversal(url):
print(f"Path Traversal detected: {url} check your URL path")
else:
print("Failed to download:", file)
return ""
return target
if "file://" in file:
file_path = join(PROJECT_DIR,file.lstrip("file://").split(os.path.sep)[-1])
try:
response = requests.get(file_entry.split(" ")[0], timeout=10)
if response.ok:
return response.content.decode('utf-8')
except requests.RequestException as e:
print(f"Error downloading {file_entry}: {e}")
except UnicodeDecodeError as e:
print(f"Error decoding response from {file_entry}: {e}")
return ""
# Handle local files
if "file://" in file_entry:
file_ref = file_entry[7:] if file_entry.startswith("file://") else file_entry
filename = os.path.basename(file_ref)
file_path = join(PROJECT_DIR, filename)
if os.path.exists(file_path):
with open(file_path, 'r') as file:
target = file.read()
try:
with open(file_path, 'r') as f:
return f.read()
except IOError as e:
print(f"Error reading file {file_path}: {e}")
return ""
else:
print("File not found:", file_path)
print("File not found, check path:", file_path)
return ""
return target
return ""
def extract_flag_name(line):
"""Extract flag name from sdkconfig line."""
line = line.strip()
if line.startswith("#") and "is not set" in line:
return line.split(" ")[1]
elif not line.startswith("#") and "=" in line:
return line.split("=")[0]
return None
custom_sdk_config_flags = ""
board_idf_config_flags = ""
sdkconfig_file_flags = ""
custom_sdkconfig_file_str = ""
def build_idf_config_flags():
"""Build complete IDF configuration flags from all sources."""
flags = []
if config.has_option("env:"+env["PIOENV"], "custom_sdkconfig"):
flag_custom_sdkonfig = True
custom_sdk_config_flags = (env.GetProjectOption("custom_sdkconfig").rstrip("\n")) + "\n"
custom_sdkconfig_file_str = custom_sdkconfig_file(sdkconfig_file_flags)
# Add board-specific flags first
if "espidf.custom_sdkconfig" in board:
board_flags = board.get("espidf.custom_sdkconfig", [])
if board_flags:
flags.extend(board_flags)
if "espidf.custom_sdkconfig" in board:
board_idf_config_flags = ('\n'.join([element for element in board.get("espidf.custom_sdkconfig", "")])).rstrip("\n") + "\n"
flag_custom_sdkonfig = True
# Add custom sdkconfig file content
custom_file_content = load_custom_sdkconfig_file()
if custom_file_content:
flags.append(custom_file_content)
if flag_custom_sdkonfig == True: # TDOO duplicated
print("*** Add \"custom_sdkconfig\" settings to IDF sdkconfig.defaults ***")
idf_config_flags = custom_sdk_config_flags
if custom_sdkconfig_file_str != "":
sdkconfig_file_flags = custom_sdkconfig_file_str + "\n"
idf_config_flags = sdkconfig_file_flags + idf_config_flags
idf_config_flags = board_idf_config_flags + idf_config_flags
# Add project-level custom sdkconfig
if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
custom_flags = env.GetProjectOption("custom_sdkconfig").rstrip("\n")
if custom_flags:
flags.append(custom_flags)
return "\n".join(flags) + "\n" if flags else ""
def add_flash_configuration(config_flags):
"""Add flash frequency and mode configuration."""
if flash_frequency != "80m":
idf_config_flags = idf_config_flags + "# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set\n"
esptool_flashfreq_y = "CONFIG_ESPTOOLPY_FLASHFREQ_%s=y\n" % flash_frequency.upper()
esptool_flashfreq_M = "CONFIG_ESPTOOLPY_FLASHFREQ=\"%s\"\n" % flash_frequency
idf_config_flags = idf_config_flags + esptool_flashfreq_y + esptool_flashfreq_M
config_flags += "# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set\n"
config_flags += f"CONFIG_ESPTOOLPY_FLASHFREQ_{flash_frequency.upper()}=y\n"
config_flags += f"CONFIG_ESPTOOLPY_FLASHFREQ=\"{flash_frequency}\"\n"
if flash_mode != "qio":
idf_config_flags = idf_config_flags + "# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set\n"
esptool_flashmode = "CONFIG_ESPTOOLPY_FLASHMODE_%s=y\n" % flash_mode.upper()
if esptool_flashmode not in idf_config_flags:
idf_config_flags = idf_config_flags + esptool_flashmode
if mcu in ("esp32") and "CONFIG_FREERTOS_UNICORE=y" in idf_config_flags:
idf_config_flags = idf_config_flags + "# CONFIG_SPIRAM is not set\n"
config_flags += "# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set\n"
idf_config_flags = idf_config_flags.splitlines()
sdkconfig_src = join(ARDUINO_FRMWRK_LIB_DIR,mcu,"sdkconfig")
flash_mode_flag = f"CONFIG_ESPTOOLPY_FLASHMODE_{flash_mode.upper()}=y\n"
if flash_mode_flag not in config_flags:
config_flags += flash_mode_flag
def get_flag(line):
if line.startswith("#") and "is not set" in line:
return line.split(" ")[1]
elif not line.startswith("#") and len(line.split("=")) > 1:
return line.split("=")[0]
else:
return None
# ESP32 specific SPIRAM configuration
if mcu == "esp32" and "CONFIG_FREERTOS_UNICORE=y" in config_flags:
config_flags += "# CONFIG_SPIRAM is not set\n"
with open(sdkconfig_src) as src:
sdkconfig_dst = os.path.join(PROJECT_DIR, "sdkconfig.defaults")
dst = open(sdkconfig_dst,"w")
dst.write("# TASMOTA__"+ get_MD5_hash(''.join(custom_sdk_config_flags).strip() + mcu) +"\n")
while line := src.readline():
flag = get_flag(line)
if flag is None:
return config_flags
def write_sdkconfig_file(idf_config_flags, checksum_source):
if "arduino" not in env.subst("$PIOFRAMEWORK"):
print("Error: Arduino framework required for sdkconfig processing")
return
"""Write the final sdkconfig.defaults file with checksum."""
sdkconfig_src = join(arduino_libs_mcu, "sdkconfig")
sdkconfig_dst = join(PROJECT_DIR, "sdkconfig.defaults")
# Generate checksum for validation (maintains original logic)
checksum = get_MD5_hash(checksum_source.strip() + mcu)
with open(sdkconfig_src, 'r', encoding='utf-8') as src, open(sdkconfig_dst, 'w', encoding='utf-8') as dst:
# Write checksum header (critical for compilation decision logic)
dst.write(f"# TASMOTA__{checksum}\n")
processed_flags = set()
# Process each line from source sdkconfig
for line in src:
flag_name = extract_flag_name(line)
if flag_name is None:
dst.write(line)
else:
no_match = True
for item in idf_config_flags:
if flag == get_flag(item.replace("\'", "")):
dst.write(item.replace("\'", "")+"\n")
no_match = False
print("Replace:",line,"with:",item.replace("\'", ""))
idf_config_flags.remove(item)
if no_match:
dst.write(line)
for item in idf_config_flags: # are there new flags?
print("Add:",item.replace("\'", ""))
dst.write(item.replace("\'", "")+"\n")
dst.close()
return
else:
continue
# Check if we have a custom replacement for this flag
flag_replaced = False
for custom_flag in idf_config_flags[:]: # Create copy for safe removal
custom_flag_name = extract_flag_name(custom_flag.replace("'", ""))
if flag_name == custom_flag_name:
cleaned_flag = custom_flag.replace("'", "")
dst.write(cleaned_flag + "\n")
print(f"Replace: {line.strip()} with: {cleaned_flag}")
idf_config_flags.remove(custom_flag)
processed_flags.add(custom_flag_name)
flag_replaced = True
break
if not flag_replaced:
dst.write(line)
# Add any remaining new flags
for remaining_flag in idf_config_flags:
cleaned_flag = remaining_flag.replace("'", "")
print(f"Add: {cleaned_flag}")
dst.write(cleaned_flag + "\n")
# Main execution logic
has_custom_config = (
config.has_option("env:" + env["PIOENV"], "custom_sdkconfig") or
"espidf.custom_sdkconfig" in board
)
if not has_custom_config:
return
print("*** Add \"custom_sdkconfig\" settings to IDF sdkconfig.defaults ***")
# Build complete configuration
idf_config_flags = build_idf_config_flags()
idf_config_flags = add_flash_configuration(idf_config_flags)
# Convert to list for processing
idf_config_list = [line for line in idf_config_flags.splitlines() if line.strip()]
# Write final configuration file with checksum
custom_sdk_config_flags = ""
if config.has_option("env:" + env["PIOENV"], "custom_sdkconfig"):
custom_sdk_config_flags = env.GetProjectOption("custom_sdkconfig").rstrip("\n") + "\n"
write_sdkconfig_file(idf_config_list, custom_sdk_config_flags)
def HandleCOMPONENTsettings(env):
if flag_custom_component_add == True or flag_custom_component_remove == True: # todo remove duplicated
import yaml
from yaml import SafeLoader
print("*** \"custom_component\" is used to select managed idf components ***")
if flag_custom_component_remove == True:
idf_custom_component_remove = env.GetProjectOption("custom_component_remove").splitlines()
else:
idf_custom_component_remove = ""
if flag_custom_component_add == True:
idf_custom_component_add = env.GetProjectOption("custom_component_add").splitlines()
else:
idf_custom_component_add = ""
from component_manager import ComponentManager
component_manager = ComponentManager(env)
# search "idf_component.yml" file
try: # 1.st in Arduino framework
idf_component_yml_src = os.path.join(ARDUINO_FRAMEWORK_DIR, "idf_component.yml")
shutil.copy(join(ARDUINO_FRAMEWORK_DIR,"idf_component.yml"),join(ARDUINO_FRAMEWORK_DIR,"idf_component.yml.orig"))
yml_file_dir = idf_component_yml_src
except: # 2.nd Project source
try:
idf_component_yml_src = os.path.join(PROJECT_SRC_DIR, "idf_component.yml")
shutil.copy(join(PROJECT_SRC_DIR,"idf_component.yml"),join(PROJECT_SRC_DIR,"idf_component.yml.orig"))
yml_file_dir = idf_component_yml_src
except: # no idf_component.yml in Project source -> create
idf_component_yml_src = os.path.join(PROJECT_SRC_DIR, "idf_component.yml")
yml_file_dir = idf_component_yml_src
idf_component_yml_str = """
dependencies:
idf: \">=5.1\"
"""
idf_component_yml = yaml.safe_load(idf_component_yml_str)
with open(idf_component_yml_src, 'w',) as f :
yaml.dump(idf_component_yml,f)
if flag_custom_component_add or flag_custom_component_remove:
actions = [action for flag, action in [
(flag_custom_component_add, "select"),
(flag_custom_component_remove, "deselect")
] if flag]
action_text = " and ".join(actions)
print(f"*** \"custom_component\" is used to {action_text} managed idf components ***")
yaml_file=open(idf_component_yml_src,"r")
idf_component=yaml.load(yaml_file, Loader=SafeLoader)
idf_component_str=json.dumps(idf_component) # convert to json string
idf_component_json=json.loads(idf_component_str) # convert string to json dict
if idf_custom_component_remove != "":
for entry in idf_custom_component_remove:
# checking if the entry exists before removing
if entry in idf_component_json["dependencies"]:
print("*** Removing component:",entry)
del idf_component_json["dependencies"][entry]
if idf_custom_component_add != "":
for entry in idf_custom_component_add:
if len(str(entry)) > 4: # too short or empty entry
# add new entrys to json
if "@" in entry:
idf_comp_entry = str(entry.split("@")[0]).replace(" ", "")
idf_comp_vers = str(entry.split("@")[1]).replace(" ", "")
else:
idf_comp_entry = str(entry).replace(" ", "")
idf_comp_vers = "*"
if idf_comp_entry not in idf_component_json["dependencies"]:
print("*** Adding component:", idf_comp_entry, idf_comp_vers)
new_entry = {idf_comp_entry: {"version": idf_comp_vers}}
idf_component_json["dependencies"].update(new_entry)
idf_component_yml_file = open(yml_file_dir,"w")
yaml.dump(idf_component_json, idf_component_yml_file)
idf_component_yml_file.close()
# print("JSON from modified idf_component.yml:")
# print(json.dumps(idf_component_json))
component_manager.handle_component_settings(
add_components=flag_custom_component_add,
remove_components=flag_custom_component_remove
)
return
return
if flag_custom_component_add == True or flag_custom_component_remove == True:
if "arduino" in env.subst("$PIOFRAMEWORK"):
HandleCOMPONENTsettings(env)
if flag_custom_sdkonfig == True and "arduino" in env.subst("$PIOFRAMEWORK"):
if flag_custom_sdkonfig == True and "arduino" in env.subst("$PIOFRAMEWORK") and "espidf" not in env.subst("$PIOFRAMEWORK"):
HandleArduinoIDFsettings(env)
LIB_SOURCE = os.path.join(ProjectConfig.get_instance().get("platformio", "platforms_dir"), "espressif32", "builder", "build_lib")
if not bool(os.path.exists(os.path.join(PROJECT_DIR, ".dummy"))):
@@ -397,7 +443,6 @@ def get_project_lib_includes(env):
return paths
def is_cmake_reconfigure_required(cmake_api_reply_dir):
cmake_cache_file = os.path.join(BUILD_DIR, "CMakeCache.txt")
cmake_txt_files = [
@@ -536,6 +581,8 @@ def populate_idf_env_vars(idf_env):
if "IDF_TOOLS_PATH" in idf_env:
del idf_env["IDF_TOOLS_PATH"]
idf_env["ESP_ROM_ELF_DIR"] = platform.get_package_dir("tool-esp-rom-elfs")
def get_target_config(project_configs, target_index, cmake_api_reply_dir):
target_json = project_configs.get("targets")[target_index].get("jsonFile", "")
@@ -595,6 +642,8 @@ def extract_defines(compile_group):
define_string = define_string.strip()
if "=" in define_string:
define, value = define_string.split("=", maxsplit=1)
if define == "OPENTHREAD_BUILD_DATETIME":
return None
if any(char in value for char in (' ', '<', '>')):
value = f'"{value}"'
elif '"' in value and not value.startswith("\\"):
@@ -1531,11 +1580,10 @@ def install_python_deps():
# https://github.com/platformio/platformio-core/issues/4614
"urllib3": "<2",
# https://github.com/platformio/platform-espressif32/issues/635
"cryptography": "~=41.0.1",
"future": ">=0.18.3",
"cryptography": "~=44.0.0",
"pyparsing": ">=3.1.0,<4",
"idf-component-manager": "~=2.0.1",
"esp-idf-kconfig": ">=2.5.0"
"esp-idf-kconfig": "~=2.5.0"
}
if sys_platform.system() == "Darwin" and "arm" in sys_platform.machine().lower():
@@ -1556,7 +1604,7 @@ def install_python_deps():
env.Execute(
env.VerboseAction(
(
'"%s" -m pip install -U ' % python_exe_path
'"%s" -m pip install -U -q -q -q ' % python_exe_path
+ " ".join(['"%s%s"' % (p, deps[p]) for p in packages_to_install])
),
"Installing ESP-IDF's Python dependencies",
@@ -1566,7 +1614,7 @@ def install_python_deps():
if IS_WINDOWS and "windows-curses" not in installed_packages:
env.Execute(
env.VerboseAction(
'"%s" -m pip install windows-curses' % python_exe_path,
'"%s" -m pip install -q -q -q windows-curses' % python_exe_path,
"Installing windows-curses package",
)
)
@@ -1786,18 +1834,36 @@ if "arduino" in env.subst("$PIOFRAMEWORK"):
LIBSOURCE_DIRS=[os.path.join(ARDUINO_FRAMEWORK_DIR, "libraries")]
)
# Set ESP-IDF version environment variables (needed for proper Kconfig processing)
framework_version = get_framework_version()
major_version = framework_version.split('.')[0] + '.' + framework_version.split('.')[1]
os.environ["ESP_IDF_VERSION"] = major_version
# Configure CMake arguments with ESP-IDF version
extra_cmake_args = [
"-DIDF_TARGET=" + idf_variant,
"-DPYTHON_DEPS_CHECKED=1",
"-DEXTRA_COMPONENT_DIRS:PATH=" + ";".join(extra_components),
"-DPYTHON=" + get_python_exe(),
"-DSDKCONFIG=" + SDKCONFIG_PATH,
f"-DESP_IDF_VERSION={major_version}",
f"-DESP_IDF_VERSION_MAJOR={framework_version.split('.')[0]}",
f"-DESP_IDF_VERSION_MINOR={framework_version.split('.')[1]}",
]
# This will add the linker flag for the map file
extra_cmake_args.append(
f'-DCMAKE_EXE_LINKER_FLAGS=-Wl,-Map={os.path.join(BUILD_DIR, env.subst("$PROGNAME") + ".map")}'
)
# Add any extra args from board config
extra_cmake_args += click.parser.split_arg_string(board.get("build.cmake_extra_args", ""))
print("Reading CMake configuration...")
project_codemodel = get_cmake_code_model(
PROJECT_DIR,
BUILD_DIR,
[
"-DIDF_TARGET=" + idf_variant,
"-DPYTHON_DEPS_CHECKED=1",
"-DEXTRA_COMPONENT_DIRS:PATH=" + ";".join(extra_components),
"-DPYTHON=" + get_python_exe(),
"-DSDKCONFIG=" + SDKCONFIG_PATH,
]
+ click.parser.split_arg_string(board.get("build.cmake_extra_args", "")),
extra_cmake_args
)
# At this point the sdkconfig file should be generated by the underlying build system
@@ -1973,7 +2039,7 @@ env.Prepend(
(
board.get(
"upload.bootloader_offset",
"0x1000" if mcu in ["esp32", "esp32s2"] else ("0x2000" if mcu in ["esp32p4"] else "0x0"),
"0x1000" if mcu in ["esp32", "esp32s2"] else ("0x2000" if mcu in ["esp32c5", "esp32p4"] else "0x0"),
),
os.path.join("$BUILD_DIR", "bootloader.bin"),
),
@@ -2063,6 +2129,15 @@ extra_elf2bin_flags = "--elf-sha256-offset 0xb0"
# For chips that support configurable MMU page size feature
# If page size is configured to values other than the default "64KB" in menuconfig,
mmu_page_size = "64KB"
if sdk_config.get("MMU_PAGE_SIZE_8KB", False):
mmu_page_size = "8KB"
elif sdk_config.get("MMU_PAGE_SIZE_16KB", False):
mmu_page_size = "16KB"
elif sdk_config.get("MMU_PAGE_SIZE_32KB", False):
mmu_page_size = "32KB"
else:
mmu_page_size = "64KB"
if sdk_config.get("SOC_MMU_PAGE_SIZE_CONFIGURABLE", False):
if board_flash_size == "2MB":
mmu_page_size = "32KB"
@@ -2091,7 +2166,7 @@ if os.path.isdir(ulp_dir) and os.listdir(ulp_dir) and mcu not in ("esp32c2", "es
# Compile Arduino IDF sources
#
if "arduino" in env.get("PIOFRAMEWORK") and "espidf" not in env.get("PIOFRAMEWORK"):
if ("arduino" in env.subst("$PIOFRAMEWORK")) and ("espidf" not in env.subst("$PIOFRAMEWORK")):
def idf_lib_copy(source, target, env):
env_build = join(env["PROJECT_BUILD_DIR"],env["PIOENV"])
sdkconfig_h_path = join(env_build,"config","sdkconfig.h")
@@ -2147,9 +2222,14 @@ if "arduino" in env.get("PIOFRAMEWORK") and "espidf" not in env.get("PIOFRAMEWOR
print("*** Original Arduino \"idf_component.yml\" restored ***")
except:
print("*** Original Arduino \"idf_component.yml\" couldnt be restored ***")
env.AddPostAction("checkprogsize", idf_lib_copy)
# Restore original pioarduino-build.py
from component_manager import ComponentManager
component_manager = ComponentManager(env)
component_manager.restore_pioarduino_build_py()
silent_action = create_silent_action(idf_lib_copy)
env.AddPostAction("checkprogsize", silent_action)
if "espidf" in env.get("PIOFRAMEWORK") and (flag_custom_component_add == True or flag_custom_component_remove == True):
if "espidf" in env.subst("$PIOFRAMEWORK") and (flag_custom_component_add == True or flag_custom_component_remove == True):
def idf_custom_component(source, target, env):
try:
shutil.copy(join(ARDUINO_FRAMEWORK_DIR,"idf_component.yml.orig"),join(ARDUINO_FRAMEWORK_DIR,"idf_component.yml"))
@@ -2163,8 +2243,14 @@ if "espidf" in env.get("PIOFRAMEWORK") and (flag_custom_component_add == True or
os.remove(join(PROJECT_SRC_DIR,"idf_component.yml"))
print("*** pioarduino generated \"idf_component.yml\" removed ***")
except:
print("*** \"idf_component.yml\" couldnt be removed ***")
env.AddPostAction("checkprogsize", idf_custom_component)
print("*** no custom \"idf_component.yml\" found for removing ***")
if "arduino" in env.subst("$PIOFRAMEWORK"):
# Restore original pioarduino-build.py, only used with Arduino
from component_manager import ComponentManager
component_manager = ComponentManager(env)
component_manager.restore_pioarduino_build_py()
silent_action = create_silent_action(idf_custom_component)
env.AddPostAction("checkprogsize", silent_action)
#
# Process OTA partition and image
#
@@ -2219,7 +2305,7 @@ def _parse_size(value):
partitions_csv = env.subst("$PARTITIONS_TABLE_CSV")
result = []
next_offset = 0
bound = int(board.get("upload.offset_address", "0x10000"), 16) # default 0x10000
bound = 0x10000
with open(partitions_csv) as fp:
for line in fp.readlines():
line = line.strip()
+1 -1
View File
@@ -37,7 +37,7 @@ def prepare_ulp_env_vars(env):
toolchain_path = platform.get_package_dir(
"toolchain-xtensa-esp-elf"
if idf_variant not in ("esp32c6", "esp32p4")
if idf_variant not in ("esp32c5","esp32c6", "esp32p4")
else "toolchain-riscv32-esp"
)
+333 -127
View File
@@ -12,31 +12,47 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import locale
import os
import re
import shlex
import subprocess
import sys
from os.path import isfile, join
from SCons.Script import (
ARGUMENTS, COMMAND_LINE_TARGETS, AlwaysBuild, Builder, Default,
DefaultEnvironment)
ARGUMENTS,
COMMAND_LINE_TARGETS,
AlwaysBuild,
Builder,
Default,
DefaultEnvironment,
)
from platformio.project.helpers import get_project_dir
from platformio.util import get_serial_ports
# Initialize environment and configuration
env = DefaultEnvironment()
platform = env.PioPlatform()
projectconfig = env.GetProjectConfig()
terminal_cp = locale.getpreferredencoding().lower()
#
# Helpers
#
# Framework directory path
FRAMEWORK_DIR = platform.get_package_dir("framework-arduinoespressif32")
def BeforeUpload(target, source, env):
"""
Prepare the environment before uploading firmware.
Handles port detection and special upload configurations.
"""
upload_options = {}
if "BOARD" in env:
upload_options = env.BoardConfig().get("upload", {})
env.AutodetectUploadPort()
if not env.subst("$UPLOAD_PORT"):
env.AutodetectUploadPort()
before_ports = get_serial_ports()
if upload_options.get("use_1200bps_touch", False):
@@ -47,6 +63,10 @@ def BeforeUpload(target, source, env):
def _get_board_memory_type(env):
"""
Determine the memory type configuration for the board.
Returns the appropriate memory type string based on board configuration.
"""
board_config = env.BoardConfig()
default_type = "%s_%s" % (
board_config.get("build.flash_mode", "dio"),
@@ -62,24 +82,33 @@ def _get_board_memory_type(env):
),
)
def _normalize_frequency(frequency):
"""
Convert frequency value to normalized string format (e.g., "40m").
Removes 'L' suffix and converts to MHz format.
"""
frequency = str(frequency).replace("L", "")
return str(int(int(frequency) / 1000000)) + "m"
def _get_board_f_flash(env):
"""Get the flash frequency for the board."""
frequency = env.subst("$BOARD_F_FLASH")
return _normalize_frequency(frequency)
def _get_board_f_image(env):
"""Get the image frequency for the board, fallback to flash frequency."""
board_config = env.BoardConfig()
if "build.f_image" in board_config:
return _normalize_frequency(board_config.get("build.f_image"))
return _get_board_f_flash(env)
def _get_board_f_boot(env):
"""Get the boot frequency for the board, fallback to flash frequency."""
board_config = env.BoardConfig()
if "build.f_boot" in board_config:
return _normalize_frequency(board_config.get("build.f_boot"))
@@ -88,10 +117,11 @@ def _get_board_f_boot(env):
def _get_board_flash_mode(env):
if _get_board_memory_type(env) in (
"opi_opi",
"opi_qspi",
):
"""
Determine the appropriate flash mode for the board.
Handles special cases for OPI memory types.
"""
if _get_board_memory_type(env) in ("opi_opi", "opi_qspi"):
return "dout"
mode = env.subst("$BOARD_FLASH_MODE")
@@ -101,6 +131,10 @@ def _get_board_flash_mode(env):
def _get_board_boot_mode(env):
"""
Determine the boot mode for the board.
Handles special cases for OPI memory types.
"""
memory_type = env.BoardConfig().get("build.arduino.memory_type", "")
build_boot = env.BoardConfig().get("build.boot", "$BOARD_FLASH_MODE")
if memory_type in ("opi_opi", "opi_qspi"):
@@ -109,6 +143,10 @@ def _get_board_boot_mode(env):
def _parse_size(value):
"""
Parse size values from various formats (int, hex, K/M suffixes).
Returns the size in bytes as an integer.
"""
if isinstance(value, int):
return value
elif value.isdigit():
@@ -122,16 +160,23 @@ def _parse_size(value):
def _parse_partitions(env):
"""
Parse the partition table CSV file and return partition information.
Also sets the application offset for the environment.
"""
partitions_csv = env.subst("$PARTITIONS_TABLE_CSV")
if not isfile(partitions_csv):
sys.stderr.write("Could not find the file %s with partitions "
"table.\n" % partitions_csv)
sys.stderr.write(
"Could not find the file %s with partitions table.\n"
% partitions_csv
)
env.Exit(1)
return
result = []
next_offset = 0
app_offset = int(board.get("upload.offset_address", "0x10000"), 16) # default 0x10000
app_offset = 0x10000 # Default address for firmware
with open(partitions_csv) as fp:
for line in fp.readlines():
line = line.strip()
@@ -148,25 +193,34 @@ def _parse_partitions(env):
"subtype": tokens[2],
"offset": tokens[3] or calculated_offset,
"size": tokens[4],
"flags": tokens[5] if len(tokens) > 5 else None
"flags": tokens[5] if len(tokens) > 5 else None,
}
result.append(partition)
next_offset = _parse_size(partition["offset"])
if (partition["subtype"] == "ota_0"):
if partition["subtype"] == "ota_0":
app_offset = next_offset
next_offset = next_offset + _parse_size(partition["size"])
# Configure application partition offset
env.Replace(ESP32_APP_OFFSET=str(hex(app_offset)))
# Propagate application offset to debug configurations
env["INTEGRATION_EXTRA_DATA"].update({"application_offset": str(hex(app_offset))})
env["INTEGRATION_EXTRA_DATA"].update(
{"application_offset": str(hex(app_offset))}
)
return result
def _update_max_upload_size(env):
"""
Update the maximum upload size based on partition table configuration.
Prioritizes user-specified partition names.
"""
if not env.get("PARTITIONS_TABLE_CSV"):
return
sizes = {
p["subtype"]: _parse_size(p["size"]) for p in _parse_partitions(env)
p["subtype"]: _parse_size(p["size"])
for p in _parse_partitions(env)
if p["type"] in ("0", "app")
}
@@ -177,12 +231,15 @@ def _update_max_upload_size(env):
if custom_app_partition_name:
selected_partition = partitions.get(custom_app_partition_name, {})
if selected_partition:
board.update("upload.maximum_size", _parse_size(selected_partition["size"]))
board.update(
"upload.maximum_size", _parse_size(selected_partition["size"])
)
return
else:
print(
"Warning! Selected partition `%s` is not available in the partition " \
"table! Default partition will be used!" % custom_app_partition_name
"Warning! Selected partition `%s` is not available in the "
"partition table! Default partition will be used!"
% custom_app_partition_name
)
for p in partitions.values():
@@ -191,20 +248,23 @@ def _update_max_upload_size(env):
break
def _to_unix_slashes(path):
"""Convert Windows-style backslashes to Unix-style forward slashes."""
return path.replace("\\", "/")
#
# Filesystem helpers
#
def fetch_fs_size(env):
"""
Extract filesystem size and offset information from partition table.
Sets FS_START, FS_SIZE, FS_PAGE, and FS_BLOCK environment variables.
"""
fs = None
for p in _parse_partitions(env):
if p["type"] == "data" and p["subtype"] in ("spiffs", "fat", "littlefs"):
if p["type"] == "data" and p["subtype"] in (
"spiffs",
"fat",
"littlefs",
):
fs = p
if not fs:
sys.stderr.write(
@@ -213,6 +273,7 @@ def fetch_fs_size(env):
)
env.Exit(1)
return
env["FS_START"] = _parse_size(fs["offset"])
env["FS_SIZE"] = _parse_size(fs["size"])
env["FS_PAGE"] = int("0x100", 16)
@@ -226,20 +287,37 @@ def fetch_fs_size(env):
def __fetch_fs_size(target, source, env):
"""Wrapper function for fetch_fs_size to be used as SCons emitter."""
fetch_fs_size(env)
return (target, source)
def check_lib_archive_exists():
"""
Check if lib_archive is set in platformio.ini configuration.
Returns True if found, False otherwise.
"""
for section in projectconfig.sections():
if "lib_archive" in projectconfig.options(section):
return True
return False
# Initialize board configuration and MCU settings
board = env.BoardConfig()
mcu = board.get("build.mcu", "esp32")
toolchain_arch = "xtensa-%s" % mcu
filesystem = board.get("build.filesystem", "spiffs")
if mcu in ("esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4"):
filesystem = board.get("build.filesystem", "littlefs")
# Set toolchain architecture for RISC-V based ESP32 variants
if mcu in ("esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32h2", "esp32p4"):
toolchain_arch = "riscv32-esp"
# Initialize integration extra data if not present
if "INTEGRATION_EXTRA_DATA" not in env:
env["INTEGRATION_EXTRA_DATA"] = {}
# Configure build tools and environment variables
env.Replace(
__get_board_boot_mode=_get_board_boot_mode,
__get_board_f_flash=_get_board_f_flash,
@@ -247,7 +325,6 @@ env.Replace(
__get_board_f_boot=_get_board_f_boot,
__get_board_flash_mode=_get_board_flash_mode,
__get_board_memory_type=_get_board_memory_type,
AR="%s-elf-gcc-ar" % toolchain_arch,
AS="%s-elf-as" % toolchain_arch,
CC="%s-elf-gcc" % toolchain_arch,
@@ -255,7 +332,14 @@ env.Replace(
GDB=join(
platform.get_package_dir(
"tool-riscv32-esp-elf-gdb"
if mcu in ("esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4")
if mcu in (
"esp32c2",
"esp32c3",
"esp32c5",
"esp32c6",
"esp32h2",
"esp32p4",
)
else "tool-xtensa-esp-elf-gdb"
)
or "",
@@ -265,20 +349,14 @@ env.Replace(
OBJCOPY=join(platform.get_package_dir("tool-esptoolpy") or "", "esptool.py"),
RANLIB="%s-elf-gcc-ranlib" % toolchain_arch,
SIZETOOL="%s-elf-size" % toolchain_arch,
ARFLAGS=["rc"],
SIZEPROGREGEXP=r"^(?:\.iram0\.text|\.iram0\.vectors|\.dram0\.data|\.flash\.text|\.flash\.rodata|)\s+([0-9]+).*",
SIZEPROGREGEXP=r"^(?:\.iram0\.text|\.iram0\.vectors|\.dram0\.data|"
r"\.flash\.text|\.flash\.rodata|)\s+([0-9]+).*",
SIZEDATAREGEXP=r"^(?:\.dram0\.data|\.dram0\.bss|\.noinit)\s+([0-9]+).*",
SIZECHECKCMD="$SIZETOOL -A -d $SOURCES",
SIZEPRINTCMD="$SIZETOOL -B -d $SOURCES",
ERASEFLAGS=[
"--chip", mcu,
"--port", '"$UPLOAD_PORT"'
],
ERASECMD='"$PYTHONEXE" "$OBJCOPY" $ERASEFLAGS erase_flash',
ERASEFLAGS=["--chip", mcu, "--port", '"$UPLOAD_PORT"'],
ERASECMD='"$PYTHONEXE" "$OBJCOPY" $ERASEFLAGS erase-flash',
# mkspiffs package contains two different binaries for IDF and Arduino
MKFSTOOL="mk%s" % filesystem
+ (
@@ -293,46 +371,61 @@ env.Replace(
if filesystem == "spiffs"
else ""
),
# Legacy `ESP32_SPIFFS_IMAGE_NAME` is used as the second fallback value for
# backward compatibility
# Legacy `ESP32_SPIFFS_IMAGE_NAME` is used as the second fallback value
# for backward compatibility
ESP32_FS_IMAGE_NAME=env.get(
"ESP32_FS_IMAGE_NAME", env.get("ESP32_SPIFFS_IMAGE_NAME", filesystem)
"ESP32_FS_IMAGE_NAME",
env.get("ESP32_SPIFFS_IMAGE_NAME", filesystem),
),
ESP32_APP_OFFSET=env.get("INTEGRATION_EXTRA_DATA").get(
"application_offset"
),
ESP32_APP_OFFSET=env.get("INTEGRATION_EXTRA_DATA").get("application_offset"),
ARDUINO_LIB_COMPILE_FLAG="Inactive",
PROGSUFFIX=".elf"
PROGSUFFIX=".elf",
)
# Check if lib_archive is set in platformio.ini and set it to False
# if not found. This makes weak defs in framework and libs possible.
if not check_lib_archive_exists():
env_section = "env:" + env["PIOENV"]
projectconfig.set(env_section, "lib_archive", "False")
# Allow user to override via pre:script
if env.get("PROGNAME", "program") == "program":
env.Replace(PROGNAME="firmware")
# Configure build actions and builders
env.Append(
BUILDERS=dict(
ElfToBin=Builder(
action=env.VerboseAction(" ".join([
'"$PYTHONEXE" "$OBJCOPY"',
"--chip", mcu, "elf2image",
"--flash_mode", "${__get_board_flash_mode(__env__)}",
"--flash_freq", "${__get_board_f_image(__env__)}",
"--flash_size", board.get("upload.flash_size", "4MB"),
"-o", "$TARGET", "$SOURCES"
]), "Building $TARGET"),
suffix=".bin"
action=env.VerboseAction(
" ".join(
[
'"$PYTHONEXE" "$OBJCOPY"',
"--chip",
mcu,
"elf2image",
"--flash-mode",
"${__get_board_flash_mode(__env__)}",
"--flash-freq",
"${__get_board_f_image(__env__)}",
"--flash-size",
board.get("upload.flash_size", "4MB"),
"-o",
"$TARGET",
"$SOURCES",
]
),
"Building $TARGET",
),
suffix=".bin",
),
DataToBin=Builder(
action=env.VerboseAction(
" ".join(
['"$MKFSTOOL"', "-c", "$SOURCES", "-s", "$FS_SIZE"]
+ (
[
"-p",
"$FS_PAGE",
"-b",
"$FS_BLOCK",
]
["-p", "$FS_PAGE", "-b", "$FS_BLOCK"]
if filesystem in ("littlefs", "spiffs")
else []
)
@@ -347,9 +440,76 @@ env.Append(
)
)
# Load framework-specific configuration
if not env.get("PIOFRAMEWORK"):
env.SConscript("frameworks/_bare.py", exports="env")
def firmware_metrics(target, source, env):
"""
Custom target to run esp-idf-size with support for command line parameters
Usage: pio run -t metrics -- [esp-idf-size arguments]
"""
if terminal_cp != "utf-8":
print("Firmware metrics can not be shown. Set the terminal codepage to \"utf-8\"")
return
map_file = os.path.join(env.subst("$BUILD_DIR"), env.subst("$PROGNAME") + ".map")
if not os.path.isfile(map_file):
# map file can be in project dir
map_file = os.path.join(get_project_dir(), env.subst("$PROGNAME") + ".map")
if not os.path.isfile(map_file):
print(f"Error: Map file not found: {map_file}")
print("Make sure the project is built first with 'pio run'")
return
try:
import subprocess
import sys
import shlex
cmd = [env.subst("$PYTHONEXE"), "-m", "esp_idf_size", "--ng"]
# Parameters from platformio.ini
extra_args = env.GetProjectOption("custom_esp_idf_size_args", "")
if extra_args:
cmd.extend(shlex.split(extra_args))
# Command Line Parameter, after --
cli_args = []
if "--" in sys.argv:
dash_index = sys.argv.index("--")
if dash_index + 1 < len(sys.argv):
cli_args = sys.argv[dash_index + 1:]
cmd.extend(cli_args)
# Add CLI arguments before the map file
if cli_args:
cmd.extend(cli_args)
# Map-file as last argument
cmd.append(map_file)
# Debug-Info if wanted
if env.GetProjectOption("custom_esp_idf_size_verbose", False):
print(f"Running command: {' '.join(cmd)}")
# Call esp-idf-size
result = subprocess.run(cmd, check=False, capture_output=False)
if result.returncode != 0:
print(f"Warning: esp-idf-size exited with code {result.returncode}")
except ImportError:
print("Error: esp-idf-size module not found.")
print("Install with: pip install esp-idf-size")
except FileNotFoundError:
print("Error: Python executable not found.")
print("Check your Python installation.")
except Exception as e:
print(f"Error: Failed to run firmware metrics: {e}")
print("Make sure esp-idf-size is installed: pip install esp-idf-size")
#
# Target: Build executable and linkable firmware or FS image
#
@@ -364,6 +524,10 @@ if "nobuild" in COMMAND_LINE_TARGETS:
target_firm = join("$BUILD_DIR", "${PROGNAME}.bin")
else:
target_elf = env.BuildProgram()
silent_action = env.Action(firmware_metrics)
# Hack to silence scons command output
silent_action.strfunction = lambda target, source, env: ""
env.AddPostAction(target_elf, silent_action)
if set(["buildfs", "uploadfs", "uploadfsota"]) & set(COMMAND_LINE_TARGETS):
target_firm = env.DataToBin(
join("$BUILD_DIR", "${ESP32_FS_IMAGE_NAME}"), "$PROJECT_DATA_DIR"
@@ -371,26 +535,27 @@ else:
env.NoCache(target_firm)
AlwaysBuild(target_firm)
else:
target_firm = env.ElfToBin(
join("$BUILD_DIR", "${PROGNAME}"), target_elf)
target_firm = env.ElfToBin(join("$BUILD_DIR", "${PROGNAME}"), target_elf)
env.Depends(target_firm, "checkprogsize")
env.AddPlatformTarget("buildfs", target_firm, target_firm, "Build Filesystem Image")
# Configure platform targets
env.AddPlatformTarget(
"buildfs", target_firm, target_firm, "Build Filesystem Image"
)
AlwaysBuild(env.Alias("nobuild", target_firm))
target_buildprog = env.Alias("buildprog", target_firm, target_firm)
# update max upload size based on CSV file
# Update max upload size based on CSV file
if env.get("PIOMAINPROG"):
env.AddPreAction(
"checkprogsize",
env.VerboseAction(
lambda source, target, env: _update_max_upload_size(env),
"Retrieving maximum program size $SOURCES"))
"Retrieving maximum program size $SOURCES",
),
)
#
# Target: Print binary size
#
target_size = env.AddPlatformTarget(
"size",
target_elf,
@@ -399,25 +564,25 @@ target_size = env.AddPlatformTarget(
"Calculate program size",
)
#
# Target: Upload firmware or FS image
#
upload_protocol = env.subst("$UPLOAD_PROTOCOL")
debug_tools = board.get("debug.tools", {})
upload_actions = []
# Compatibility with old OTA configurations
if (upload_protocol != "espota"
and re.match(r"\"?((([0-9]{1,3}\.){3}[0-9]{1,3})|[^\\/]+\.local)\"?$",
env.get("UPLOAD_PORT", ""))):
if upload_protocol != "espota" and re.match(
r"\"?((([0-9]{1,3}\.){3}[0-9]{1,3})|[^\\/]+\.local)\"?$",
env.get("UPLOAD_PORT", ""),
):
upload_protocol = "espota"
sys.stderr.write(
"Warning! We have just detected `upload_port` as IP address or host "
"name of ESP device. `upload_protocol` is switched to `espota`.\n"
"Please specify `upload_protocol = espota` in `platformio.ini` "
"project configuration file.\n")
"project configuration file.\n"
)
# Configure upload protocol: ESP OTA
if upload_protocol == "espota":
if not env.subst("$UPLOAD_PORT"):
sys.stderr.write(
@@ -425,32 +590,45 @@ if upload_protocol == "espota":
"using `upload_port` for build environment or use "
"global `--upload-port` option.\n"
"See https://docs.platformio.org/page/platforms/"
"espressif32.html#over-the-air-ota-update\n")
"espressif32.html#over-the-air-ota-update\n"
)
env.Replace(
UPLOADER=join(FRAMEWORK_DIR,"tools", "espota.py"),
UPLOADER=join(FRAMEWORK_DIR, "tools", "espota.py"),
UPLOADERFLAGS=["--debug", "--progress", "-i", "$UPLOAD_PORT"],
UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS -f $SOURCE'
UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS -f $SOURCE',
)
if set(["uploadfs", "uploadfsota"]) & set(COMMAND_LINE_TARGETS):
env.Append(UPLOADERFLAGS=["--spiffs"])
upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
# Configure upload protocol: esptool
elif upload_protocol == "esptool":
env.Replace(
UPLOADER=join(
platform.get_package_dir("tool-esptoolpy") or "", "esptool.py"),
platform.get_package_dir("tool-esptoolpy") or "", "esptool.py"
),
UPLOADERFLAGS=[
"--chip", mcu,
"--port", '"$UPLOAD_PORT"',
"--baud", "$UPLOAD_SPEED",
"--before", board.get("upload.before_reset", "default_reset"),
"--after", board.get("upload.after_reset", "hard_reset"),
"write_flash", "-z",
"--flash_mode", "${__get_board_flash_mode(__env__)}",
"--flash_freq", "${__get_board_f_image(__env__)}",
"--flash_size", "detect"
"--chip",
mcu,
"--port",
'"$UPLOAD_PORT"',
"--baud",
"$UPLOAD_SPEED",
"--before",
board.get("upload.before_reset", "default-reset"),
"--after",
board.get("upload.after_reset", "hard-reset"),
"write-flash",
"-z",
"--flash-mode",
"${__get_board_flash_mode(__env__)}",
"--flash-freq",
"${__get_board_f_image(__env__)}",
"--flash-size",
"detect",
],
UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS $ESP32_APP_OFFSET $SOURCE'
UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS '
"$ESP32_APP_OFFSET $SOURCE",
)
for image in env.get("FLASH_EXTRA_IMAGES", []):
env.Append(UPLOADERFLAGS=[image[0], env.subst(image[1])])
@@ -458,27 +636,36 @@ elif upload_protocol == "esptool":
if "uploadfs" in COMMAND_LINE_TARGETS:
env.Replace(
UPLOADERFLAGS=[
"--chip", mcu,
"--port", '"$UPLOAD_PORT"',
"--baud", "$UPLOAD_SPEED",
"--before", board.get("upload.before_reset", "default_reset"),
"--after", board.get("upload.after_reset", "hard_reset"),
"write_flash", "-z",
"--flash_mode", "${__get_board_flash_mode(__env__)}",
"--flash_freq", "${__get_board_f_image(__env__)}",
"--flash_size", "detect",
"$FS_START"
"--chip",
mcu,
"--port",
'"$UPLOAD_PORT"',
"--baud",
"$UPLOAD_SPEED",
"--before",
board.get("upload.before_reset", "default-reset"),
"--after",
board.get("upload.after_reset", "hard-reset"),
"write-flash",
"-z",
"--flash-mode",
"${__get_board_flash_mode(__env__)}",
"--flash-freq",
"${__get_board_f_image(__env__)}",
"--flash-size",
"detect",
"$FS_START",
],
UPLOADCMD='"$PYTHONEXE" "$UPLOADER" $UPLOADERFLAGS $SOURCE',
)
upload_actions = [
env.VerboseAction(BeforeUpload, "Looking for upload port..."),
env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")
env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE"),
]
# Configure upload protocol: DFU
elif upload_protocol == "dfu":
hwids = board.get("build.hwids", [["0x2341", "0x0070"]])
vid = hwids[0][0]
pid = hwids[0][1]
@@ -493,17 +680,18 @@ elif upload_protocol == "dfu":
"-d",
",".join(["%s:%s" % (hwid[0], hwid[1]) for hwid in hwids]),
"-Q",
"-D"
"-D",
],
UPLOADCMD='"$UPLOADER" $UPLOADERFLAGS "$SOURCE"',
)
# Configure upload protocol: Debug tools (OpenOCD)
elif upload_protocol in debug_tools:
_parse_partitions(env)
openocd_args = ["-d%d" % (2 if int(ARGUMENTS.get("PIOVERBOSE", 0)) else 1)]
openocd_args.extend(
debug_tools.get(upload_protocol).get("server").get("arguments", []))
debug_tools.get(upload_protocol).get("server").get("arguments", [])
)
openocd_args.extend(
[
"-c",
@@ -531,7 +719,9 @@ elif upload_protocol in debug_tools:
f.replace(
"$PACKAGE_DIR",
_to_unix_slashes(
platform.get_package_dir("tool-openocd-esp32") or ""))
platform.get_package_dir("tool-openocd-esp32") or ""
),
)
for f in openocd_args
]
env.Replace(
@@ -541,55 +731,71 @@ elif upload_protocol in debug_tools:
)
upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
# custom upload tool
# Configure upload protocol: Custom
elif upload_protocol == "custom":
upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
else:
sys.stderr.write("Warning! Unknown upload protocol %s\n" % upload_protocol)
# Register upload targets
env.AddPlatformTarget("upload", target_firm, upload_actions, "Upload")
env.AddPlatformTarget("uploadfs", target_firm, upload_actions, "Upload Filesystem Image")
env.AddPlatformTarget(
"uploadfsota", target_firm, upload_actions, "Upload Filesystem Image OTA")
"uploadfs", target_firm, upload_actions, "Upload Filesystem Image"
)
env.AddPlatformTarget(
"uploadfsota",
target_firm,
upload_actions,
"Upload Filesystem Image OTA",
)
#
# Target: Erase Flash and Upload
#
env.AddPlatformTarget(
"erase_upload",
target_firm,
[
env.VerboseAction(BeforeUpload, "Looking for upload port..."),
env.VerboseAction("$ERASECMD", "Erasing..."),
env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")
env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE"),
],
"Erase Flash and Upload",
)
#
# Target: Erase Flash
#
env.AddPlatformTarget(
"erase",
None,
[
env.VerboseAction(BeforeUpload, "Looking for upload port..."),
env.VerboseAction("$ERASECMD", "Erasing...")
env.VerboseAction("$ERASECMD", "Erasing..."),
],
"Erase Flash",
)
#
# Override memory inspection behavior
#
# Register Custom Target for firmware metrics
env.AddCustomTarget(
name="metrics",
dependencies="$BUILD_DIR/${PROGNAME}.elf",
actions=firmware_metrics,
title="Firmware Size Metrics",
description="Analyze firmware size using esp-idf-size "
"(supports CLI args after --)",
always_build=True,
)
# Additional Target without Build-Dependency when already compiled
env.AddCustomTarget(
name="metrics-only",
dependencies=None,
actions=firmware_metrics,
title="Firmware Size Metrics (No Build)",
description="Analyze firmware size without building first",
always_build=True,
)
# Override memory inspection behavior
env.SConscript("sizedata.py", exports="env")
#
# Default targets
#
# Set default targets
Default([target_buildprog, target_size])
@@ -0,0 +1,5 @@
# NimBLE_extended_client example using h2zero Arduino NimBLE stack
BLE 5 client example, using the great [h2zero NimBLE](https://github.com/h2zero/NimBLE-Arduino) implementation.
Thx @h2zero for the great BLE library.
@@ -0,0 +1,53 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env]
platform = espressif32
framework = arduino
monitor_speed = 115200
build_flags =
'-DCONFIG_BT_NIMBLE_EXT_ADV=1'
lib_deps =
https://github.com/h2zero/NimBLE-Arduino
lib_ignore =
BLE
BluetoothSerial
SimpleBLE
WiFiProv
custom_component_remove =
espressif/esp_hosted
espressif/esp_wifi_remote
espressif/esp-dsp
espressif/network_provisioning
espressif/esp_rainmaker
espressif/rmaker_common
espressif/esp_insights
espressif/esp_diag_data_store
espressif/esp_diagnostics
espressif/libsodium
espressif/esp-modbus
espressif/esp-cbor
espressif/esp-sr
espressif/esp32-camera
[env:esp32s3]
board = esp32-s3-devkitc-1
[env:esp32c2]
board = esp32-c2-devkitm-1
[env:esp32c3]
board = esp32-c3-devkitm-1
[env:esp32c6]
board = esp32-c6-devkitm-1
[env:esp32h2]
board = esp32-h2-devkitm-1
@@ -0,0 +1,150 @@
/** NimBLE Extended Client Demo:
*
* Demonstrates the Bluetooth 5.x client capabilities.
*
* Created: on April 2 2022
* Author: H2zero
*
*/
#include <Arduino.h>
#include <NimBLEDevice.h>
#if !CONFIG_BT_NIMBLE_EXT_ADV
# error Must enable extended advertising, see nimconfig.h file.
#endif
#define SERVICE_UUID "ABCD"
#define CHARACTERISTIC_UUID "1234"
static const NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false;
static uint32_t scanTime = 10 * 1000; // In milliseconds, 0 = scan forever
/** Define the PHY's to use when connecting to peer devices, can be 1, 2, or all 3 (default).*/
static uint8_t connectPhys = BLE_GAP_LE_PHY_CODED_MASK | BLE_GAP_LE_PHY_1M_MASK /*| BLE_GAP_LE_PHY_2M_MASK */;
/** Define a class to handle the callbacks for client connection events */
class ClientCallbacks : public NimBLEClientCallbacks {
void onConnect(NimBLEClient* pClient) override { Serial.printf("Connected\n"); };
void onDisconnect(NimBLEClient* pClient, int reason) override {
Serial.printf("%s Disconnected, reason = %d - Starting scan\n", pClient->getPeerAddress().toString().c_str(), reason);
NimBLEDevice::getScan()->start(scanTime);
}
} clientCallbacks;
/** Define a class to handle the callbacks when advertisements are received */
class scanCallbacks : public NimBLEScanCallbacks {
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
Serial.printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
if (advertisedDevice->isAdvertisingService(NimBLEUUID("ABCD"))) {
Serial.printf("Found Our Service\n");
doConnect = true;
/** Save the device reference in a global for the client to use*/
advDevice = advertisedDevice;
/** stop scan before connecting */
NimBLEDevice::getScan()->stop();
}
}
/** Callback to process the results of the completed scan or restart it */
void onScanEnd(const NimBLEScanResults& results, int rc) override { Serial.printf("Scan Ended\n"); }
} scanCallbacks;
/** Handles the provisioning of clients and connects / interfaces with the server */
bool connectToServer() {
NimBLEClient* pClient = nullptr;
pClient = NimBLEDevice::createClient();
pClient->setClientCallbacks(&clientCallbacks, false);
/**
* Set the PHY's to use for this connection. This is a bitmask that represents the PHY's:
* * 0x01 BLE_GAP_LE_PHY_1M_MASK
* * 0x02 BLE_GAP_LE_PHY_2M_MASK
* * 0x04 BLE_GAP_LE_PHY_CODED_MASK
* Combine these with OR ("|"), eg BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK | BLE_GAP_LE_PHY_CODED_MASK;
*/
pClient->setConnectPhy(connectPhys);
/** Set how long we are willing to wait for the connection to complete (milliseconds), default is 30000. */
pClient->setConnectTimeout(10 * 1000);
if (!pClient->connect(advDevice)) {
/** Created a client but failed to connect, don't need to keep it as it has no data */
NimBLEDevice::deleteClient(pClient);
Serial.printf("Failed to connect, deleted client\n");
return false;
}
Serial.printf("Connected to: %s RSSI: %d\n", pClient->getPeerAddress().toString().c_str(), pClient->getRssi());
/** Now we can read/write/subscribe the characteristics of the services we are interested in */
NimBLERemoteService* pSvc = nullptr;
NimBLERemoteCharacteristic* pChr = nullptr;
pSvc = pClient->getService(SERVICE_UUID);
if (pSvc) {
pChr = pSvc->getCharacteristic(CHARACTERISTIC_UUID);
if (pChr) {
if (pChr->canRead()) {
std::string value = pChr->readValue();
Serial.printf("Characteristic value: %s\n", value.c_str());
}
}
} else {
Serial.printf("ABCD service not found.\n");
}
NimBLEDevice::deleteClient(pClient);
Serial.printf("Done with this device!\n");
return true;
}
void setup() {
Serial.begin(115200);
Serial.printf("Starting NimBLE Client\n");
/** Initialize NimBLE and set the device name */
NimBLEDevice::init("NimBLE Extended Client");
/** Create aNimBLE Scan instance and set the callbacks for scan events */
NimBLEScan* pScan = NimBLEDevice::getScan();
pScan->setScanCallbacks(&scanCallbacks);
/** Set scan interval (how often) and window (how long) in milliseconds */
pScan->setInterval(97);
pScan->setWindow(67);
/**
* Active scan will gather scan response data from advertisers
* but will use more energy from both devices
*/
pScan->setActiveScan(true);
/**
* Start scanning for advertisers for the scan time specified (in milliseconds) 0 = forever
* Optional callback for when scanning stops.
*/
pScan->start(scanTime);
Serial.printf("Scanning for peripherals\n");
}
void loop() {
/** Loop here until we find a device we want to connect to */
if (doConnect) {
if (connectToServer()) {
Serial.printf("Success!, scanning for more!\n");
} else {
Serial.printf("Failed to connect, starting scan\n");
}
doConnect = false;
NimBLEDevice::getScan()->start(scanTime);
}
delay(10);
}
+24 -1
View File
@@ -12,6 +12,10 @@ platform = espressif32
framework = arduino
board = esp32-solo1
build_flags = -DLED_BUILTIN=2
lib_ignore = wifi
spiffs
NetworkClientSecure
custom_component_remove =
espressif/esp_hosted
espressif/esp_wifi_remote
@@ -31,6 +35,9 @@ platform = espressif32
framework = arduino
board = esp32-c2-devkitm-1
monitor_speed = 115200
lib_ignore = wifi
spiffs
NetworkClientSecure
custom_component_remove = espressif/esp_hosted
espressif/esp_wifi_remote
espressif/esp-dsp
@@ -43,12 +50,16 @@ custom_component_remove = espressif/esp_hosted
espressif/esp_diagnostics
espressif/esp_rainmaker
espressif/rmaker_common
custom_component_add = espressif/cmake_utilities @ 0.*
[env:esp32-s3-arduino_nano_esp32]
platform = espressif32
framework = arduino
board = arduino_nano_esp32
monitor_speed = 115200
lib_ignore = wifi
spiffs
NetworkClientSecure
custom_component_remove = espressif/esp_hosted
espressif/esp_wifi_remote
espressif/esp-dsp
@@ -67,6 +78,9 @@ custom_component_remove = espressif/esp_hosted
platform = espressif32
framework = arduino
board = esp32s3_120_16_8-qio_opi
lib_ignore =
spiffs
NetworkClientSecure
custom_sdkconfig = CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_120M=y
CONFIG_LCD_RGB_ISR_IRAM_SAFE=y
@@ -87,13 +101,16 @@ custom_component_remove = espressif/esp_hosted
espressif/esp_diagnostics
espressif/esp_rainmaker
espressif/rmaker_common
custom_component_add = lvgl/lvgl @ ^9.2.2
[env:esp32-c6-devkitc-1]
platform = espressif32
framework = arduino
build_type = debug
board = esp32-c6-devkitc-1
monitor_speed = 115200
lib_ignore = wifi
spiffs
NetworkClientSecure
custom_component_remove = espressif/esp_hosted
espressif/esp_wifi_remote
espressif/mdns
@@ -106,6 +123,9 @@ platform = espressif32
framework = arduino
board = esp32-h2-devkitm-1
monitor_speed = 115200
lib_ignore =
spiffs
NetworkClientSecure
custom_component_remove = espressif/esp_hosted
espressif/esp_wifi_remote
espressif/mdns
@@ -118,6 +138,9 @@ platform = espressif32
framework = arduino
board = esp32-p4
build_flags = -DLED_BUILTIN=2
lib_ignore = wifi
spiffs
NetworkClientSecure
monitor_speed = 115200
custom_component_remove = espressif/esp_hosted
espressif/esp_wifi_remote
+12
View File
@@ -2,6 +2,9 @@
platform = espressif32
framework = arduino
board = esp32-s2-saola-1
lib_ignore = wifi
spiffs
NetworkClientSecure
build_flags = -DBUILTIN_RGBLED_PIN=18
-DNR_OF_LEDS=1
@@ -9,6 +12,9 @@ build_flags = -DBUILTIN_RGBLED_PIN=18
platform = espressif32
framework = arduino
board = esp32-s3-devkitc-1
lib_ignore = wifi
spiffs
NetworkClientSecure
build_flags = -DBUILTIN_RGBLED_PIN=48
-DNR_OF_LEDS=1
@@ -16,6 +22,9 @@ build_flags = -DBUILTIN_RGBLED_PIN=48
platform = espressif32
framework = arduino
board = esp32-c3-devkitm-1
lib_ignore = wifi
spiffs
NetworkClientSecure
build_flags = -DBUILTIN_RGBLED_PIN=8
-DNR_OF_LEDS=1
@@ -23,5 +32,8 @@ build_flags = -DBUILTIN_RGBLED_PIN=8
platform = espressif32
framework = arduino
board = esp32-c6-devkitm-1
lib_ignore = wifi
spiffs
NetworkClientSecure
build_flags = -DBUILTIN_RGBLED_PIN=8
-DNR_OF_LEDS=1
+48
View File
@@ -12,3 +12,51 @@ platform = espressif32
framework = arduino
board = esp-wrover-kit
monitor_speed = 115200
[env:esp32-s2]
platform = espressif32
framework = arduino
board = esp32-s2-saola-1
upload_protocol = esp-prog
monitor_speed = 115200
check_tool = clangtidy
[env:esp32-s3]
platform = espressif32
framework = arduino
board = esp32-s3-devkitc-1
upload_protocol = esp-builtin
monitor_speed = 115200
check_tool = cppcheck
[env:esp32-c2]
platform = espressif32
framework = arduino
board = esp32-c2-devkitm-1
upload_protocol = esp-prog
monitor_speed = 115200
check_tool = clangtidy
custom_component_remove =
espressif/esp-dsp
espressif/network_provisioning
espressif/esp-zboss-lib
espressif/esp-zigbee-lib
espressif/esp_rainmaker
espressif/esp-sr
espressif/esp-modbus
espressif/esp32-camera
[env:esp32-c3]
platform = espressif32
framework = arduino
board = esp32-c3-devkitm-1
upload_protocol = esp-builtin
monitor_speed = 115200
check_tool = pvs-studio
[env:esp32-c6]
platform = espressif32
framework = arduino
board = esp32-c6-devkitm-1
upload_protocol = esp-builtin
monitor_speed = 115200
@@ -1,2 +0,0 @@
.pio
.vscode
@@ -1,4 +0,0 @@
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
add_compile_definitions(ARDUINO_ARCH_ESP32=1)
project(Arduino_IDF_BLE_scan)
@@ -1,7 +0,0 @@
# Arduino_IDF_BLE_scan example using 3rd party NimBLE stack
BLE scan example, using the great h2zero NimBLE implementation. The needed NimBLE lib is loaded via the IDF component manager -> `idf_component.yml`.
Mandantory not to forget to switch Arduino included BLE libs.
Done in `sdkconfig.defaults`
Thx @h2zero for the great BLE library.
@@ -1,27 +0,0 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env]
platform = espressif32
framework = arduino, espidf
monitor_speed = 115200
board_build.embed_txtfiles =
managed_components/espressif__esp_insights/server_certs/https_server.crt
managed_components/espressif__esp_rainmaker/server_certs/rmaker_mqtt_server.crt
managed_components/espressif__esp_rainmaker/server_certs/rmaker_claim_service_server.crt
managed_components/espressif__esp_rainmaker/server_certs/rmaker_ota_server.crt
lib_ignore =
BLE
BluetoothSerial
SimpleBLE
WiFiProv
[env:esp32]
board = esp32dev
@@ -1,34 +0,0 @@
# CONFIG_AUTOSTART_ARDUINO is not set
# CONFIG_WS2812_LED_ENABLE is not set
CONFIG_FREERTOS_HZ=1000
CONFIG_MBEDTLS_PSK_MODES=y
CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y
#
# Arduino Configuration
#
#
# Disable all Arduino included BLE libraries
#
CONFIG_ARDUINO_SELECTIVE_COMPILATION=y
# CONFIG_ARDUINO_SELECTIVE_WiFiProv is not set
# CONFIG_ARDUINO_SELECTIVE_BLE is not set
# CONFIG_ARDUINO_SELECTIVE_BluetoothSerial is not set
# CONFIG_ARDUINO_SELECTIVE_SimpleBLE is not set
# end of Arduino Configuration
@@ -1,6 +0,0 @@
# This file was automatically generated for projects
# without default 'CMakeLists.txt' file.
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
idf_component_register(SRCS ${app_sources})
@@ -1,41 +0,0 @@
dependencies:
# Required IDF version
idf: ">=4.4"
esp-nimble-cpp:
git: https://github.com/h2zero/esp-nimble-cpp.git
version: 877a29a8b1d0022c5e8f67ba8b879316e67b6c3d
# # Defining a dependency from the registry:
# # https://components.espressif.com/component/example/cmp
# example/cmp: "^3.3.3" # Automatically update minor releases
#
# # Other ways to define dependencies
#
# # For components maintained by Espressif only name can be used.
# # Same as `espressif/cmp`
# component: "~1.0.0" # Automatically update bugfix releases
#
# # Or in a longer form with extra parameters
# component2:
# version: ">=2.0.0"
#
# # For transient dependencies `public` flag can be set.
# # `public` flag doesn't have an effect for the `main` component.
# # All dependencies of `main` are public by default.
# public: true
#
# # For components hosted on non-default registry:
# service_url: "https://componentregistry.company.com"
#
# # For components in git repository:
# test_component:
# path: test_component
# git: ssh://git@gitlab.com/user/components.git
#
# # For test projects during component development
# # components can be used from a local directory
# # with relative or absolute path
# some_local_component:
# path: ../../projects/component
@@ -1,54 +0,0 @@
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
Ported to Arduino ESP32 by Evandro Copercini
Refactored back to IDF by H2zero
*/
/** NimBLE differences highlighted in comment blocks **/
/*******original********
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
***********************/
#include <Arduino.h>
#include <NimBLEDevice.h>
extern "C"{void app_main(void);}
int scanTime = 5 * 1000; // In milliseconds, 0 = scan forever
BLEScan* pBLEScan;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice* advertisedDevice) {
printf("Advertised Device: %s \n", advertisedDevice->toString().c_str());
}
};
void scanTask (void * parameter){
for(;;) {
// put your main code here, to run repeatedly:
BLEScanResults foundDevices = pBLEScan->getResults(scanTime, false);
printf("Devices found: %d\n", foundDevices.getCount());
printf("Scan done!\n");
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
vTaskDelay(2000/portTICK_PERIOD_MS); // Delay a second between loops.
}
vTaskDelete(NULL);
}
void app_main(void) {
printf("Scanning...\n");
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setScanCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99); // less or equal setInterval value
xTaskCreate(scanTask, "scanTask", 5000, NULL, 1, NULL);
}
@@ -1,10 +1,14 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(PROJECT_VER "1.0")
set(PROJECT_VER_NUMBER 1)
project(matter-light)
# This should be done before using the IDF_TARGET variable.
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(espidf_arduino_matter_light)
# WARNING: This is just an example for using key for decrypting the encrypted OTA image
# Please do not use it as is.
@@ -16,7 +20,7 @@ if(CONFIG_IDF_TARGET_ESP32C2)
include(relinker)
endif()
idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++17;-Os;-DCHIP_HAVE_CONFIG_H" APPEND)
idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++2a;-Os;-DCHIP_HAVE_CONFIG_H" APPEND)
idf_build_set_property(C_COMPILE_OPTIONS "-Os" APPEND)
# For RISCV chips, project_include.cmake sets -Wno-format, but does not clear various
# flags that depend on -Wformat
+9 -12
View File
@@ -1,14 +1,12 @@
| Supported Targets | ESP32-S3 | ESP32-C3 | ESP32-C6 |
| ----------------- | -------- | -------- | -------- |
| Supported Targets | ESP32-C3 | ESP32-C6 |
| ----------------- | -------- | -------- |
# Managed Component Light
This example is configured by default to work with the ESP32-S3, which has the RGB LED GPIO set as pin 48 and the BOOT button on GPIO 0.
This example is configured by default to work with the ESP32-C6, which has the RGB LED GPIO set as pin 8 and the BOOT button on GPIO 9.
This example creates a Color Temperature Light device using the esp_matter component downloaded from the [Espressif Component Registry](https://components.espressif.com/) instead of an extra component locally, so the example can work without setting up the esp-matter environment.
See the [docs](https://docs.espressif.com/projects/esp-matter/en/latest/esp32/developing.html) for more information about building and flashing the firmware.
This example creates a Color Temperature Light device using the esp_matter component automatically downloaded from the [Espressif Component Registry](https://components.espressif.com/). See the [docs](https://docs.espressif.com/projects/esp-matter/en/latest/esp32/developing.html) for more information about matter.
The code is based on the Arduino API and uses Arduino as an IDF Component.
@@ -27,8 +25,8 @@ There is no QR Code to be used when the Smartphone APP wants to add the Matter D
Please enter the code manually: `34970112332`
The devboard has a built-in LED that will be used as the Matter Light.
The default setting of the code uses pin 48 for the ESP32-S3.
Please change it in `main/matter_accessory_driver.h` or in the `sdkconfig.defaults.<SOC>` file.
The default setting of the code uses pin 8 for the ESP32-C6,
Please change it in `main/matter_accessory_driver.h` or in the `sdkconfig.defaults` file.
## LED Status and Factory Mode
@@ -53,10 +51,9 @@ Holding the BOOT button pressed for more than 10 seconds and then releasing it w
## Building the Application using WiFi and Matter
Use ESP-IDF 5.1.4 from https://github.com/espressif/esp-idf/tree/release/v5.1
This example has been tested with Arduino Core 3.0.4
This example has been tested with Arduino Core 3.2.0. It should work with newer versions too.
There is a configuration file for these SoC: esp32s3, esp32c3, esp32c6.
There is a configuration file for these SoCs: esp32c3, esp32c6.
Those are the tested devices that have a WS2812 RGB LED and can run BLE, WiFi and Matter.
In case it is necessary to change the Button Pin or the REG LED Pin, please use the `menuconfig` and change the Menu Option `Light Matter Accessory`
@@ -64,5 +61,5 @@ In case it is necessary to change the Button Pin or the REG LED Pin, please use
## Using OpenThread with Matter
This is possible with the ESP32-C6.
It is neessasy to have a Thread Border Routed in the Matter Environment. Check you matter hardware provider.
It is necessary to have a Thread Border Router in the Matter Environment. Check your Matter hardware provider.
@@ -3,100 +3,40 @@ menu "Light Matter Accessory"
config BUTTON_PIN
int
prompt "Button 1 GPIO"
default ENV_GPIO_BOOT_BUTTON
default 9 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C6
default 0
range -1 ENV_GPIO_IN_RANGE_MAX
help
The GPIO pin for button that will be used to turn on/off the Matter Light. It shall be connected to a push button. It can use the BOOT button of the development board.
endmenu
menu "LEDs"
config WS2812_PIN
int
prompt "WS2812 RGB LED GPIO"
default ENV_GPIO_RGB_LED
default 8 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C6
default 48
range -1 ENV_GPIO_OUT_RANGE_MAX
help
The GPIO pin for the Matter Light that will be driven by RMT. It shall be connected to one single WS2812 RGB LED.
endmenu
# TARGET CONFIGURATION
if IDF_TARGET_ESP32C3
config ENV_GPIO_RANGE_MIN
int
default 0
config ENV_GPIO_RANGE_MIN
int
default 0
config ENV_GPIO_RANGE_MAX
int
default 19
# GPIOs 20/21 are always used by UART in examples
config ENV_GPIO_RANGE_MAX
int
default 19 if IDF_TARGET_ESP32C3
default 30 if IDF_TARGET_ESP32C6
default 48
config ENV_GPIO_IN_RANGE_MAX
int
default ENV_GPIO_RANGE_MAX
config ENV_GPIO_IN_RANGE_MAX
int
default ENV_GPIO_RANGE_MAX
config ENV_GPIO_OUT_RANGE_MAX
int
default ENV_GPIO_RANGE_MAX
config ENV_GPIO_BOOT_BUTTON
int
default 9
config ENV_GPIO_RGB_LED
int
default 8
endif
if IDF_TARGET_ESP32C6
config ENV_GPIO_RANGE_MIN
int
default 0
config ENV_GPIO_RANGE_MAX
int
default 30
# GPIOs 16/17 are always used by UART in examples
config ENV_GPIO_IN_RANGE_MAX
int
default ENV_GPIO_RANGE_MAX
config ENV_GPIO_OUT_RANGE_MAX
int
default ENV_GPIO_RANGE_MAX
config ENV_GPIO_BOOT_BUTTON
int
default 9
config ENV_GPIO_RGB_LED
int
default 8
endif
if IDF_TARGET_ESP32S3
config ENV_GPIO_RANGE_MIN
int
default 0
config ENV_GPIO_RANGE_MAX
int
default 48
config ENV_GPIO_IN_RANGE_MAX
int
default ENV_GPIO_RANGE_MAX
config ENV_GPIO_OUT_RANGE_MAX
int
default ENV_GPIO_RANGE_MAX
config ENV_GPIO_BOOT_BUTTON
int
default 0
config ENV_GPIO_RGB_LED
int
default 48
endif
config ENV_GPIO_OUT_RANGE_MAX
int
default ENV_GPIO_RANGE_MAX
endmenu
@@ -13,235 +13,225 @@
#include "builtinLED.h"
typedef struct {
uint16_t hue;
uint8_t saturation;
uint16_t hue;
uint8_t saturation;
} HS_color_t;
static const HS_color_t temperatureTable[] = {
{4, 100}, {8, 100}, {11, 100}, {14, 100}, {16, 100}, {18, 100}, {20, 100}, {22, 100}, {24, 100}, {25, 100},
{27, 100}, {28, 100}, {30, 100}, {31, 100}, {31, 95}, {30, 89}, {30, 85}, {29, 80}, {29, 76}, {29, 73},
{29, 69}, {28, 66}, {28, 63}, {28, 60}, {28, 57}, {28, 54}, {28, 52}, {27, 49}, {27, 47}, {27, 45},
{27, 43}, {27, 41}, {27, 39}, {27, 37}, {27, 35}, {27, 33}, {27, 31}, {27, 30}, {27, 28}, {27, 26},
{27, 25}, {27, 23}, {27, 22}, {27, 21}, {27, 19}, {27, 18}, {27, 17}, {27, 15}, {28, 14}, {28, 13},
{28, 12}, {29, 10}, {29, 9}, {30, 8}, {31, 7}, {32, 6}, {34, 5}, {36, 4}, {41, 3}, {49, 2},
{0, 0}, {294, 2}, {265, 3}, {251, 4}, {242, 5}, {237, 6}, {233, 7}, {231, 8}, {229, 9}, {228, 10},
{227, 11}, {226, 11}, {226, 12}, {225, 13}, {225, 13}, {224, 14}, {224, 14}, {224, 15}, {224, 15}, {223, 16},
{223, 16}, {223, 17}, {223, 17}, {223, 17}, {222, 18}, {222, 18}, {222, 19}, {222, 19}, {222, 19}, {222, 19},
{222, 20}, {222, 20}, {222, 20}, {222, 21}, {222, 21}
{4, 100}, {8, 100}, {11, 100}, {14, 100}, {16, 100}, {18, 100}, {20, 100}, {22, 100}, {24, 100}, {25, 100}, {27, 100}, {28, 100}, {30, 100}, {31, 100},
{31, 95}, {30, 89}, {30, 85}, {29, 80}, {29, 76}, {29, 73}, {29, 69}, {28, 66}, {28, 63}, {28, 60}, {28, 57}, {28, 54}, {28, 52}, {27, 49},
{27, 47}, {27, 45}, {27, 43}, {27, 41}, {27, 39}, {27, 37}, {27, 35}, {27, 33}, {27, 31}, {27, 30}, {27, 28}, {27, 26}, {27, 25}, {27, 23},
{27, 22}, {27, 21}, {27, 19}, {27, 18}, {27, 17}, {27, 15}, {28, 14}, {28, 13}, {28, 12}, {29, 10}, {29, 9}, {30, 8}, {31, 7}, {32, 6},
{34, 5}, {36, 4}, {41, 3}, {49, 2}, {0, 0}, {294, 2}, {265, 3}, {251, 4}, {242, 5}, {237, 6}, {233, 7}, {231, 8}, {229, 9}, {228, 10},
{227, 11}, {226, 11}, {226, 12}, {225, 13}, {225, 13}, {224, 14}, {224, 14}, {224, 15}, {224, 15}, {223, 16}, {223, 16}, {223, 17}, {223, 17}, {223, 17},
{222, 18}, {222, 18}, {222, 19}, {222, 19}, {222, 19}, {222, 19}, {222, 20}, {222, 20}, {222, 20}, {222, 21}, {222, 21}
};
/* step brightness table: gamma = 2.3 */
static const uint8_t gamma_table[MAX_PROGRESS] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2,
2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5,
5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17,
17, 18, 18, 19, 19, 20, 20, 21, 22, 22, 23, 23, 24, 25, 25, 26,
26, 27, 28, 28, 29, 30, 30, 31, 32, 33, 33, 34, 35, 36, 36, 37,
38, 39, 40, 40, 41, 42, 43, 44, 45, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
68, 69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 82, 83, 84, 86,
87, 88, 89, 91, 92, 93, 95, 96, 97, 99, 100, 101, 103, 104, 105, 107,
108, 110, 111, 112, 114, 115, 117, 118, 120, 121, 123, 124, 126, 128, 129, 131,
132, 134, 135, 137, 139, 140, 142, 144, 145, 147, 149, 150, 152, 154, 156, 157,
159, 161, 163, 164, 166, 168, 170, 172, 174, 175, 177, 179, 181, 183, 185, 187,
189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219,
221, 223, 226, 228, 230, 232, 234, 236, 239, 241, 243, 245, 248, 250, 252, 255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8,
8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20,
21, 22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, 33, 33, 34, 35, 36, 36, 37, 38, 39, 40, 40,
41, 42, 43, 44, 45, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
69, 70, 71, 72, 74, 75, 76, 77, 78, 79, 81, 82, 83, 84, 86, 87, 88, 89, 91, 92, 93, 95, 96, 97, 99, 100, 101, 103, 104,
105, 107, 108, 110, 111, 112, 114, 115, 117, 118, 120, 121, 123, 124, 126, 128, 129, 131, 132, 134, 135, 137, 139, 140, 142, 144, 145, 147, 149,
150, 152, 154, 156, 157, 159, 161, 163, 164, 166, 168, 170, 172, 174, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203,
205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 226, 228, 230, 232, 234, 236, 239, 241, 243, 245, 248, 250, 252, 255,
};
BuiltInLED::BuiltInLED() {
pin_number = (uint8_t) -1; // no pin number
state = false; // LED is off
hsv_color.value = 0; // black color
pin_number = (uint8_t)-1; // no pin number
state = false; // LED is off
hsv_color.value = 0; // black color
}
BuiltInLED::~BuiltInLED(){
end();
BuiltInLED::~BuiltInLED() {
end();
}
led_indicator_color_hsv_t BuiltInLED::rgb2hsv(led_indicator_color_rgb_t rgb) {
led_indicator_color_hsv_t hsv;
uint8_t minRGB, maxRGB;
uint8_t delta;
led_indicator_color_hsv_t hsv;
uint8_t minRGB, maxRGB;
uint8_t delta;
minRGB = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
maxRGB = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
hsv.value = 0;
hsv.v = maxRGB;
delta = maxRGB - minRGB;
minRGB = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
maxRGB = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
hsv.value = 0;
hsv.v = maxRGB;
delta = maxRGB - minRGB;
if (delta == 0) {
hsv.h = 0;
hsv.s = 0;
if (delta == 0) {
hsv.h = 0;
hsv.s = 0;
} else {
hsv.s = delta * 255 / maxRGB;
if (rgb.r == maxRGB) {
hsv.h = (60 * (rgb.g - rgb.b) / delta + 360) % 360;
} else if (rgb.g == maxRGB) {
hsv.h = (60 * (rgb.b - rgb.r) / delta + 120);
} else {
hsv.s = delta * 255 / maxRGB;
if (rgb.r == maxRGB) {
hsv.h = (60 * (rgb.g - rgb.b) / delta + 360) % 360;
} else if (rgb.g == maxRGB) {
hsv.h = (60 * (rgb.b - rgb.r) / delta + 120);
} else {
hsv.h = (60 * (rgb.r - rgb.g) / delta + 240);
}
hsv.h = (60 * (rgb.r - rgb.g) / delta + 240);
}
return hsv;
}
return hsv;
}
led_indicator_color_rgb_t BuiltInLED::hsv2rgb(led_indicator_color_hsv_t hsv) {
led_indicator_color_rgb_t rgb;
uint8_t rgb_max = hsv.v;
uint8_t rgb_min = rgb_max * (255 - hsv.s) / 255.0f;
led_indicator_color_rgb_t rgb;
uint8_t rgb_max = hsv.v;
uint8_t rgb_min = rgb_max * (255 - hsv.s) / 255.0f;
uint8_t i = hsv.h / 60;
uint8_t diff = hsv.h % 60;
uint8_t i = hsv.h / 60;
uint8_t diff = hsv.h % 60;
// RGB adjustment amount by hue
uint8_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
rgb.value = 0;
switch (i) {
// RGB adjustment amount by hue
uint8_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
rgb.value = 0;
switch (i) {
case 0:
rgb.r = rgb_max;
rgb.g = rgb_min + rgb_adj;
rgb.b = rgb_min;
break;
rgb.r = rgb_max;
rgb.g = rgb_min + rgb_adj;
rgb.b = rgb_min;
break;
case 1:
rgb.r = rgb_max - rgb_adj;
rgb.g = rgb_max;
rgb.b = rgb_min;
break;
rgb.r = rgb_max - rgb_adj;
rgb.g = rgb_max;
rgb.b = rgb_min;
break;
case 2:
rgb.r = rgb_min;
rgb.g = rgb_max;
rgb.b = rgb_min + rgb_adj;
break;
rgb.r = rgb_min;
rgb.g = rgb_max;
rgb.b = rgb_min + rgb_adj;
break;
case 3:
rgb.r = rgb_min;
rgb.g = rgb_max - rgb_adj;
rgb.b = rgb_max;
break;
rgb.r = rgb_min;
rgb.g = rgb_max - rgb_adj;
rgb.b = rgb_max;
break;
case 4:
rgb.r = rgb_min + rgb_adj;
rgb.g = rgb_min;
rgb.b = rgb_max;
break;
rgb.r = rgb_min + rgb_adj;
rgb.g = rgb_min;
rgb.b = rgb_max;
break;
default:
rgb.r = rgb_max;
rgb.g = rgb_min;
rgb.b = rgb_max - rgb_adj;
break;
}
rgb.r = rgb_max;
rgb.g = rgb_min;
rgb.b = rgb_max - rgb_adj;
break;
}
// gamma correction
rgb.r = gamma_table[rgb.r];
rgb.g = gamma_table[rgb.g];
rgb.b = gamma_table[rgb.b];
return rgb;
// gamma correction
rgb.r = gamma_table[rgb.r];
rgb.g = gamma_table[rgb.g];
rgb.b = gamma_table[rgb.b];
return rgb;
}
void BuiltInLED::begin(uint8_t pin){
if (pin < NUM_DIGITAL_PINS) {
pin_number = pin;
log_i("Initializing pin %d", pin);
void BuiltInLED::begin(uint8_t pin) {
if (pin < NUM_DIGITAL_PINS) {
pin_number = pin;
log_i("Initializing pin %d", pin);
} else {
log_e("Invalid pin (%d) number", pin);
}
}
void BuiltInLED::end() {
state = false;
write(); // turn off the LED
if (pin_number < NUM_DIGITAL_PINS) {
if (!rmtDeinit(pin_number)) {
log_e("Failed to deinitialize RMT");
}
}
}
void BuiltInLED::on() {
state = true;
}
void BuiltInLED::off() {
state = false;
}
void BuiltInLED::toggle() {
state = !state;
}
bool BuiltInLED::getState() {
return state;
}
bool BuiltInLED::write() {
led_indicator_color_rgb_t rgb_color = getRGB();
log_d("Writing to pin %d with state = %s", pin_number, state ? "ON" : "OFF");
log_d("HSV: %d, %d, %d", hsv_color.h, hsv_color.s, hsv_color.v);
log_d("RGB: %d, %d, %d", rgb_color.r, rgb_color.g, rgb_color.b);
if (pin_number < NUM_DIGITAL_PINS) {
if (state) {
rgbLedWrite(pin_number, rgb_color.r, rgb_color.g, rgb_color.b);
} else {
log_e("Invalid pin (%d) number", pin);
}
}
void BuiltInLED::end(){
state = false;
write(); // turn off the LED
if (pin_number < NUM_DIGITAL_PINS) {
if (!rmtDeinit(pin_number)) {
log_e("Failed to deinitialize RMT");
}
rgbLedWrite(pin_number, 0, 0, 0);
}
return true;
} else {
log_e("Invalid pin (%d) number", pin_number);
return false;
}
}
void BuiltInLED::on(){
state = true;
void BuiltInLED::setBrightness(uint8_t brightness) {
hsv_color.v = brightness;
}
void BuiltInLED::off(){
state = false;
uint8_t BuiltInLED::getBrightness() {
return hsv_color.v;
}
void BuiltInLED::toggle(){
state = !state;
void BuiltInLED::setHSV(led_indicator_color_hsv_t hsv) {
if (hsv.h > MAX_HUE) {
hsv.h = MAX_HUE;
}
hsv_color.value = hsv.value;
}
bool BuiltInLED::getState(){
return state;
led_indicator_color_hsv_t BuiltInLED::getHSV() {
return hsv_color;
}
bool BuiltInLED::write(){
led_indicator_color_rgb_t rgb_color = getRGB();
log_d("Writing to pin %d with state = %s", pin_number, state ? "ON" : "OFF");
log_d("HSV: %d, %d, %d", hsv_color.h, hsv_color.s, hsv_color.v);
log_d("RGB: %d, %d, %d", rgb_color.r, rgb_color.g, rgb_color.b);
if(pin_number < NUM_DIGITAL_PINS){
if (state) {
rgbLedWrite(pin_number, rgb_color.r, rgb_color.g, rgb_color.b);
} else {
rgbLedWrite(pin_number, 0, 0, 0);
}
return true;
void BuiltInLED::setRGB(led_indicator_color_rgb_t rgb_color) {
hsv_color = rgb2hsv(rgb_color);
}
led_indicator_color_rgb_t BuiltInLED::getRGB() {
return hsv2rgb(hsv_color);
}
void BuiltInLED::setTemperature(uint32_t temperature) {
uint16_t hue;
uint8_t saturation;
log_d("Requested Temperature: %ld", temperature);
//hsv_color.v = gamma_table[((temperature >> 25) & 0x7F)];
temperature &= 0xFFFFFF;
if (temperature < 600) {
hue = 0;
saturation = 100;
} else {
if (temperature > 10000) {
hue = 222;
saturation = 21 + (temperature - 10000) * 41 / 990000;
} else {
log_e("Invalid pin (%d) number", pin_number);
return false;
temperature -= 600;
temperature /= 100;
hue = temperatureTable[temperature].hue;
saturation = temperatureTable[temperature].saturation;
}
}
void BuiltInLED::setBrightness(uint8_t brightness){
hsv_color.v = brightness;
}
uint8_t BuiltInLED::getBrightness(){
return hsv_color.v;
}
void BuiltInLED::setHSV(led_indicator_color_hsv_t hsv){
if (hsv.h > MAX_HUE) {
hsv.h = MAX_HUE;
}
hsv_color.value = hsv.value;
}
led_indicator_color_hsv_t BuiltInLED::getHSV(){
return hsv_color;
}
void BuiltInLED::setRGB(led_indicator_color_rgb_t rgb_color){
hsv_color = rgb2hsv(rgb_color);
}
led_indicator_color_rgb_t BuiltInLED::getRGB(){
return hsv2rgb(hsv_color);
}
void BuiltInLED::setTemperature(uint32_t temperature){
uint16_t hue;
uint8_t saturation;
log_d("Requested Temperature: %ld", temperature);
//hsv_color.v = gamma_table[((temperature >> 25) & 0x7F)];
temperature &= 0xFFFFFF;
if (temperature < 600) {
hue = 0;
saturation = 100;
} else {
if (temperature > 10000) {
hue = 222;
saturation = 21 + (temperature - 10000) * 41 / 990000;
} else {
temperature -= 600;
temperature /= 100;
hue = temperatureTable[temperature].hue;
saturation = temperatureTable[temperature].saturation;
}
}
saturation = (saturation * 255) / 100;
// brightness is not changed
hsv_color.h = hue;
hsv_color.s = saturation;
log_d("Calculated Temperature: %ld, Hue: %d, Saturation: %d, Brightness: %d", temperature, hue, saturation, hsv_color.v);
}
saturation = (saturation * 255) / 100;
// brightness is not changed
hsv_color.h = hue;
hsv_color.s = saturation;
log_d("Calculated Temperature: %ld, Hue: %d, Saturation: %d, Brightness: %d", temperature, hue, saturation, hsv_color.v);
}
@@ -14,61 +14,61 @@
#include <Arduino.h>
#define MAX_HUE 360
#define MAX_HUE 360
#define MAX_SATURATION 255
#define MAX_BRIGHTNESS 255
#define MAX_PROGRESS 256
#define MAX_PROGRESS 256
typedef struct {
union {
struct {
uint32_t v: 8; /*!< Brightness/Value of the LED. 0-255 */
uint32_t s: 8; /*!< Saturation of the LED. 0-255 */
uint32_t h: 9; /*!< Hue of the LED. 0-360 */
};
uint32_t value; /*!< IHSV value of the LED. */
union {
struct {
uint32_t v : 8; /*!< Brightness/Value of the LED. 0-255 */
uint32_t s : 8; /*!< Saturation of the LED. 0-255 */
uint32_t h : 9; /*!< Hue of the LED. 0-360 */
};
uint32_t value; /*!< IHSV value of the LED. */
};
} led_indicator_color_hsv_t;
typedef struct {
union {
struct {
uint32_t r: 8; /*!< Red component of the LED color. Range: 0-255. */
uint32_t g: 8; /*!< Green component of the LED color. Range: 0-255. */
uint32_t b: 8; /*!< Blue component of the LED color. Range: 0-255. */
};
uint32_t value; /*!< Combined RGB value of the LED color. */
union {
struct {
uint32_t r : 8; /*!< Red component of the LED color. Range: 0-255. */
uint32_t g : 8; /*!< Green component of the LED color. Range: 0-255. */
uint32_t b : 8; /*!< Blue component of the LED color. Range: 0-255. */
};
uint32_t value; /*!< Combined RGB value of the LED color. */
};
} led_indicator_color_rgb_t;
class BuiltInLED {
private:
uint8_t pin_number;
bool state;
led_indicator_color_hsv_t hsv_color;
uint8_t pin_number;
bool state;
led_indicator_color_hsv_t hsv_color;
public:
BuiltInLED();
~BuiltInLED();
BuiltInLED();
~BuiltInLED();
static led_indicator_color_hsv_t rgb2hsv(led_indicator_color_rgb_t rgb_value);
static led_indicator_color_rgb_t hsv2rgb(led_indicator_color_hsv_t hsv);
static led_indicator_color_hsv_t rgb2hsv(led_indicator_color_rgb_t rgb_value);
static led_indicator_color_rgb_t hsv2rgb(led_indicator_color_hsv_t hsv);
void begin(uint8_t pin);
void end();
void begin(uint8_t pin);
void end();
void on();
void off();
void toggle();
bool getState();
void on();
void off();
void toggle();
bool getState();
bool write();
bool write();
void setBrightness(uint8_t brightness);
uint8_t getBrightness();
void setHSV(led_indicator_color_hsv_t hsv);
led_indicator_color_hsv_t getHSV();
void setRGB(led_indicator_color_rgb_t color);
led_indicator_color_rgb_t getRGB();
void setTemperature(uint32_t temperature);
void setBrightness(uint8_t brightness);
uint8_t getBrightness();
void setHSV(led_indicator_color_hsv_t hsv);
led_indicator_color_hsv_t getHSV();
void setRGB(led_indicator_color_rgb_t color);
led_indicator_color_rgb_t getRGB();
void setTemperature(uint32_t temperature);
};
@@ -1,6 +1,6 @@
dependencies:
espressif/esp_matter:
version: "^1.3.0"
version: ">=1.4.0"
espressif/cmake_utilities:
version: "0.*"
rules:
@@ -11,85 +11,79 @@
#include "matter_accessory_driver.h"
/* Do any conversions/remapping for the actual value here */
esp_err_t light_accessory_set_power(void *led, uint8_t val)
{
BuiltInLED *builtinLED = (BuiltInLED *) led;
esp_err_t err = ESP_OK;
if (val) {
builtinLED->on();
} else {
builtinLED->off();
}
if (!builtinLED->write()) {
err = ESP_FAIL;
}
log_i("LED set power: %d", val);
return err;
esp_err_t light_accessory_set_power(void *led, uint8_t val) {
BuiltInLED *builtinLED = (BuiltInLED *)led;
esp_err_t err = ESP_OK;
if (val) {
builtinLED->on();
} else {
builtinLED->off();
}
if (!builtinLED->write()) {
err = ESP_FAIL;
}
log_i("LED set power: %d", val);
return err;
}
esp_err_t light_accessory_set_brightness(void *led, uint8_t val)
{
esp_err_t err = ESP_OK;
BuiltInLED *builtinLED = (BuiltInLED *) led;
int value = REMAP_TO_RANGE(val, MATTER_BRIGHTNESS, STANDARD_BRIGHTNESS);
esp_err_t light_accessory_set_brightness(void *led, uint8_t val) {
esp_err_t err = ESP_OK;
BuiltInLED *builtinLED = (BuiltInLED *)led;
int value = REMAP_TO_RANGE(val, MATTER_BRIGHTNESS, STANDARD_BRIGHTNESS);
builtinLED->setBrightness(value);
if (!builtinLED->write()) {
err = ESP_FAIL;
}
log_i("LED set brightness: %d", value);
return err;
builtinLED->setBrightness(value);
if (!builtinLED->write()) {
err = ESP_FAIL;
}
log_i("LED set brightness: %d", value);
return err;
}
esp_err_t light_accessory_set_hue(void *led, uint8_t val)
{
esp_err_t err = ESP_OK;
BuiltInLED *builtinLED = (BuiltInLED *) led;
int value = REMAP_TO_RANGE(val, MATTER_HUE, STANDARD_HUE);
led_indicator_color_hsv_t hsv = builtinLED->getHSV();
hsv.h = value;
builtinLED->setHSV(hsv);
if (!builtinLED->write()) {
err = ESP_FAIL;
}
log_i("LED set hue: %d", value);
return err;
esp_err_t light_accessory_set_hue(void *led, uint8_t val) {
esp_err_t err = ESP_OK;
BuiltInLED *builtinLED = (BuiltInLED *)led;
int value = REMAP_TO_RANGE(val, MATTER_HUE, STANDARD_HUE);
led_indicator_color_hsv_t hsv = builtinLED->getHSV();
hsv.h = value;
builtinLED->setHSV(hsv);
if (!builtinLED->write()) {
err = ESP_FAIL;
}
log_i("LED set hue: %d", value);
return err;
}
esp_err_t light_accessory_set_saturation(void *led, uint8_t val)
{
esp_err_t err = ESP_OK;
BuiltInLED *builtinLED = (BuiltInLED *) led;
int value = REMAP_TO_RANGE(val, MATTER_SATURATION, STANDARD_SATURATION);
led_indicator_color_hsv_t hsv = builtinLED->getHSV();
hsv.s = value;
builtinLED->setHSV(hsv);
if (!builtinLED->write()) {
err = ESP_FAIL;
}
log_i("LED set saturation: %d", value);
return err;
esp_err_t light_accessory_set_saturation(void *led, uint8_t val) {
esp_err_t err = ESP_OK;
BuiltInLED *builtinLED = (BuiltInLED *)led;
int value = REMAP_TO_RANGE(val, MATTER_SATURATION, STANDARD_SATURATION);
led_indicator_color_hsv_t hsv = builtinLED->getHSV();
hsv.s = value;
builtinLED->setHSV(hsv);
if (!builtinLED->write()) {
err = ESP_FAIL;
}
log_i("LED set saturation: %d", value);
return err;
}
esp_err_t light_accessory_set_temperature(void *led, uint16_t val)
{
esp_err_t err = ESP_OK;
BuiltInLED *builtinLED = (BuiltInLED *) led;
uint32_t value = REMAP_TO_RANGE_INVERSE(val, STANDARD_TEMPERATURE_FACTOR);
builtinLED->setTemperature(value);
if (!builtinLED->write()) {
err = ESP_FAIL;
}
log_i("LED set temperature: %ld", value);
return err;
esp_err_t light_accessory_set_temperature(void *led, uint16_t val) {
esp_err_t err = ESP_OK;
BuiltInLED *builtinLED = (BuiltInLED *)led;
uint32_t value = REMAP_TO_RANGE_INVERSE(val, STANDARD_TEMPERATURE_FACTOR);
builtinLED->setTemperature(value);
if (!builtinLED->write()) {
err = ESP_FAIL;
}
log_i("LED set temperature: %ld", value);
return err;
}
app_driver_handle_t light_accessory_init()
{
/* Initialize led */
static BuiltInLED builtinLED;
app_driver_handle_t light_accessory_init() {
/* Initialize led */
static BuiltInLED builtinLED;
const uint8_t pin = WS2812_PIN; // set your board WS2812b pin here
builtinLED.begin(pin);
return (app_driver_handle_t) &builtinLED;
const uint8_t pin = WS2812_PIN; // set your board WS2812b pin here
builtinLED.begin(pin);
return (app_driver_handle_t)&builtinLED;
}
@@ -3,7 +3,7 @@
// set your board WS2812b pin here (e.g. 48 is the default pin for the ESP32-S3 devkit)
#ifndef CONFIG_WS2812_PIN
#define WS2812_PIN 48 // ESP32-S3 DevKitC built-in LED
#define WS2812_PIN 48 // ESP32-S3 DevKitC built-in LED
#else
#define WS2812_PIN CONFIG_WS2812_PIN // From sdkconfig.defaults.<soc>
#endif
@@ -14,32 +14,32 @@
// Set your board button pin here (e.g. 0 is the default pin for the ESP32-S3 devkit)
#ifndef CONFIG_BUTTON_PIN
#define BUTTON_PIN 0 // ESP32-S3 DevKitC built-in button
#define BUTTON_PIN 0 // ESP32-S3 DevKitC built-in button
#else
#define BUTTON_PIN CONFIG_BUTTON_PIN // From sdkconfig.defaults.<soc>
#define BUTTON_PIN CONFIG_BUTTON_PIN // From sdkconfig.defaults.<soc>
#endif
/** Standard max values (used for remapping attributes) */
#define STANDARD_BRIGHTNESS 255
#define STANDARD_HUE 360
#define STANDARD_SATURATION 255
#define STANDARD_BRIGHTNESS 255
#define STANDARD_HUE 360
#define STANDARD_SATURATION 255
#define STANDARD_TEMPERATURE_FACTOR 1000000
/** Matter max values (used for remapping attributes) */
#define MATTER_BRIGHTNESS 254
#define MATTER_HUE 254
#define MATTER_SATURATION 254
#define MATTER_BRIGHTNESS 254
#define MATTER_HUE 254
#define MATTER_SATURATION 254
#define MATTER_TEMPERATURE_FACTOR 1000000
/** Default attribute values used during initialization */
#define DEFAULT_POWER true
#define DEFAULT_POWER true
#define DEFAULT_BRIGHTNESS 64
#define DEFAULT_HUE 128
#define DEFAULT_HUE 128
#define DEFAULT_SATURATION 254
typedef void *app_driver_handle_t;
esp_err_t light_accessory_set_power(void *led, uint8_t val);
esp_err_t light_accessory_set_power(void *led, uint8_t val);
esp_err_t light_accessory_set_brightness(void *led, uint8_t val);
esp_err_t light_accessory_set_hue(void *led, uint8_t val);
esp_err_t light_accessory_set_saturation(void *led, uint8_t val);
@@ -20,24 +20,18 @@
#include <platform/ESP32/OpenthreadLauncher.h>
#include "esp_openthread_types.h"
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
{ \
.radio_mode = RADIO_MODE_NATIVE, \
}
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG() \
{ .radio_mode = RADIO_MODE_NATIVE, }
#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
{ \
.host_connection_mode = HOST_CONNECTION_MODE_NONE, \
}
#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG() \
{ .host_connection_mode = HOST_CONNECTION_MODE_NONE, }
#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \
{ \
.storage_partition_name = "nvs", .netif_queue_size = 10, .task_queue_size = 10, \
}
#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG() \
{ .storage_partition_name = "nvs", .netif_queue_size = 10, .task_queue_size = 10, }
#endif
// set your board button pin here
const uint8_t button_gpio = BUTTON_PIN; // GPIO BOOT Button
const uint8_t button_gpio = BUTTON_PIN; // GPIO BOOT Button
uint16_t light_endpoint_id = 0;
@@ -54,290 +48,259 @@ extern const char decryption_key_end[] asm("_binary_esp_image_encryption_key_pem
static const char *s_decryption_key = decryption_key_start;
static const uint16_t s_decryption_key_len = decryption_key_end - decryption_key_start;
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
bool isAccessoryCommissioned() {
return chip::Server::GetInstance().GetFabricTable().FabricCount() > 0;
return chip::Server::GetInstance().GetFabricTable().FabricCount() > 0;
}
#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION
bool isWifiConnected() {
return chip::DeviceLayer::ConnectivityMgr().IsWiFiStationConnected();
return chip::DeviceLayer::ConnectivityMgr().IsWiFiStationConnected();
}
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
bool isThreadConnected() {
return chip::DeviceLayer::ConnectivityMgr().IsThreadAttached();
return chip::DeviceLayer::ConnectivityMgr().IsThreadAttached();
}
#endif
static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg)
{
switch (event->Type) {
static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg) {
switch (event->Type) {
case chip::DeviceLayer::DeviceEventType::kInterfaceIpAddressChanged:
log_i("Interface %s Address changed",
event->InterfaceIpAddressChanged.Type == chip::DeviceLayer::InterfaceIpChangeType::kIpV4_Assigned ?
"IPv4" : "IPV6" );
break;
log_i(
"Interface %s Address changed", event->InterfaceIpAddressChanged.Type == chip::DeviceLayer::InterfaceIpChangeType::kIpV4_Assigned ? "IPv4" : "IPV6"
);
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningComplete:
log_i("Commissioning complete");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningComplete: log_i("Commissioning complete"); break;
case chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired:
log_i("Commissioning failed, fail safe timer expired");
break;
case chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired: log_i("Commissioning failed, fail safe timer expired"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStarted:
log_i("Commissioning session started");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStarted: log_i("Commissioning session started"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStopped:
log_i("Commissioning session stopped");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStopped: log_i("Commissioning session stopped"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowOpened:
log_i("Commissioning window opened");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowOpened: log_i("Commissioning window opened"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowClosed:
log_i("Commissioning window closed");
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowClosed: log_i("Commissioning window closed"); break;
case chip::DeviceLayer::DeviceEventType::kFabricRemoved:
{
log_i("Fabric removed successfully");
if (chip::Server::GetInstance().GetFabricTable().FabricCount() == 0)
{
chip::CommissioningWindowManager & commissionMgr = chip::Server::GetInstance().GetCommissioningWindowManager();
constexpr auto kTimeoutSeconds = chip::System::Clock::Seconds16(k_timeout_seconds);
if (!commissionMgr.IsCommissioningWindowOpen())
{
/* After removing last fabric, this example does not remove the Wi-Fi credentials
{
log_i("Fabric removed successfully");
if (chip::Server::GetInstance().GetFabricTable().FabricCount() == 0) {
chip::CommissioningWindowManager &commissionMgr = chip::Server::GetInstance().GetCommissioningWindowManager();
constexpr auto kTimeoutSeconds = chip::System::Clock::Seconds16(k_timeout_seconds);
if (!commissionMgr.IsCommissioningWindowOpen()) {
/* After removing last fabric, this example does not remove the Wi-Fi credentials
* and still has IP connectivity so, only advertising on DNS-SD.
*/
CHIP_ERROR err = commissionMgr.OpenBasicCommissioningWindow(kTimeoutSeconds,
chip::CommissioningWindowAdvertisement::kDnssdOnly);
if (err != CHIP_NO_ERROR)
{
log_e("Failed to open commissioning window, err:%" CHIP_ERROR_FORMAT, err.Format());
}
}
}
break;
CHIP_ERROR err = commissionMgr.OpenBasicCommissioningWindow(kTimeoutSeconds, chip::CommissioningWindowAdvertisement::kDnssdOnly);
if (err != CHIP_NO_ERROR) {
log_e("Failed to open commissioning window, err:%" CHIP_ERROR_FORMAT, err.Format());
}
}
case chip::DeviceLayer::DeviceEventType::kFabricWillBeRemoved:
log_i("Fabric will be removed");
break;
case chip::DeviceLayer::DeviceEventType::kFabricUpdated:
log_i("Fabric is updated");
break;
case chip::DeviceLayer::DeviceEventType::kFabricCommitted:
log_i("Fabric is committed");
break;
case chip::DeviceLayer::DeviceEventType::kBLEDeinitialized:
log_i("BLE deinitialized and memory reclaimed");
break;
default:
break;
}
}
esp_err_t matter_light_attribute_update(app_driver_handle_t driver_handle, uint16_t endpoint_id, uint32_t cluster_id,
uint32_t attribute_id, esp_matter_attr_val_t *val)
{
esp_err_t err = ESP_OK;
if (endpoint_id == light_endpoint_id) {
void *led = (void *)driver_handle;
if (cluster_id == OnOff::Id) {
if (attribute_id == OnOff::Attributes::OnOff::Id) {
err = light_accessory_set_power(led, val->val.b);
}
} else if (cluster_id == LevelControl::Id) {
if (attribute_id == LevelControl::Attributes::CurrentLevel::Id) {
err = light_accessory_set_brightness(led, val->val.u8);
}
} else if (cluster_id == ColorControl::Id) {
if (attribute_id == ColorControl::Attributes::CurrentHue::Id) {
err = light_accessory_set_hue(led, val->val.u8);
} else if (attribute_id == ColorControl::Attributes::CurrentSaturation::Id) {
err = light_accessory_set_saturation(led, val->val.u8);
} else if (attribute_id == ColorControl::Attributes::ColorTemperatureMireds::Id) {
err = light_accessory_set_temperature(led, val->val.u16);
}
}
}
return err;
}
esp_err_t matter_light_set_defaults(uint16_t endpoint_id)
{
esp_err_t err = ESP_OK;
void *led = endpoint::get_priv_data(endpoint_id);
node_t *node = node::get();
endpoint_t *endpoint = endpoint::get(node, endpoint_id);
cluster_t *cluster = NULL;
attribute_t *attribute = NULL;
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
/* Setting brightness */
cluster = cluster::get(endpoint, LevelControl::Id);
attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id);
attribute::get_val(attribute, &val);
err |= light_accessory_set_brightness(led, val.val.u8);
/* Setting color */
cluster = cluster::get(endpoint, ColorControl::Id);
attribute = attribute::get(cluster, ColorControl::Attributes::ColorMode::Id);
attribute::get_val(attribute, &val);
if (val.val.u8 == (uint8_t)ColorControl::ColorMode::kCurrentHueAndCurrentSaturation) {
/* Setting hue */
attribute = attribute::get(cluster, ColorControl::Attributes::CurrentHue::Id);
attribute::get_val(attribute, &val);
err |= light_accessory_set_hue(led, val.val.u8);
/* Setting saturation */
attribute = attribute::get(cluster, ColorControl::Attributes::CurrentSaturation::Id);
attribute::get_val(attribute, &val);
err |= light_accessory_set_saturation(led, val.val.u8);
} else if (val.val.u8 == (uint8_t)ColorControl::ColorMode::kColorTemperature) {
/* Setting temperature */
attribute = attribute::get(cluster, ColorControl::Attributes::ColorTemperatureMireds::Id);
attribute::get_val(attribute, &val);
err |= light_accessory_set_temperature(led, val.val.u16);
} else {
log_e("Color mode not supported");
}
break;
}
/* Setting power */
cluster = cluster::get(endpoint, OnOff::Id);
attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id);
attribute::get_val(attribute, &val);
err |= light_accessory_set_power(led, val.val.b);
case chip::DeviceLayer::DeviceEventType::kFabricWillBeRemoved: log_i("Fabric will be removed"); break;
return err;
case chip::DeviceLayer::DeviceEventType::kFabricUpdated: log_i("Fabric is updated"); break;
case chip::DeviceLayer::DeviceEventType::kFabricCommitted: log_i("Fabric is committed"); break;
case chip::DeviceLayer::DeviceEventType::kBLEDeinitialized: log_i("BLE deinitialized and memory reclaimed"); break;
default: break;
}
}
void button_driver_init()
{
/* Initialize button */
pinMode(button_gpio, INPUT_PULLUP);
esp_err_t matter_light_attribute_update(
app_driver_handle_t driver_handle, uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val
) {
esp_err_t err = ESP_OK;
if (endpoint_id == light_endpoint_id) {
void *led = (void *)driver_handle;
if (cluster_id == OnOff::Id) {
if (attribute_id == OnOff::Attributes::OnOff::Id) {
err = light_accessory_set_power(led, val->val.b);
}
} else if (cluster_id == LevelControl::Id) {
if (attribute_id == LevelControl::Attributes::CurrentLevel::Id) {
err = light_accessory_set_brightness(led, val->val.u8);
}
} else if (cluster_id == ColorControl::Id) {
if (attribute_id == ColorControl::Attributes::CurrentHue::Id) {
err = light_accessory_set_hue(led, val->val.u8);
} else if (attribute_id == ColorControl::Attributes::CurrentSaturation::Id) {
err = light_accessory_set_saturation(led, val->val.u8);
} else if (attribute_id == ColorControl::Attributes::ColorTemperatureMireds::Id) {
err = light_accessory_set_temperature(led, val->val.u16);
}
}
}
return err;
}
esp_err_t matter_light_set_defaults(uint16_t endpoint_id) {
esp_err_t err = ESP_OK;
void *led = endpoint::get_priv_data(endpoint_id);
node_t *node = node::get();
endpoint_t *endpoint = endpoint::get(node, endpoint_id);
cluster_t *cluster = NULL;
attribute_t *attribute = NULL;
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
/* Setting brightness */
cluster = cluster::get(endpoint, LevelControl::Id);
attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id);
attribute::get_val(attribute, &val);
err |= light_accessory_set_brightness(led, val.val.u8);
/* Setting color */
cluster = cluster::get(endpoint, ColorControl::Id);
attribute = attribute::get(cluster, ColorControl::Attributes::ColorMode::Id);
attribute::get_val(attribute, &val);
if (val.val.u8 == (uint8_t)ColorControl::ColorMode::kCurrentHueAndCurrentSaturation) {
/* Setting hue */
attribute = attribute::get(cluster, ColorControl::Attributes::CurrentHue::Id);
attribute::get_val(attribute, &val);
err |= light_accessory_set_hue(led, val.val.u8);
/* Setting saturation */
attribute = attribute::get(cluster, ColorControl::Attributes::CurrentSaturation::Id);
attribute::get_val(attribute, &val);
err |= light_accessory_set_saturation(led, val.val.u8);
} else if (val.val.u8 == (uint8_t)ColorControl::ColorMode::kColorTemperature) {
/* Setting temperature */
attribute = attribute::get(cluster, ColorControl::Attributes::ColorTemperatureMireds::Id);
attribute::get_val(attribute, &val);
err |= light_accessory_set_temperature(led, val.val.u16);
} else {
log_e("Color mode not supported");
}
/* Setting power */
cluster = cluster::get(endpoint, OnOff::Id);
attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id);
attribute::get_val(attribute, &val);
err |= light_accessory_set_power(led, val.val.b);
return err;
}
void button_driver_init() {
/* Initialize button */
pinMode(button_gpio, INPUT_PULLUP);
}
// This callback is called for every attribute update. The callback implementation shall
// handle the desired attributes and return an appropriate error code. If the attribute
// is not of your interest, please do not return an error code and strictly return ESP_OK.
static esp_err_t app_attribute_update_cb(attribute::callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id,
uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data)
{
esp_err_t err = ESP_OK;
static esp_err_t app_attribute_update_cb(
attribute::callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data
) {
esp_err_t err = ESP_OK;
if (type == PRE_UPDATE) {
/* Driver update */
app_driver_handle_t driver_handle = (app_driver_handle_t)priv_data;
err = matter_light_attribute_update(driver_handle, endpoint_id, cluster_id, attribute_id, val);
}
if (type == PRE_UPDATE) {
/* Driver update */
app_driver_handle_t driver_handle = (app_driver_handle_t)priv_data;
err = matter_light_attribute_update(driver_handle, endpoint_id, cluster_id, attribute_id, val);
}
return err;
return err;
}
// This callback is invoked when clients interact with the Identify Cluster.
// In the callback implementation, an endpoint can identify itself. (e.g., by flashing an LED or light).
static esp_err_t app_identification_cb(identification::callback_type_t type, uint16_t endpoint_id, uint8_t effect_id,
uint8_t effect_variant, void *priv_data)
{
log_i("Identification callback: type: %u, effect: %u, variant: %u", type, effect_id, effect_variant);
return ESP_OK;
static esp_err_t app_identification_cb(identification::callback_type_t type, uint16_t endpoint_id, uint8_t effect_id, uint8_t effect_variant, void *priv_data) {
log_i("Identification callback: type: %u, effect: %u, variant: %u", type, effect_id, effect_variant);
return ESP_OK;
}
void setup()
{
esp_err_t err = ESP_OK;
void setup() {
esp_err_t err = ESP_OK;
/* Initialize driver */
app_driver_handle_t light_handle = light_accessory_init();
button_driver_init();
/* Initialize driver */
app_driver_handle_t light_handle = light_accessory_init();
button_driver_init();
/* Create a Matter node and add the mandatory Root Node device type on endpoint 0 */
node::config_t node_config;
/* Create a Matter node and add the mandatory Root Node device type on endpoint 0 */
node::config_t node_config;
// node handle can be used to add/modify other endpoints.
node_t *node = node::create(&node_config, app_attribute_update_cb, app_identification_cb);
if (node == nullptr) {
log_e("Failed to create Matter node");
abort();
}
// node handle can be used to add/modify other endpoints.
node_t *node = node::create(&node_config, app_attribute_update_cb, app_identification_cb);
if (node == nullptr) {
log_e("Failed to create Matter node");
abort();
}
extended_color_light::config_t light_config;
light_config.on_off.on_off = DEFAULT_POWER;
light_config.on_off.lighting.start_up_on_off = nullptr;
light_config.level_control.current_level = DEFAULT_BRIGHTNESS;
light_config.level_control.lighting.start_up_current_level = DEFAULT_BRIGHTNESS;
light_config.color_control.color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature;
light_config.color_control.enhanced_color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature;
light_config.color_control.color_temperature.startup_color_temperature_mireds = nullptr;
extended_color_light::config_t light_config;
light_config.on_off.on_off = DEFAULT_POWER;
light_config.on_off.lighting.start_up_on_off = nullptr;
light_config.level_control.current_level = DEFAULT_BRIGHTNESS;
light_config.level_control.lighting.start_up_current_level = DEFAULT_BRIGHTNESS;
light_config.color_control.color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature;
light_config.color_control.enhanced_color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature;
light_config.color_control.color_temperature.startup_color_temperature_mireds = nullptr;
// endpoint handles can be used to add/modify clusters.
endpoint_t *endpoint = extended_color_light::create(node, &light_config, ENDPOINT_FLAG_NONE, light_handle);
if (endpoint == nullptr) {
log_e("Failed to create extended color light endpoint");
abort();
}
// endpoint handles can be used to add/modify clusters.
endpoint_t *endpoint = extended_color_light::create(node, &light_config, ENDPOINT_FLAG_NONE, light_handle);
if (endpoint == nullptr) {
log_e("Failed to create extended color light endpoint");
abort();
}
light_endpoint_id = endpoint::get_id(endpoint);
log_i("Light created with endpoint_id %d", light_endpoint_id);
light_endpoint_id = endpoint::get_id(endpoint);
log_i("Light created with endpoint_id %d", light_endpoint_id);
/* Mark deferred persistence for some attributes that might be changed rapidly */
cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id);
attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id);
attribute::set_deferred_persistence(current_level_attribute);
/* Mark deferred persistence for some attributes that might be changed rapidly */
cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id);
attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id);
attribute::set_deferred_persistence(current_level_attribute);
cluster_t *color_control_cluster = cluster::get(endpoint, ColorControl::Id);
attribute_t *current_x_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::CurrentX::Id);
attribute::set_deferred_persistence(current_x_attribute);
attribute_t *current_y_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::CurrentY::Id);
attribute::set_deferred_persistence(current_y_attribute);
attribute_t *color_temp_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::ColorTemperatureMireds::Id);
attribute::set_deferred_persistence(color_temp_attribute);
cluster_t *color_control_cluster = cluster::get(endpoint, ColorControl::Id);
attribute_t *current_x_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::CurrentX::Id);
attribute::set_deferred_persistence(current_x_attribute);
attribute_t *current_y_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::CurrentY::Id); // codespell:ignore
attribute::set_deferred_persistence(current_y_attribute);
attribute_t *color_temp_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::ColorTemperatureMireds::Id);
attribute::set_deferred_persistence(color_temp_attribute);
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
/* Set OpenThread platform config */
esp_openthread_platform_config_t config = {
.radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
.host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
.port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
};
set_openthread_platform_config(&config);
/* Set OpenThread platform config */
esp_openthread_platform_config_t config = {
.radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
.host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
.port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
};
set_openthread_platform_config(&config);
#endif
/* Matter start */
err = esp_matter::start(app_event_cb);
if (err != ESP_OK) {
log_e("Failed to start Matter, err:%d", err);
abort();
}
/* Matter start */
err = esp_matter::start(app_event_cb);
if (err != ESP_OK) {
log_e("Failed to start Matter, err:%d", err);
abort();
}
#if CONFIG_ENABLE_ENCRYPTED_OTA
err = esp_matter_ota_requestor_encrypted_init(s_decryption_key, s_decryption_key_len);
if (err != ESP_OK) {
log_e("Failed to initialized the encrypted OTA, err: %d", err);
abort();
}
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
err = esp_matter_ota_requestor_encrypted_init(s_decryption_key, s_decryption_key_len);
if (err != ESP_OK) {
log_e("Failed to initialized the encrypted OTA, err: %d", err);
abort();
}
#endif // CONFIG_ENABLE_ENCRYPTED_OTA
#if CONFIG_ENABLE_CHIP_SHELL
esp_matter::console::diagnostics_register_commands();
esp_matter::console::wifi_register_commands();
esp_matter::console::diagnostics_register_commands();
esp_matter::console::wifi_register_commands();
#if CONFIG_OPENTHREAD_CLI
esp_matter::console::otcli_register_commands();
esp_matter::console::otcli_register_commands();
#endif
esp_matter::console::init();
esp_matter::console::init();
#endif
}
@@ -346,10 +309,10 @@ void loop() {
static bool button_state = false;
static bool started = false;
if(!isAccessoryCommissioned()) {
if (!isAccessoryCommissioned()) {
log_w("Accessory not commissioned yet. Waiting for commissioning.");
#ifdef RGB_BUILTIN
rgbLedWrite(RGB_BUILTIN, 48, 0, 20); // Purple indicates accessory not commissioned
rgbLedWrite(RGB_BUILTIN, 48, 0, 20); // Purple indicates accessory not commissioned
#endif
delay(5000);
return;
@@ -359,7 +322,7 @@ void loop() {
if (!isWifiConnected()) {
log_w("Wi-Fi not connected yet. Waiting for connection.");
#ifdef RGB_BUILTIN
rgbLedWrite(RGB_BUILTIN, 48, 20, 0); // Orange indicates accessory not connected to Wi-Fi
rgbLedWrite(RGB_BUILTIN, 48, 20, 0); // Orange indicates accessory not connected to Wi-Fi
#endif
delay(5000);
return;
@@ -370,7 +333,7 @@ void loop() {
if (!isThreadConnected()) {
log_w("Thread not connected yet. Waiting for connection.");
#ifdef RGB_BUILTIN
rgbLedWrite(RGB_BUILTIN, 0, 20, 48); // Blue indicates accessory not connected to Trhead
rgbLedWrite(RGB_BUILTIN, 0, 20, 48); // Blue indicates accessory not connected to Trhead
#endif
delay(5000);
return;
@@ -389,10 +352,10 @@ void loop() {
// Check if the button is pressed and toggle the light right away
if (digitalRead(button_gpio) == LOW && !button_state) {
// deals with button debounce
button_time_stamp = millis(); // record the time while the button is pressed.
button_state = true; // pressed.
button_time_stamp = millis(); // record the time while the button is pressed.
button_state = true; // pressed.
// Toggle button is pressed - toggle the light
// Toggle button is pressed - toggle the light
log_i("Toggle button pressed");
endpoint_t *endpoint = endpoint::get(node::get(), light_endpoint_id);
@@ -408,14 +371,14 @@ void loop() {
// Check if the button is released and handle the factory reset
uint32_t time_diff = millis() - button_time_stamp;
if (button_state && time_diff > 100 && digitalRead(button_gpio) == HIGH) {
button_state = false; // released. It can be pressed again after 100ms debounce.
button_state = false; // released. It can be pressed again after 100ms debounce.
// Factory reset is triggered if the button is pressed for more than 10 seconds
if (time_diff > 10000) {
log_i("Factory reset triggered. Light will retored to factory settings.");
esp_matter::factory_reset();
log_i("Factory reset triggered. Light will restored to factory settings.");
esp_matter::factory_reset();
}
}
delay(50); // WDT is happier with a delay
delay(50); // WDT is happier with a delay
}
@@ -17,10 +17,16 @@ platform = espressif32
framework = arduino, espidf
board_build.partitions = partitions.csv
monitor_speed = 115200
build_unflags =
-std=c++17
-std=gnu++2b
build_flags =
-std=gnu++2a
-Wno-missing-field-initializers
[env:esp32s3]
board = esp32-s3-devkitc-1
[env:esp32c6]
board = esp32-c6-devkitc-1
board_build.embed_txtfiles =
managed_components/espressif__esp_insights/server_certs/https_server.crt
managed_components/espressif__esp_rainmaker/server_certs/rmaker_mqtt_server.crt
@@ -1,3 +1,5 @@
CONFIG_IDF_TARGET="esp32c6"
# Arduino Settings
CONFIG_FREERTOS_HZ=1000
CONFIG_AUTOSTART_ARDUINO=y
@@ -6,9 +8,17 @@ CONFIG_AUTOSTART_ARDUINO=y
# Boot Messages - Log level
CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y
# Arduino Log Level
CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE=y
CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_INFO=y
# IDF Log Level
CONFIG_LOG_DEFAULT_LEVEL_WARN=y
CONFIG_LOG_DEFAULT_LEVEL_ERROR=y
# Default to 921600 baud when flashing and monitoring device
CONFIG_ESPTOOLPY_BAUD_921600B=y
CONFIG_ESPTOOLPY_BAUD=921600
CONFIG_ESPTOOLPY_COMPRESSED=y
CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y
CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
#enable BT
CONFIG_BT_ENABLED=y
@@ -17,6 +27,11 @@ CONFIG_BT_NIMBLE_ENABLED=y
#disable BT connection reattempt
CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=n
# NIMBLE
CONFIG_BT_NIMBLE_EXT_ADV=n
CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70
CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=y
#enable lwip ipv6 autoconfig
CONFIG_LWIP_IPV6_AUTOCONFIG=y
@@ -28,16 +43,17 @@ CONFIG_PARTITION_TABLE_OFFSET=0xC000
# Disable chip shell
CONFIG_ENABLE_CHIP_SHELL=n
# Enable OTA Requester
CONFIG_ENABLE_OTA_REQUESTOR=n
#enable lwIP route hooks
CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y
CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y
# Button
CONFIG_BUTTON_PERIOD_TIME_MS=20
CONFIG_BUTTON_LONG_PRESS_TIME_MS=5000
# disable softap by default
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n
CONFIG_ENABLE_WIFI_STATION=y
CONFIG_ENABLE_WIFI_AP=n
# Disable DS Peripheral
CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL=n
@@ -52,15 +68,23 @@ CONFIG_MBEDTLS_HKDF_C=y
# unique local addresses for fabrics(MAX_FABRIC), a link local address(1)
CONFIG_LWIP_IPV6_NUM_ADDRESSES=6
# libsodium
CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y
# ESP32-S3-DevKitC-1 Settings
# FreeRTOS should use legacy API
CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y
# Use minimal mDNS
CONFIG_USE_MINIMAL_MDNS=y
CONFIG_ENABLE_EXTENDED_DISCOVERY=y
# ESP32-C6-DevKit Settings
# Buttons
CONFIG_BSP_BUTTONS_NUM=1
CONFIG_BSP_BUTTON_1_TYPE_GPIO=y
CONFIG_BSP_BUTTON_1_GPIO=0
CONFIG_BSP_BUTTON_1_LEVEL=0
CONFIG_BUTTON_PIN=9
# LEDs
CONFIG_BSP_LEDS_NUM=1
CONFIG_BSP_LED_TYPE_RGB=y
CONFIG_BSP_LED_RGB_GPIO=48
CONFIG_BSP_LED_RGB_BACKEND_RMT=y
CONFIG_WS2812_PIN=8
# max GPIO
CONFIG_ENV_GPIO_RANGE_MIN=0
CONFIG_ENV_GPIO_RANGE_MAX=30
CONFIG_ENV_GPIO_IN_RANGE_MAX=30
CONFIG_ENV_GPIO_OUT_RANGE_MAX=30
@@ -56,7 +56,7 @@ CONFIG_LWIP_MULTICAST_PING=y
CONFIG_USE_MINIMAL_MDNS=n
CONFIG_ENABLE_EXTENDED_DISCOVERY=y
# Enable OTA Requestor
# Enable OTA Requester
CONFIG_ENABLE_OTA_REQUESTOR=n
# Disable STA and AP for ESP32C6
@@ -77,4 +77,3 @@ CONFIG_MRP_MAX_RETRANS=3
# Enable HKDF in mbedtls
CONFIG_MBEDTLS_HKDF_C=y
@@ -1,64 +0,0 @@
CONFIG_IDF_TARGET="esp32c3"
# Arduino Settings
CONFIG_FREERTOS_HZ=1000
CONFIG_AUTOSTART_ARDUINO=y
# Log Levels
# Boot Messages - Log level
CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y
# Arduino Log Level
CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_INFO=y
# IDF Log Level
CONFIG_LOG_DEFAULT_LEVEL_ERROR=y
# Default to 921600 baud when flashing and monitoring device
CONFIG_ESPTOOLPY_BAUD_921600B=y
CONFIG_ESPTOOLPY_BAUD=921600
CONFIG_ESPTOOLPY_COMPRESSED=y
CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y
CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
#enable BT
CONFIG_BT_ENABLED=y
CONFIG_BT_NIMBLE_ENABLED=y
#disable BT connection reattempt
CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=n
#enable lwip ipv6 autoconfig
CONFIG_LWIP_IPV6_AUTOCONFIG=y
# Use a custom partition table
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_OFFSET=0xC000
# Disable chip shell
CONFIG_ENABLE_CHIP_SHELL=n
# Enable OTA Requestor
CONFIG_ENABLE_OTA_REQUESTOR=n
#enable lwIP route hooks
CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y
CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y
# disable softap by default
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n
CONFIG_ENABLE_WIFI_STATION=y
CONFIG_ENABLE_WIFI_AP=n
# Disable DS Peripheral
CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL=n
# Use compact attribute storage mode
CONFIG_ESP_MATTER_NVS_USE_COMPACT_ATTR_STORAGE=y
# Enable HKDF in mbedtls
CONFIG_MBEDTLS_HKDF_C=y
# Increase LwIP IPv6 address number to 6 (MAX_FABRIC + 1)
# unique local addresses for fabrics(MAX_FABRIC), a link local address(1)
CONFIG_LWIP_IPV6_NUM_ADDRESSES=6
@@ -1,68 +1,5 @@
CONFIG_IDF_TARGET="esp32c6"
# Arduino Settings
CONFIG_FREERTOS_HZ=1000
CONFIG_AUTOSTART_ARDUINO=y
# Log Levels
# Boot Messages - Log level
CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y
# Arduino Log Level
CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_INFO=y
# IDF Log Level
CONFIG_LOG_DEFAULT_LEVEL_ERROR=y
# Default to 921600 baud when flashing and monitoring device
CONFIG_ESPTOOLPY_BAUD_921600B=y
CONFIG_ESPTOOLPY_BAUD=921600
CONFIG_ESPTOOLPY_COMPRESSED=y
CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y
CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
#enable BT
CONFIG_BT_ENABLED=y
CONFIG_BT_NIMBLE_ENABLED=y
#disable BT connection reattempt
CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=n
#enable lwip ipv6 autoconfig
CONFIG_LWIP_IPV6_AUTOCONFIG=y
# Use a custom partition table
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_OFFSET=0xC000
# Disable chip shell
CONFIG_ENABLE_CHIP_SHELL=n
# Enable OTA Requestor
CONFIG_ENABLE_OTA_REQUESTOR=n
#enable lwIP route hooks
CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y
CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y
# disable softap by default
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n
CONFIG_ENABLE_WIFI_STATION=y
CONFIG_ENABLE_WIFI_AP=n
# Disable DS Peripheral
CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL=n
# Use compact attribute storage mode
CONFIG_ESP_MATTER_NVS_USE_COMPACT_ATTR_STORAGE=y
# Enable HKDF in mbedtls
CONFIG_MBEDTLS_HKDF_C=y
# Increase LwIP IPv6 address number to 6 (MAX_FABRIC + 1)
# unique local addresses for fabrics(MAX_FABRIC), a link local address(1)
CONFIG_LWIP_IPV6_NUM_ADDRESSES=6
# libsodium
CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y
@@ -1,64 +0,0 @@
CONFIG_IDF_TARGET="esp32s3"
# Arduino Settings
CONFIG_FREERTOS_HZ=1000
CONFIG_AUTOSTART_ARDUINO=y
# Log Levels
# Boot Messages - Log level
CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y
# Arduino Log Level
CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_INFO=y
# IDF Log Level
CONFIG_LOG_DEFAULT_LEVEL_ERROR=y
# Default to 921600 baud when flashing and monitoring device
CONFIG_ESPTOOLPY_BAUD_921600B=y
CONFIG_ESPTOOLPY_BAUD=921600
CONFIG_ESPTOOLPY_COMPRESSED=y
CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y
CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
#enable BT
CONFIG_BT_ENABLED=y
CONFIG_BT_NIMBLE_ENABLED=y
#disable BT connection reattempt
CONFIG_BT_NIMBLE_ENABLE_CONN_REATTEMPT=n
#enable lwip ipv6 autoconfig
CONFIG_LWIP_IPV6_AUTOCONFIG=y
# Use a custom partition table
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_OFFSET=0xC000
# Disable chip shell
CONFIG_ENABLE_CHIP_SHELL=n
# Enable OTA Requestor
CONFIG_ENABLE_OTA_REQUESTOR=n
#enable lwIP route hooks
CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT=y
CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT=y
# disable softap by default
CONFIG_ESP_WIFI_SOFTAP_SUPPORT=n
CONFIG_ENABLE_WIFI_STATION=y
CONFIG_ENABLE_WIFI_AP=n
# Disable DS Peripheral
CONFIG_ESP_SECURE_CERT_DS_PERIPHERAL=n
# Use compact attribute storage mode
CONFIG_ESP_MATTER_NVS_USE_COMPACT_ATTR_STORAGE=y
# Enable HKDF in mbedtls
CONFIG_MBEDTLS_HKDF_C=y
# Increase LwIP IPv6 address number to 6 (MAX_FABRIC + 1)
# unique local addresses for fabrics(MAX_FABRIC), a link local address(1)
CONFIG_LWIP_IPV6_NUM_ADDRESSES=6
+10 -1
View File
@@ -13,7 +13,6 @@ framework = espidf
board = esp32dev
monitor_speed = 115200
build_flags =
; https://docs.espressif.com/projects/esp-idf/en/latest/get-started/get-started-wrover-kit.html#rgb-led
-D CONFIG_BLINK_GPIO=2
-D CONFIG_BLINK_LED_GPIO=2
-D CONFIG_BLINK_PERIOD=1000
@@ -37,3 +36,13 @@ build_flags =
-D CONFIG_BLINK_GPIO=2
-D CONFIG_BLINK_LED_GPIO=2
-D CONFIG_BLINK_PERIOD=1000
[env:esp32-p4]
platform = espressif32
framework = espidf
board = esp32-p4
monitor_speed = 115200
build_flags =
-D CONFIG_BLINK_GPIO=2
-D CONFIG_BLINK_LED_GPIO=2
-D CONFIG_BLINK_PERIOD=1000
@@ -18,3 +18,6 @@ board_build.embed_txtfiles =
[env:esp-wrover-kit]
board = esp-wrover-kit
build_flags =
-D SHOW_METRICS
+5 -3
View File
@@ -29,13 +29,15 @@ lib_ignore = ${env:tasmota32_base.lib_ignore}
NetworkClientSecure
Zigbee
custom_sdkconfig = https://raw.githubusercontent.com/pioarduino/sdkconfig/refs/heads/main/sdkconfig_tasmota_esp32
'# CONFIG_BT_ENABLED is not set'
'# CONFIG_BT_NIMBLE_ENABLED is not set'
'# CONFIG_BT_CONTROLLER_ENABLED is not set'
CONFIG_BT_CONTROLLER_DISABLED=y
'# CONFIG_ETH_USE_ESP32_EMAC is not set'
'# CONFIG_ETH_PHY_INTERFACE_RMII is not set'
'# CONFIG_ETH_RMII_CLK_INPUT is not set'
'# CONFIG_ETH_RMII_CLK_IN_GPIO is not set'
custom_component_remove = espressif/esp_hosted
espressif/esp_wifi_remote
espressif/esp-dsp
custom_component_remove =
espressif/network_provisioning
espressif/esp-zboss-lib
espressif/esp-zigbee-lib
+45572
View File
File diff suppressed because it is too large Load Diff
+21108
View File
File diff suppressed because it is too large Load Diff
+34807
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+70756
View File
File diff suppressed because it is too large Load Diff
+53987
View File
File diff suppressed because it is too large Load Diff
+133701
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+46655
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+69011
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -32,7 +32,7 @@ IS_WINDOWS = sys.platform.startswith("win")
class Esp32ExceptionDecoder(DeviceMonitorFilterBase):
NAME = "esp32_exception_decoder"
ADDR_PATTERN = re.compile(r"((?:0x[0-9a-fA-F]{8}[: ]?)+)\s?$")
ADDR_PATTERN = re.compile(r"((?:0x[0-9a-fA-F]{8}[: ]?)+)")
ADDR_SPLIT = re.compile(r"[ :]")
PREFIX_RE = re.compile(r"^ *")
+89 -40
View File
@@ -6,19 +6,19 @@
"license": "Apache-2.0",
"keywords": [
"dev-platform",
"Wi-Fi",
"WiFi",
"Bluetooth",
"Xtensa",
"RISC-V"
],
"engines": {
"platformio": ">=6.1.16"
"platformio": ">=6.1.18"
},
"repository": {
"type": "git",
"url": "https://github.com/pioarduino/platform-espressif32.git"
},
"version": "54.03.20",
"version": "54.03.21",
"frameworks": {
"arduino": {
"script": "builder/frameworks/arduino.py"
@@ -33,114 +33,163 @@
"type": "framework",
"optional": true,
"owner": "espressif",
"version": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esp32-3.2.0.zip"
"version": "https://github.com/espressif/arduino-esp32/releases/download/3.2.1/esp32-3.2.1.zip"
},
"framework-arduinoespressif32-libs": {
"type": "framework",
"optional": true,
"owner": "espressif",
"version": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-2f7dcd86-v1.zip"
"version": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.4/esp32-arduino-libs-idf-release_v5.4-858a988d-v1.zip"
},
"framework-arduino-c2-skeleton-lib": {
"type": "framework",
"optional": true,
"owner": "espressif",
"version": "https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/c2_arduino_compile_skeleton.zip"
"version": "https://github.com/pioarduino/platform-espressif32/releases/download/54.03.20/c2_arduino_compile_skeleton.zip"
},
"framework-espidf": {
"type": "framework",
"optional": true,
"owner": "pioarduino",
"version": "https://github.com/pioarduino/esp-idf/releases/download/v5.4.1/esp-idf-v5.4.1.zip"
"version": "https://github.com/pioarduino/esp-idf/releases/download/v5.4.2/esp-idf-v5.4.2.zip"
},
"toolchain-xtensa-esp-elf": {
"type": "toolchain",
"optional": true,
"owner": "platformio",
"version": "14.2.0+20241119"
"owner": "pioarduino",
"package-version": "14.2.0+20241119",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/xtensa-esp-elf-14.2.0_20241119.zip"
},
"toolchain-riscv32-esp": {
"type": "toolchain",
"optional": true,
"owner": "platformio",
"version": "14.2.0+20241119"
"owner": "pioarduino",
"package-version": "14.2.0+20241119",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/riscv32-esp-elf-14.2.0_20241119.zip"
},
"toolchain-esp32ulp": {
"type": "toolchain",
"optional": true,
"owner": "platformio",
"version": "~1.23800.0"
"owner": "pioarduino",
"package-version": "2.38.0+20240113",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/esp32ulp-elf-2.38_20240113.zip"
},
"tool-xtensa-esp-elf-gdb": {
"tool-xtensa-esp-elf-gdb": {
"type": "debugger",
"optional": true,
"owner": "platformio",
"version": "14.2.0+20240403"
"owner": "pioarduino",
"package-version": "16.2.0+20250324",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/xtensa-esp-gdb-v16.2_20250324.zip"
},
"tool-riscv32-esp-elf-gdb": {
"type": "debugger",
"optional": true,
"owner": "platformio",
"version": "14.2.0+20240403"
"owner": "pioarduino",
"package-version": "16.2.0+20250324",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/riscv32-esp-gdb-v16.2_20250324.zip"
},
"tool-esptoolpy": {
"type": "uploader",
"optional": false,
"owner": "pioarduino",
"version": "https://github.com/pioarduino/esptool/releases/download/v4.8.9/esptool.zip"
"version": "https://github.com/pioarduino/esptool/releases/download/v5.0.0-dev1/esptool.zip"
},
"tl-install": {
"type": "tool",
"optional": false,
"owner": "pioarduino",
"version": "https://github.com/pioarduino/esp_install/releases/download/v5.0.0/esp_install-v5.0.0.zip"
},
"tool-dfuutil-arduino": {
"type": "uploader",
"optional": true,
"owner": "platformio",
"version": "~1.11.0"
"owner": "pioarduino",
"package-version": "1.11.0",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/dfuutil-arduino-v1.11.0.zip"
},
"tool-openocd-esp32": {
"type": "debugger",
"optional": true,
"owner": "platformio",
"version": "~2.1100.0"
"owner": "pioarduino",
"package-version": "2.1200.20250422",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/openocd-v0.12.0-esp32-20250422.zip"
},
"tool-mklittlefs": {
"type": "uploader",
"owner": "tasmota",
"version": "^3.2.0"
"optional": true,
"owner": "pioarduino",
"package-version": "3.2.0",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/mklittlefs-3.2.0.zip"
},
"tool-mkfatfs": {
"tool-mklittlefs-4.0.0": {
"type": "uploader",
"optional": true,
"owner": "platformio",
"version": "~2.0.0"
"owner": "pioarduino",
"package-version": "4.0.0",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/mklittlefs-4.0.0.zip"
},
"tool-mkspiffs": {
"type": "uploader",
"optional": true,
"owner": "platformio",
"version": "~2.230.0"
"owner": "pioarduino",
"package-version": "2.230.0",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/mkspiffs-v2.230.0.zip"
},
"tool-mkfatfs": {
"type": "uploader",
"optional": true,
"owner": "pioarduino",
"package-version": "2.0.1",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/mkfatfs-v2.0.1.zip"
},
"tool-cppcheck": {
"type": "tool",
"optional": true,
"owner": "platformio",
"version": "~1.21100"
"owner": "pioarduino",
"package-version": "2.11.0+230717",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/cppcheck-v2.11.0-230717.zip"
},
"tool-clangtidy": {
"type": "tool",
"optional": true,
"owner": "platformio",
"version": "^1.190100.0"
"owner": "pioarduino",
"package-version": "18.1.1",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/clangtidy-v18.1.1.zip"
},
"tool-pvs-studio": {
"type": "tool",
"optional": true,
"owner": "platformio",
"version": "^7.18.0"
"owner": "pioarduino",
"package-version": "7.36.91321",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/pvs-studio-v7.36.91321.zip"
},
"tool-cmake": {
"type": "tool",
"optional": true,
"owner": "platformio",
"version": "~3.30.2"
"owner": "pioarduino",
"package-version": "3.30.2",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/cmake-3.30.2.zip"
},
"tool-esp-rom-elfs": {
"type": "tool",
"optional": true,
"owner": "pioarduino",
"package-version": "2024.10.11",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/esp-rom-elfs-20241011.zip"
},
"tool-ninja": {
"type": "tool",
"optional": true,
"owner": "platformio",
"version": "^1.7.0"
"owner": "pioarduino",
"package-version": "1.10.2",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/ninja-1.10.2.zip"
},
"tool-scons": {
"type": "tool",
"optional": true,
"owner": "pioarduino",
"package-version": "4.40801.0",
"version": "https://github.com/pioarduino/registry/releases/download/0.0.1/scons-4.8.1.zip"
}
}
}
+499 -111
View File
@@ -13,109 +13,490 @@
# limitations under the License.
import os
import urllib
import sys
import contextlib
import json
import re
import requests
import subprocess
import sys
import shutil
import logging
from typing import Optional, Dict, List, Any
from platformio.public import PlatformBase, to_unix_path
from platformio.proc import get_pythonexe_path
from platformio.project.config import ProjectConfig
from platformio.package.manager.tool import ToolPackageManager
# Constants
RETRY_LIMIT = 3
SUBPROCESS_TIMEOUT = 300
MKLITTLEFS_VERSION_320 = "3.2.0"
MKLITTLEFS_VERSION_400 = "4.0.0"
DEFAULT_DEBUG_SPEED = "5000"
DEFAULT_APP_OFFSET = "0x10000"
# MCUs that support ESP-builtin debug
ESP_BUILTIN_DEBUG_MCUS = frozenset([
"esp32c3", "esp32c5", "esp32c6", "esp32s3", "esp32h2", "esp32p4"
])
# MCU configuration mapping
MCU_TOOLCHAIN_CONFIG = {
"xtensa": {
"mcus": frozenset(["esp32", "esp32s2", "esp32s3"]),
"toolchains": ["toolchain-xtensa-esp-elf"],
"debug_tools": ["tool-xtensa-esp-elf-gdb"]
},
"riscv": {
"mcus": frozenset([
"esp32c2", "esp32c3", "esp32c5", "esp32c6", "esp32h2", "esp32p4"
]),
"toolchains": ["toolchain-riscv32-esp"],
"debug_tools": ["tool-riscv32-esp-elf-gdb"]
}
}
COMMON_IDF_PACKAGES = [
"tool-cmake",
"tool-ninja",
"tool-scons",
"tool-esp-rom-elfs"
]
CHECK_PACKAGES = [
"tool-cppcheck",
"tool-clangtidy",
"tool-pvs-studio"
]
# System-specific configuration
IS_WINDOWS = sys.platform.startswith("win")
# Set Platformio env var to use windows_amd64 for all windows architectures
# only windows_amd64 native espressif toolchains are available
# needs platformio core >= 6.1.16b2 or pioarduino core 6.1.16+test
# needs platformio/pioarduino core >= 6.1.17
if IS_WINDOWS:
os.environ["PLATFORMIO_SYSTEM_TYPE"] = "windows_amd64"
# Global variables
python_exe = get_pythonexe_path()
pm = ToolPackageManager()
# Configure logger
logger = logging.getLogger(__name__)
def safe_file_operation(operation_func):
"""Decorator for safe filesystem operations with error handling."""
def wrapper(*args, **kwargs):
try:
return operation_func(*args, **kwargs)
except (OSError, IOError, FileNotFoundError) as e:
logger.error(f"Filesystem error in {operation_func.__name__}: {e}")
return False
except Exception as e:
logger.error(f"Unexpected error in {operation_func.__name__}: {e}")
raise # Re-raise unexpected exceptions
return wrapper
@safe_file_operation
def safe_remove_directory(path: str) -> bool:
"""Safely remove directories with error handling."""
if os.path.exists(path) and os.path.isdir(path):
shutil.rmtree(path)
logger.debug(f"Directory removed: {path}")
return True
@safe_file_operation
def safe_copy_file(src: str, dst: str) -> bool:
"""Safely copy files with error handling."""
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.copyfile(src, dst)
logger.debug(f"File copied: {src} -> {dst}")
return True
class Espressif32Platform(PlatformBase):
def configure_default_packages(self, variables, targets):
if not variables.get("board"):
return super().configure_default_packages(variables, targets)
"""ESP32 platform implementation for PlatformIO with optimized toolchain management."""
board_config = self.board_config(variables.get("board"))
mcu = variables.get("board_build.mcu", board_config.get("build.mcu", "esp32"))
board_sdkconfig = variables.get("board_espidf.custom_sdkconfig", board_config.get("espidf.custom_sdkconfig", ""))
core_variant_board = ''.join(variables.get("board_build.extra_flags", board_config.get("build.extra_flags", "")))
core_variant_board = core_variant_board.replace("-D", " ")
core_variant_build = (''.join(variables.get("build_flags", []))).replace("-D", " ")
frameworks = variables.get("pioframework", [])
def __init__(self, *args, **kwargs):
"""Initialize the ESP32 platform with caching mechanisms."""
super().__init__(*args, **kwargs)
self._packages_dir = None
self._tools_cache = {}
self._mcu_config_cache = {}
if "arduino" in frameworks:
self.packages["framework-arduinoespressif32"]["optional"] = False
self.packages["framework-arduinoespressif32-libs"]["optional"] = False
@property
def packages_dir(self) -> str:
"""Get cached packages directory path."""
if self._packages_dir is None:
config = ProjectConfig.get_instance()
self._packages_dir = config.get("platformio", "packages_dir")
return self._packages_dir
if variables.get("custom_sdkconfig") is not None or len(str(board_sdkconfig)) > 3:
def _get_tool_paths(self, tool_name: str) -> Dict[str, str]:
"""Get centralized path calculation for tools with caching."""
if tool_name not in self._tools_cache:
tool_path = os.path.join(self.packages_dir, tool_name)
self._tools_cache[tool_name] = {
'tool_path': tool_path,
'package_path': os.path.join(tool_path, "package.json"),
'tools_json_path': os.path.join(tool_path, "tools.json"),
'piopm_path': os.path.join(tool_path, ".piopm"),
'idf_tools_path': os.path.join(
self.packages_dir, "tl-install", "tools", "idf_tools.py"
)
}
return self._tools_cache[tool_name]
def _check_tool_status(self, tool_name: str) -> Dict[str, bool]:
"""Check the installation status of a tool."""
paths = self._get_tool_paths(tool_name)
return {
'has_idf_tools': os.path.exists(paths['idf_tools_path']),
'has_tools_json': os.path.exists(paths['tools_json_path']),
'has_piopm': os.path.exists(paths['piopm_path']),
'tool_exists': os.path.exists(paths['tool_path'])
}
def _run_idf_tools_install(self, tools_json_path: str, idf_tools_path: str) -> bool:
"""Execute idf_tools.py install command with timeout and error handling."""
cmd = [
python_exe,
idf_tools_path,
"--quiet",
"--non-interactive",
"--tools-json",
tools_json_path,
"install"
]
try:
result = subprocess.run(
cmd,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
timeout=SUBPROCESS_TIMEOUT,
check=False
)
if result.returncode != 0:
logger.error("idf_tools.py installation failed")
return False
logger.debug("idf_tools.py executed successfully")
return True
except subprocess.TimeoutExpired:
logger.error(f"Timeout in idf_tools.py after {SUBPROCESS_TIMEOUT}s")
return False
except (subprocess.SubprocessError, OSError) as e:
logger.error(f"Error in idf_tools.py: {e}")
return False
def _check_tool_version(self, tool_name: str) -> bool:
"""Check if the installed tool version matches the required version."""
paths = self._get_tool_paths(tool_name)
try:
with open(paths['package_path'], 'r', encoding='utf-8') as f:
package_data = json.load(f)
required_version = self.packages.get(tool_name, {}).get("package-version")
installed_version = package_data.get("version")
if not required_version:
logger.debug(f"No version check required for {tool_name}")
return True
if not installed_version:
logger.warning(f"Installed version for {tool_name} unknown")
return False
version_match = required_version == installed_version
if not version_match:
logger.info(
f"Version mismatch for {tool_name}: "
f"{installed_version} != {required_version}"
)
return version_match
except (json.JSONDecodeError, FileNotFoundError) as e:
logger.error(f"Error reading package data for {tool_name}: {e}")
return False
def install_tool(self, tool_name: str, retry_count: int = 0) -> bool:
"""Install a tool with optimized retry mechanism."""
if retry_count >= RETRY_LIMIT:
logger.error(
f"Installation of {tool_name} failed after {RETRY_LIMIT} attempts"
)
return False
self.packages[tool_name]["optional"] = False
paths = self._get_tool_paths(tool_name)
status = self._check_tool_status(tool_name)
# Case 1: New installation with idf_tools
if status['has_idf_tools'] and status['has_tools_json']:
return self._install_with_idf_tools(tool_name, paths)
# Case 2: Tool already installed, version check
if (status['has_idf_tools'] and status['has_piopm'] and
not status['has_tools_json']):
return self._handle_existing_tool(tool_name, paths, retry_count)
logger.debug(f"Tool {tool_name} already configured")
return True
def _install_with_idf_tools(self, tool_name: str, paths: Dict[str, str]) -> bool:
"""Install tool using idf_tools.py installation method."""
if not self._run_idf_tools_install(
paths['tools_json_path'], paths['idf_tools_path']
):
return False
# Copy tool files
tools_path_default = os.path.join(
os.path.expanduser("~"), ".platformio"
)
target_package_path = os.path.join(
tools_path_default, "tools", tool_name, "package.json"
)
if not safe_copy_file(paths['package_path'], target_package_path):
return False
safe_remove_directory(paths['tool_path'])
tl_path = f"file://{os.path.join(tools_path_default, 'tools', tool_name)}"
pm.install(tl_path)
logger.info(f"Tool {tool_name} successfully installed")
return True
def _handle_existing_tool(
self, tool_name: str, paths: Dict[str, str], retry_count: int
) -> bool:
"""Handle already installed tools with version checking."""
if self._check_tool_version(tool_name):
# Version matches, use tool
self.packages[tool_name]["version"] = paths['tool_path']
self.packages[tool_name]["optional"] = False
logger.debug(f"Tool {tool_name} found with correct version")
return True
# Wrong version, reinstall
logger.info(f"Reinstalling {tool_name} due to version mismatch")
safe_remove_directory(paths['tool_path'])
return self.install_tool(tool_name, retry_count + 1)
def _configure_arduino_framework(self, frameworks: List[str]) -> None:
"""Configure Arduino framework with dynamic library URL fetching."""
if "arduino" not in frameworks:
return
self.packages["framework-arduinoespressif32"]["optional"] = False
self.packages["framework-arduinoespressif32-libs"]["optional"] = False
def _configure_espidf_framework(
self, frameworks: List[str], variables: Dict, board_config: Dict, mcu: str
) -> None:
"""Configure ESP-IDF framework based on custom sdkconfig settings."""
custom_sdkconfig = variables.get("custom_sdkconfig")
board_sdkconfig = variables.get(
"board_espidf.custom_sdkconfig",
board_config.get("espidf.custom_sdkconfig", "")
)
if custom_sdkconfig is not None or len(str(board_sdkconfig)) > 3:
frameworks.append("espidf")
self.packages["framework-espidf"]["optional"] = False
if mcu == "esp32c2":
self.packages["framework-arduino-c2-skeleton-lib"]["optional"] = False
# Enable check tools only when "check_tool" is active
for p in self.packages:
if p in ("tool-cppcheck", "tool-clangtidy", "tool-pvs-studio"):
self.packages[p]["optional"] = False if str(variables.get("check_tool")).strip("['']") in p else True
def _get_mcu_config(self, mcu: str) -> Optional[Dict]:
"""Get MCU configuration with optimized caching and search."""
if mcu in self._mcu_config_cache:
return self._mcu_config_cache[mcu]
if "buildfs" in targets:
filesystem = variables.get("board_build.filesystem", "littlefs")
if filesystem == "littlefs":
self.packages["tool-mklittlefs"]["optional"] = False
elif filesystem == "fatfs":
self.packages["tool-mkfatfs"]["optional"] = False
else:
self.packages["tool-mkspiffs"]["optional"] = False
if variables.get("upload_protocol"):
self.packages["tool-openocd-esp32"]["optional"] = False
if os.path.isdir("ulp"):
self.packages["toolchain-esp32ulp"]["optional"] = False
for _, config in MCU_TOOLCHAIN_CONFIG.items():
if mcu in config["mcus"]:
# Dynamically add ULP toolchain
result = config.copy()
result["ulp_toolchain"] = ["toolchain-esp32ulp"]
if mcu != "esp32":
result["ulp_toolchain"].append("toolchain-riscv32-esp")
self._mcu_config_cache[mcu] = result
return result
return None
if "downloadfs" in targets:
filesystem = variables.get("board_build.filesystem", "littlefs")
if filesystem == "littlefs":
# Use Tasmota mklittlefs v4.0.0 to unpack, older version is incompatible
self.packages["tool-mklittlefs"]["version"] = "~4.0.0"
def _needs_debug_tools(self, variables: Dict, targets: List[str]) -> bool:
"""Check if debug tools are needed based on build configuration."""
return bool(
variables.get("build_type") or
"debug" in targets or
variables.get("upload_protocol")
)
def _configure_mcu_toolchains(
self, mcu: str, variables: Dict, targets: List[str]
) -> None:
"""Configure MCU-specific toolchains with optimized installation."""
mcu_config = self._get_mcu_config(mcu)
if not mcu_config:
logger.warning(f"Unknown MCU: {mcu}")
return
# Install base toolchains
for toolchain in mcu_config["toolchains"]:
self.install_tool(toolchain)
# ULP toolchain if ULP directory exists
if mcu_config.get("ulp_toolchain") and os.path.isdir("ulp"):
for toolchain in mcu_config["ulp_toolchain"]:
self.install_tool(toolchain)
# Debug tools when needed
if self._needs_debug_tools(variables, targets):
for debug_tool in mcu_config["debug_tools"]:
self.install_tool(debug_tool)
self.install_tool("tool-openocd-esp32")
def _configure_installer(self) -> None:
"""Configure the ESP-IDF tools installer."""
installer_path = os.path.join(
self.packages_dir, "tl-install", "tools", "idf_tools.py"
)
if os.path.exists(installer_path):
self.packages["tl-install"]["optional"] = True
def _install_common_idf_packages(self) -> None:
"""Install common ESP-IDF packages required for all builds."""
for package in COMMON_IDF_PACKAGES:
self.install_tool(package)
def _configure_check_tools(self, variables: Dict) -> None:
"""Configure static analysis and check tools based on configuration."""
check_tools = variables.get("check_tool", [])
if not check_tools:
return
for package in CHECK_PACKAGES:
if any(tool in package for tool in check_tools):
self.install_tool(package)
def _ensure_mklittlefs_version(self) -> None:
"""Ensure correct mklittlefs version is installed."""
piopm_path = os.path.join(self.packages_dir, "tool-mklittlefs", ".piopm")
if os.path.exists(piopm_path):
try:
with open(piopm_path, 'r', encoding='utf-8') as f:
package_data = json.load(f)
if package_data.get('version') != MKLITTLEFS_VERSION_320:
os.remove(piopm_path)
logger.info("Outdated mklittlefs version removed")
except (json.JSONDecodeError, KeyError) as e:
logger.error(f"Error reading mklittlefs package data: {e}")
def _setup_mklittlefs_for_download(self) -> None:
"""Setup mklittlefs for download functionality with version 4.0.0."""
mklittlefs_dir = os.path.join(self.packages_dir, "tool-mklittlefs")
mklittlefs400_dir = os.path.join(
self.packages_dir, "tool-mklittlefs-4.0.0"
)
# Ensure mklittlefs 3.2.0 is installed
if not os.path.exists(mklittlefs_dir):
self.install_tool("tool-mklittlefs")
if os.path.exists(os.path.join(mklittlefs_dir, "tools.json")):
self.install_tool("tool-mklittlefs")
# Install mklittlefs 4.0.0
if not os.path.exists(mklittlefs400_dir):
self.install_tool("tool-mklittlefs-4.0.0")
if os.path.exists(os.path.join(mklittlefs400_dir, "tools.json")):
self.install_tool("tool-mklittlefs-4.0.0")
# Copy mklittlefs 4.0.0 over 3.2.0
if os.path.exists(mklittlefs400_dir):
package_src = os.path.join(mklittlefs_dir, "package.json")
package_dst = os.path.join(mklittlefs400_dir, "package.json")
safe_copy_file(package_src, package_dst)
shutil.copytree(mklittlefs400_dir, mklittlefs_dir, dirs_exist_ok=True)
self.packages.pop("tool-mkfatfs", None)
def _handle_littlefs_tool(self, for_download: bool) -> None:
"""Handle LittleFS tool installation with special download configuration."""
if for_download:
self._setup_mklittlefs_for_download()
else:
self._ensure_mklittlefs_version()
self.install_tool("tool-mklittlefs")
def _install_filesystem_tool(self, filesystem: str, for_download: bool = False) -> None:
"""Install filesystem-specific tools based on the filesystem type."""
tool_mapping = {
"default": lambda: self._handle_littlefs_tool(for_download),
"fatfs": lambda: self.install_tool("tool-mkfatfs"),
"spiffs": lambda: self.install_tool("tool-mkspiffs")
}
handler = tool_mapping.get(filesystem, tool_mapping["default"])
handler()
def _handle_dfuutil_tool(self, variables: Dict, for_download: bool = False) -> None:
"""Install dfuutil tool for Arduino Nano ESP32 board."""
# Currently only Arduino Nano ESP32 uses the dfuutil tool as uploader
if variables.get("board") == "arduino_nano_esp32":
self.packages["tool-dfuutil-arduino"]["optional"] = False
else:
del self.packages["tool-dfuutil-arduino"]
self.install_tool("tool-dfuutil-arduino")
# Starting from v12, Espressif's toolchains are shipped without
# bundled GDB. Instead, it's distributed as separate packages for Xtensa
# and RISC-V targets.
for gdb_package in ("tool-xtensa-esp-elf-gdb", "tool-riscv32-esp-elf-gdb"):
self.packages[gdb_package]["optional"] = False
# if IS_WINDOWS:
# Note: On Windows GDB v12 is not able to
# launch a GDB server in pipe mode while v11 works fine
# self.packages[gdb_package]["version"] = "~11.2.0"
def _configure_filesystem_tools(self, variables: Dict, targets: List[str]) -> None:
"""Configure filesystem tools based on build targets and filesystem type."""
filesystem = variables.get("board_build.filesystem", "littlefs")
# Common packages for IDF and mixed Arduino+IDF projects
if "espidf" in frameworks:
self.packages["toolchain-esp32ulp"]["optional"] = False
for p in self.packages:
if p in ("tool-scons", "tool-cmake", "tool-ninja"):
self.packages[p]["optional"] = False
# elif p in ("tool-mconf", "tool-idf") and IS_WINDOWS:
# self.packages[p]["optional"] = False
if any(target in targets for target in ["buildfs", "uploadfs"]):
self._install_filesystem_tool(filesystem, for_download=False)
if mcu in ("esp32", "esp32s2", "esp32s3"):
self.packages["toolchain-xtensa-esp-elf"]["optional"] = False
else:
self.packages.pop("toolchain-xtensa-esp-elf", None)
if "downloadfs" in targets:
self._install_filesystem_tool(filesystem, for_download=True)
if mcu in ("esp32s2", "esp32s3", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4"):
if mcu in ("esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32p4"):
self.packages.pop("toolchain-esp32ulp", None)
# RISC-V based toolchain for ESP32C3, ESP32C6 ESP32S2, ESP32S3 ULP
self.packages["toolchain-riscv32-esp"]["optional"] = False
def configure_default_packages(self, variables: Dict, targets: List[str]) -> Any:
"""Main configuration method with optimized package management."""
if not variables.get("board"):
return super().configure_default_packages(variables, targets)
# Base configuration
board_config = self.board_config(variables.get("board"))
mcu = variables.get("board_build.mcu", board_config.get("build.mcu", "esp32"))
frameworks = list(variables.get("pioframework", [])) # Create copy
try:
# Configuration steps
self._configure_installer()
self._configure_arduino_framework(frameworks)
self._configure_espidf_framework(frameworks, variables, board_config, mcu)
self._configure_mcu_toolchains(mcu, variables, targets)
if "espidf" in frameworks:
self._install_common_idf_packages()
self._configure_check_tools(variables)
self._configure_filesystem_tools(variables, targets)
self._handle_dfuutil_tool(variables)
logger.info("Package configuration completed successfully")
except Exception as e:
logger.error(f"Error in package configuration: {type(e).__name__}: {e}")
# Don't re-raise to maintain compatibility
return super().configure_default_packages(variables, targets)
def get_boards(self, id_=None):
"""Get board configuration with dynamic options."""
result = super().get_boards(id_)
if not result:
return result
@@ -127,13 +508,14 @@ class Espressif32Platform(PlatformBase):
return result
def _add_dynamic_options(self, board):
# upload protocols
"""Add dynamic board options for upload protocols and debug tools."""
# Upload protocols
if not board.get("upload.protocols", []):
board.manifest["upload"]["protocols"] = ["esptool", "espota"]
if not board.get("upload.protocol", ""):
board.manifest["upload"]["protocol"] = "esptool"
# debug tools
# Debug tools
debug = board.manifest.get("debug", {})
non_debug_protocols = ["esptool", "espota"]
supported_debug_tools = [
@@ -147,17 +529,21 @@ class Espressif32Platform(PlatformBase):
"olimex-arm-usb-ocd-h",
"olimex-arm-usb-ocd",
"olimex-jtag-tiny",
"tumpa",
"tumpa"
]
# A special case for the Kaluga board that has a separate interface config
# Special configuration for Kaluga board
if board.id == "esp32-s2-kaluga-1":
supported_debug_tools.append("ftdi")
if board.get("build.mcu", "") in ("esp32c3", "esp32c6", "esp32s3", "esp32h2"):
# ESP-builtin for certain MCUs
mcu = board.get("build.mcu", "")
if mcu in ESP_BUILTIN_DEBUG_MCUS:
supported_debug_tools.append("esp-builtin")
upload_protocol = board.manifest.get("upload", {}).get("protocol")
upload_protocols = board.manifest.get("upload", {}).get("protocols", [])
if debug:
upload_protocols.extend(supported_debug_tools)
if upload_protocol and upload_protocol not in upload_protocols:
@@ -167,37 +553,13 @@ class Espressif32Platform(PlatformBase):
if "tools" not in debug:
debug["tools"] = {}
# Debug tool configuration
for link in upload_protocols:
if link in non_debug_protocols or link in debug["tools"]:
continue
if link in ("jlink", "cmsis-dap"):
openocd_interface = link
elif link in ("esp-prog", "ftdi"):
if board.id == "esp32-s2-kaluga-1":
openocd_interface = "ftdi/esp32s2_kaluga_v1"
else:
openocd_interface = "ftdi/esp32_devkitj_v1"
elif link == "esp-bridge":
openocd_interface = "esp_usb_bridge"
elif link == "esp-builtin":
openocd_interface = "esp_usb_jtag"
else:
openocd_interface = "ftdi/" + link
server_args = [
"-s",
"$PACKAGE_DIR/share/openocd/scripts",
"-f",
"interface/%s.cfg" % openocd_interface,
"-f",
"%s/%s"
% (
("target", debug.get("openocd_target"))
if "openocd_target" in debug
else ("board", debug.get("openocd_board"))
),
]
openocd_interface = self._get_openocd_interface(link, board)
server_args = self._get_debug_server_args(openocd_interface, debug)
debug["tools"][link] = {
"server": {
@@ -229,14 +591,43 @@ class Espressif32Platform(PlatformBase):
board.manifest["debug"] = debug
return board
def _get_openocd_interface(self, link: str, board) -> str:
"""Determine OpenOCD interface configuration for debug link."""
if link in ("jlink", "cmsis-dap"):
return link
if link in ("esp-prog", "ftdi"):
if board.id == "esp32-s2-kaluga-1":
return "ftdi/esp32s2_kaluga_v1"
return "ftdi/esp32_devkitj_v1"
if link == "esp-bridge":
return "esp_usb_bridge"
if link == "esp-builtin":
return "esp_usb_jtag"
return f"ftdi/{link}"
def _get_debug_server_args(self, openocd_interface: str, debug: Dict) -> List[str]:
"""Generate debug server arguments for OpenOCD configuration."""
if 'openocd_target' in debug:
config_type = 'target'
config_name = debug.get('openocd_target')
else:
config_type = 'board'
config_name = debug.get('openocd_board')
return [
"-s", "$PACKAGE_DIR/share/openocd/scripts",
"-f", f"interface/{openocd_interface}.cfg",
"-f", f"{config_type}/{config_name}.cfg"
]
def configure_debug_session(self, debug_config):
"""Configure debug session with flash image loading."""
build_extra_data = debug_config.build_data.get("extra", {})
flash_images = build_extra_data.get("flash_images", [])
if "openocd" in (debug_config.server or {}).get("executable", ""):
debug_config.server["arguments"].extend(
["-c", "adapter speed %s" % (debug_config.speed or "5000")]
)
debug_config.server["arguments"].extend([
"-c", f"adapter speed {debug_config.speed or DEFAULT_DEBUG_SPEED}"
])
ignore_conds = [
debug_config.load_cmds != ["load"],
@@ -248,16 +639,13 @@ class Espressif32Platform(PlatformBase):
return
load_cmds = [
'monitor program_esp "{{{path}}}" {offset} verify'.format(
path=to_unix_path(item["path"]), offset=item["offset"]
)
f'monitor program_esp "{to_unix_path(item["path"])}" '
f'{item["offset"]} verify'
for item in flash_images
]
load_cmds.append(
'monitor program_esp "{%s.bin}" %s verify'
% (
to_unix_path(debug_config.build_data["prog_path"][:-4]),
build_extra_data.get("application_offset", "0x10000"),
)
f'monitor program_esp '
f'"{to_unix_path(debug_config.build_data["prog_path"][:-4])}.bin" '
f'{build_extra_data.get("application_offset", DEFAULT_APP_OFFSET)} verify'
)
debug_config.load_cmds = load_cmds